1
0

refactor(assoc): improve error handling and type safety

- Remove unused `Infallible` import
- Add `ParseProgIdKindError` for better ProgId parsing error handling
- Implement `BlankProgId` variant for empty ProgId strings
- Add `ExtKey::new` and `ProgIdKey::new` constructors
- Add `as_inner` methods to `ExtKey` and `ProgIdKey`
- Replace `to_string()` calls with `as_inner().to_string()`
- Update error type from `BlankStringError` to `BlankPathError`
- Rename `BlankStringError` to `BlankPathError` with updated message
- Update `blank_path_guard` return type and error handling
- Remove `Display` and `FromStr` implementations from `ExtKey` and `ProgIdKey`
This commit is contained in:
2025-11-01 16:11:22 +08:00
parent 3694a6a82a
commit 2741969164
2 changed files with 59 additions and 51 deletions

View File

@ -4,7 +4,6 @@
use crate::extra::windows::{Ext, ProgId};
use crate::extra::winreg as winreg_extra;
use crate::utilities;
use std::convert::Infallible;
use std::fmt::Display;
use std::str::FromStr;
use thiserror::Error as TeError;
@ -25,7 +24,9 @@ pub enum Error {
#[error("{0}")]
ParseProgId(#[from] crate::extra::windows::ParseProgIdError),
#[error("{0}")]
BlankPath(#[from] crate::extra::winreg::BlankStringError),
ParseProgIdKind(#[from] ParseProgIdKindError),
#[error("{0}")]
BlankPath(#[from] crate::extra::winreg::BlankPathError),
}
/// The result type used in this crate.
@ -113,6 +114,15 @@ impl From<Scope> for View {
// region: ProgId Kind
/// The error occurs when parsing ProgId kind.
#[derive(Debug, TeError)]
pub enum ParseProgIdKindError {
#[error("{0}")]
FromStd(#[from] crate::extra::windows::ParseProgIdError),
#[error("given ProgId is blank")]
BlankProgId,
}
/// The variant of ProgId for the compatibility
/// with those software which do not follow Microsoft suggestions.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -133,13 +143,17 @@ impl Display for ProgIdKind {
}
impl FromStr for ProgIdKind {
type Err = Infallible;
type Err = ParseProgIdKindError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(match s.parse::<ProgId>() {
Ok(v) => Self::Std(v),
Err(_) => Self::Other(s.to_string()),
})
if s.is_empty() {
Err(ParseProgIdKindError::BlankProgId)
} else {
Ok(match s.parse::<ProgId>() {
Ok(v) => Self::Std(v),
Err(_) => Self::Other(s.to_string()),
})
}
}
}
@ -231,6 +245,20 @@ pub struct ExtKey {
ext: Ext,
}
impl ExtKey {
/// Create new file extension registry key representer.
pub fn new(s: &str) -> Result<Self> {
Ok(Self {
ext: Ext::from_str(s)?,
})
}
/// Fetch the reference to inner extension representer.
pub fn as_inner(&self) -> &Ext {
&self.ext
}
}
impl ExtKey {
/// Set the default "Open With" of this file extension to given ProgId.
pub fn link(&self, prog_id: &ProgIdKey, scope: Scope) -> Result<()> {
@ -243,7 +271,7 @@ impl ExtKey {
let (subkey, _) =
classes.create_subkey_with_flags(blank_path_guard(self.ext.to_string())?, KEY_WRITE)?;
// Set the default way to open this file extension
subkey.set_value("", &prog_id.to_string())?;
subkey.set_value("", &prog_id.as_inner().to_string())?;
// Okey
Ok(())
@ -268,7 +296,7 @@ impl ExtKey {
)? {
// Only delete the default key if it is equal to our ProgId
if let Some(value) = try_get_value::<String, _>(&subkey, "")? {
if value == prog_id.to_string() {
if value == prog_id.as_inner().to_string() {
// Delete the default key.
subkey.delete_value("")?;
}
@ -297,9 +325,7 @@ impl ExtKey {
Some(subkey) => {
// Try get associated ProgId if possible
match try_get_value::<String, _>(&subkey, "")? {
Some(value) => {
Some(ProgIdKey::from_str(value.as_str()).expect("unexpected Infallable"))
}
Some(value) => Some(ProgIdKey::new(value.as_str())?),
None => None,
}
}
@ -311,22 +337,6 @@ impl ExtKey {
}
}
impl Display for ExtKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.ext)
}
}
impl FromStr for ExtKey {
type Err = <Ext as FromStr>::Err;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self {
ext: Ext::from_str(s)?,
})
}
}
// endregion
// region: ProgId Registry Key
@ -336,6 +346,20 @@ pub struct ProgIdKey {
prog_id: ProgIdKind,
}
impl ProgIdKey {
/// Create new ProgId registry representer.
pub fn new(s: &str) -> Result<Self> {
Ok(Self {
prog_id: ProgIdKind::from_str(s)?,
})
}
/// Fetch the reference to inner ProgId.
pub fn as_inner(&self) -> &ProgIdKind {
&self.prog_id
}
}
impl ProgIdKey {
/// Create ProgId into Registry in given scope with given parameters
pub fn create(&self, scope: Scope, command: &str) -> Result<()> {
@ -364,20 +388,4 @@ impl ProgIdKey {
}
}
impl Display for ProgIdKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.prog_id)
}
}
impl FromStr for ProgIdKey {
type Err = <ProgIdKind as FromStr>::Err;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self {
prog_id: ProgIdKind::from_str(s)?,
})
}
}
// endregion

View File

@ -62,14 +62,14 @@ pub fn try_get_value<T: FromRegValue, N: AsRef<OsStr>>(
// endregion
// region: Blank String Guard
// region: Blank Path Guard
/// The error occurs when given string is empty.
/// The error occurs when given registry path is empty.
#[derive(Debug, TeError)]
#[error("unexpected empty string")]
pub struct BlankStringError {}
#[error("unexpected empty registry path")]
pub struct BlankPathError {}
impl BlankStringError {
impl BlankPathError {
fn new() -> Self {
Self {}
}
@ -82,9 +82,9 @@ impl BlankStringError {
/// Because it will cause unexpected behavior that returning key self, rather than subkey.
/// This is VERY dangerous especially for those registry delete functions.
/// So I create this function to prevent any harmful blank path was passed into registry function.
pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankStringError> {
pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankPathError> {
if path.as_ref().is_empty() {
Err(BlankStringError::new())
Err(BlankPathError::new())
} else {
Ok(path)
}