From 23d0a79c0bca1a6aa032007a2f5f901fad961762 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Fri, 24 Oct 2025 10:34:07 +0800 Subject: [PATCH] feat(windows): add resource reference string parsers Add IconRc and StrRc structs for parsing Windows resource reference strings. Implement FromStr and Display traits for both types along with error handling. Rename BadExpandStrError to ParseExpandStrError for consistency. --- wfassoc/src/extra/windows.rs | 155 +++++++++++++++++++++++++++++++-- wfassoc/tests/extra_windows.rs | 27 ++++++ 2 files changed, 177 insertions(+), 5 deletions(-) diff --git a/wfassoc/src/extra/windows.rs b/wfassoc/src/extra/windows.rs index c02d0e4..d8af7bd 100644 --- a/wfassoc/src/extra/windows.rs +++ b/wfassoc/src/extra/windows.rs @@ -12,14 +12,159 @@ use thiserror::Error as TeError; use widestring::{WideCStr, WideCString, WideChar}; use windows_sys::Win32::UI::WindowsAndMessaging::HICON; +// region: Windows Resource Reference String + +// region: Icon Reference String + +#[derive(Debug, TeError)] +#[error("given string \"{inner}\" is not a valid Icon Reference String")] +pub struct ParseIconRcError { + inner: String, +} + +impl ParseIconRcError { + fn new(s: &str) -> Self { + Self { + inner: s.to_string(), + } + } +} + +#[derive(Debug, TeError)] +#[error("fail to load icon")] +pub struct FetchIconError {} + +pub struct IconRc { + path: String, + index: u32, +} + +impl IconRc { + /// Create a new Icon Reference String. + /// + /// `path` is the path to the icon resource file and it can be Expand String. + /// `index` is the index of the icon in the resource file. + pub fn new(path: &str, index: u32) -> Self { + Self { + path: path.to_string(), + index, + } + } + + pub fn fetch_icon(&self) -> Result { + todo!() + } +} + +impl Display for IconRc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{},-{}", self.path, self.index) + } +} + +impl FromStr for IconRc { + type Err = ParseIconRcError; + + fn from_str(s: &str) -> Result { + static RE: LazyLock = + LazyLock::new(|| Regex::new(r"^([^,@][^,]*),-([0-9]+)$").unwrap()); + let caps = RE.captures(s); + if let Some(caps) = caps { + let path = &caps[1]; + let index = caps.get(2).ok_or(ParseIconRcError::new(s)).and_then(|sv| { + sv.as_str() + .parse::() + .map_err(|_| ParseIconRcError::new(s)) + })?; + Ok(Self::new(path, index)) + } else { + Err(ParseIconRcError::new(s)) + } + } +} + +// endregion + +// region: String Reference String + +#[derive(Debug, TeError)] +#[error("given string \"{inner}\" is not a valid String Reference String")] +pub struct ParseStrRcError { + inner: String, +} + +impl ParseStrRcError { + fn new(s: &str) -> Self { + Self { + inner: s.to_string(), + } + } +} + +#[derive(Debug, TeError)] +#[error("fail to load string")] +pub struct FetchStrError{} + +pub struct StrRc { + path: String, + index: u32, +} + +impl StrRc { + /// Create a new String Reference String. + /// + /// `path` is the path to the string resource file and it can be Expand String. + /// `index` is the index of the string in the resource file. + pub fn new(path: &str, index: u32) -> Self { + Self { + path: path.to_string(), + index, + } + } + + pub fn fetch_string(&self) -> Result { + todo!() + } +} + +impl Display for StrRc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "@{},-{}", self.path, self.index) + } +} + +impl FromStr for StrRc { + type Err = ParseStrRcError; + + fn from_str(s: &str) -> Result { + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^@([^,]+),-([0-9]+)$").unwrap()); + let caps = RE.captures(s); + if let Some(caps) = caps { + let path = &caps[1]; + let index = caps.get(2).ok_or(ParseStrRcError::new(s)).and_then(|sv| { + sv.as_str() + .parse::() + .map_err(|_| ParseStrRcError::new(s)) + })?; + Ok(Self::new(path, index)) + } else { + Err(ParseStrRcError::new(s)) + } + } +} + +// endregion + +// endregion + // region: Expand String /// Error occurs when creating Expand String. #[derive(Debug, TeError)] #[error("given string is not an expand string")] -pub struct BadExpandStrError {} +pub struct ParseExpandStrError {} -impl BadExpandStrError { +impl ParseExpandStrError { fn new() -> Self { Self {} } @@ -58,7 +203,7 @@ impl ExpandString { impl ExpandString { /// Create a new expand string - pub fn new(s: &str) -> Result { + pub fn new(s: &str) -> Result { Self::from_str(s) } @@ -107,7 +252,7 @@ impl Display for ExpandString { } impl FromStr for ExpandString { - type Err = BadExpandStrError; + type Err = ParseExpandStrError; fn from_str(s: &str) -> Result { if Self::VAR_RE.is_match(s) { @@ -115,7 +260,7 @@ impl FromStr for ExpandString { inner: s.to_string(), }) } else { - Err(BadExpandStrError::new()) + Err(ParseExpandStrError::new()) } } } diff --git a/wfassoc/tests/extra_windows.rs b/wfassoc/tests/extra_windows.rs index 27d8af4..f3d7559 100644 --- a/wfassoc/tests/extra_windows.rs +++ b/wfassoc/tests/extra_windows.rs @@ -1,5 +1,32 @@ +use std::path::Path; use wfassoc::extra::windows::*; +#[test] +fn test_icon_rc() { + +} + +#[test] +fn test_str_rc() { + +} + +#[test] +fn test_expand_string() { + +} + +#[test] +fn test_icon() { + fn tester(file: &str, index: i32) { + let icon = Icon::new(Path::new(file), index, IconSizeKind::Small); + assert!(icon.is_ok()) + } + + // We pick it from "jpegfile" ProgId + tester("imageres.dll", 72); +} + #[test] fn test_cmd_args() { // Declare tester