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:
@ -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,13 +143,17 @@ 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> {
|
||||||
Ok(match s.parse::<ProgId>() {
|
if s.is_empty() {
|
||||||
Ok(v) => Self::Std(v),
|
Err(ParseProgIdKindError::BlankProgId)
|
||||||
Err(_) => Self::Other(s.to_string()),
|
} 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,
|
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
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user