add decl for ProgId
This commit is contained in:
@ -24,6 +24,8 @@ pub enum Error {
|
||||
BadFileExt(#[from] ParseFileExtError),
|
||||
#[error("{0}")]
|
||||
BadExecRc(#[from] ParseExecRcError),
|
||||
#[error("{0}")]
|
||||
BadProgId(#[from] ParseProgIdError),
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -192,7 +194,11 @@ impl FileExtAssoc {
|
||||
let default = thisext.get_value("").unwrap_or(String::new());
|
||||
let open_with_progids =
|
||||
if let Ok(progids) = thisext.open_subkey_with_flags("OpenWithProdIds", KEY_READ) {
|
||||
progids.enum_keys().map(|x| x.unwrap()).filter(|k| !k.is_empty()).collect()
|
||||
progids
|
||||
.enum_keys()
|
||||
.map(|x| x.unwrap())
|
||||
.filter(|k| !k.is_empty())
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
@ -220,71 +226,209 @@ impl FileExtAssoc {
|
||||
|
||||
// region: Executable Resource
|
||||
|
||||
/// The struct representing an Windows executable resources path like
|
||||
/// `path_to_file.exe,1`.
|
||||
pub struct ExecRc {
|
||||
/// The path to binary for finding resources.
|
||||
binary: PathBuf,
|
||||
/// The inner index of resources.
|
||||
index: u32,
|
||||
// /// The struct representing an Windows executable resources path like
|
||||
// /// `path_to_file.exe,1`.
|
||||
// pub struct ExecRc {
|
||||
// /// The path to binary for finding resources.
|
||||
// binary: PathBuf,
|
||||
// /// The inner index of resources.
|
||||
// index: u32,
|
||||
// }
|
||||
|
||||
// impl ExecRc {
|
||||
// pub fn new(res_str: &str) -> Result<Self, ParseExecRcError> {
|
||||
// static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([^,]+),([0-9]+)$").unwrap());
|
||||
// let caps = RE.captures(res_str);
|
||||
// if let Some(caps) = caps {
|
||||
// let binary = PathBuf::from_str(&caps[1])?;
|
||||
// let index = caps[2].parse::<u32>()?;
|
||||
// Ok(Self { binary, index })
|
||||
// } else {
|
||||
// Err(ParseExecRcError::NoCapture)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// The error occurs when try parsing string into ExecRc.
|
||||
// #[derive(Debug, TeError)]
|
||||
// #[error("given string is not a valid executable resource string")]
|
||||
// pub enum ParseExecRcError {
|
||||
// /// Given string is not matched with format.
|
||||
// NoCapture,
|
||||
// /// Fail to convert executable part into path.
|
||||
// BadBinaryPath(#[from] std::convert::Infallible),
|
||||
// /// Fail to convert index part into valid number.
|
||||
// BadIndex(#[from] std::num::ParseIntError),
|
||||
// }
|
||||
|
||||
// impl FromStr for ExecRc {
|
||||
// type Err = ParseExecRcError;
|
||||
|
||||
// fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// ExecRc::new(s)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Display for ExecRc {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// write!(f, "{},{}", self.binary.to_str().unwrap(), self.index)
|
||||
// }
|
||||
// }
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Programmatic Identifiers (ProgId)
|
||||
|
||||
/// The struct representing Programmatic Identifiers (ProgId).
|
||||
///
|
||||
/// Because there is optional part in ProgId, and not all software developer
|
||||
/// are willing to following Microsoft standard, there is no strict constaint for ProgId.
|
||||
/// So this struct is actually an enum which holding any possible ProgId format.
|
||||
pub enum ProgId {
|
||||
Plain(String),
|
||||
Loose(LosseProgId),
|
||||
Strict(StrictProgId),
|
||||
}
|
||||
|
||||
impl ExecRc {
|
||||
pub fn new(res_str: &str) -> Result<Self, ParseExecRcError> {
|
||||
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^([^,]+),([0-9]+)$").unwrap());
|
||||
let caps = RE.captures(res_str);
|
||||
if let Some(caps) = caps {
|
||||
let binary = PathBuf::from_str(&caps[1])?;
|
||||
let index = caps[2].parse::<u32>()?;
|
||||
Ok(Self { binary, index })
|
||||
} else {
|
||||
Err(ParseExecRcError::NoCapture)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The error occurs when try parsing string into ExecRc.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given string is not a valid executable resource string")]
|
||||
pub enum ParseExecRcError {
|
||||
/// Given string is not matched with format.
|
||||
NoCapture,
|
||||
/// Fail to convert executable part into path.
|
||||
BadBinaryPath(#[from] std::convert::Infallible),
|
||||
/// Fail to convert index part into valid number.
|
||||
BadIndex(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
impl FromStr for ExecRc {
|
||||
type Err = ParseExecRcError;
|
||||
impl FromStr for ProgId {
|
||||
type Err = ParseProgIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
ExecRc::new(s)
|
||||
// match it for strict ProgId first
|
||||
if let Ok(v) = StrictProgId::from_str(s) {
|
||||
return Ok(Self::Strict(v));
|
||||
}
|
||||
// then match for loose ProgId
|
||||
if let Ok(v) = LosseProgId::from_str(s) {
|
||||
return Ok(Self::Loose(v));
|
||||
}
|
||||
// fallback with plain
|
||||
Ok(Self::Plain(s.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExecRc {
|
||||
impl Display for ProgId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{},{}", self.binary.to_str().unwrap(), self.index)
|
||||
match self {
|
||||
ProgId::Plain(v) => v.fmt(f),
|
||||
ProgId::Loose(v) => v.fmt(f),
|
||||
ProgId::Strict(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The error occurs when parsing ProgId.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given ProgId string is invalid")]
|
||||
pub struct ParseProgIdError {}
|
||||
|
||||
impl ParseProgIdError {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// The ProgId similar with strict ProgId, but no version part.
|
||||
pub struct LosseProgId {
|
||||
vendor: String,
|
||||
component: String,
|
||||
}
|
||||
|
||||
impl LosseProgId {
|
||||
pub fn new(vendor: &str, component: &str) -> Self {
|
||||
Self {
|
||||
vendor: vendor.to_string(),
|
||||
component: component.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vendor(&self) -> &str {
|
||||
&self.vendor
|
||||
}
|
||||
|
||||
pub fn get_component(&self) -> &str {
|
||||
&self.component
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for LosseProgId {
|
||||
type Err = ParseProgIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static RE: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)$").unwrap());
|
||||
let caps = RE.captures(s);
|
||||
if let Some(caps) = caps {
|
||||
let vendor = &caps[1];
|
||||
let component = &caps[2];
|
||||
Ok(Self::new(vendor, component))
|
||||
} else {
|
||||
Err(ParseProgIdError::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LosseProgId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}.{}", self.vendor, self.component)
|
||||
}
|
||||
}
|
||||
|
||||
/// The ProgId exactly follows `[Vendor or Application].[Component].[Version]` format.
|
||||
pub struct StrictProgId {
|
||||
vendor: String,
|
||||
component: String,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
impl StrictProgId {
|
||||
pub fn new(vendor: &str, component: &str, version: u32) -> Self {
|
||||
Self {
|
||||
vendor: vendor.to_string(),
|
||||
component: component.to_string(),
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vendor(&self) -> &str {
|
||||
&self.vendor
|
||||
}
|
||||
|
||||
pub fn get_component(&self) -> &str {
|
||||
&self.component
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> u32 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for StrictProgId {
|
||||
type Err = ParseProgIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static RE: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^([a-zA-Z0-9]+)\.([a-zA-Z0-9]+)\.([0-9]+)$").unwrap());
|
||||
let caps = RE.captures(s);
|
||||
if let Some(caps) = caps {
|
||||
let vendor = &caps[1];
|
||||
let component = &caps[2];
|
||||
let version = caps[3].parse::<u32>().map_err(|_| ParseProgIdError::new())?;
|
||||
Ok(Self::new(vendor, component, version))
|
||||
} else {
|
||||
Err(ParseProgIdError::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StrictProgId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}.{}.{}", self.vendor, self.component, self.version)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// /// The struct representing an Windows acceptable Prgram ID,
|
||||
// /// which looks like `Program.Document.2`
|
||||
// pub struct ProgId {
|
||||
// inner: String,
|
||||
// }
|
||||
|
||||
// impl ProgId {
|
||||
// pub fn new(prog_id: &str) -> Self {
|
||||
// Self {
|
||||
// inner: prog_id.to_string(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// region: Program
|
||||
|
||||
// /// The struct representing a complete Win32 program.
|
||||
|
Reference in New Issue
Block a user