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

View File

@ -62,14 +62,14 @@ pub fn try_get_value<T: FromRegValue, N: AsRef<OsStr>>(
// endregion // 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)] #[derive(Debug, TeError)]
#[error("unexpected empty string")] #[error("unexpected empty registry path")]
pub struct BlankStringError {} pub struct BlankPathError {}
impl BlankStringError { impl BlankPathError {
fn new() -> Self { fn new() -> Self {
Self {} Self {}
} }
@ -82,9 +82,9 @@ impl BlankStringError {
/// Because it will cause unexpected behavior that returning key self, rather than subkey. /// Because it will cause unexpected behavior that returning key self, rather than subkey.
/// This is VERY dangerous especially for those registry delete functions. /// 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. /// 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() { if path.as_ref().is_empty() {
Err(BlankStringError::new()) Err(BlankPathError::new())
} else { } else {
Ok(path) Ok(path)
} }