1
0

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.
This commit is contained in:
2025-10-24 10:34:07 +08:00
parent e908e775ed
commit 23d0a79c0b
2 changed files with 177 additions and 5 deletions

View File

@ -12,14 +12,159 @@ use thiserror::Error as TeError;
use widestring::{WideCStr, WideCString, WideChar}; use widestring::{WideCStr, WideCString, WideChar};
use windows_sys::Win32::UI::WindowsAndMessaging::HICON; 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<Icon, FetchIconError> {
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<Self, Self::Err> {
static RE: LazyLock<Regex> =
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::<u32>()
.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<String, FetchStrError> {
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<Self, Self::Err> {
static RE: LazyLock<Regex> = 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::<u32>()
.map_err(|_| ParseStrRcError::new(s))
})?;
Ok(Self::new(path, index))
} else {
Err(ParseStrRcError::new(s))
}
}
}
// endregion
// endregion
// region: Expand String // region: Expand String
/// Error occurs when creating Expand String. /// Error occurs when creating Expand String.
#[derive(Debug, TeError)] #[derive(Debug, TeError)]
#[error("given string is not an expand string")] #[error("given string is not an expand string")]
pub struct BadExpandStrError {} pub struct ParseExpandStrError {}
impl BadExpandStrError { impl ParseExpandStrError {
fn new() -> Self { fn new() -> Self {
Self {} Self {}
} }
@ -58,7 +203,7 @@ impl ExpandString {
impl ExpandString { impl ExpandString {
/// Create a new expand string /// Create a new expand string
pub fn new(s: &str) -> Result<Self, BadExpandStrError> { pub fn new(s: &str) -> Result<Self, ParseExpandStrError> {
Self::from_str(s) Self::from_str(s)
} }
@ -107,7 +252,7 @@ impl Display for ExpandString {
} }
impl FromStr for ExpandString { impl FromStr for ExpandString {
type Err = BadExpandStrError; type Err = ParseExpandStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if Self::VAR_RE.is_match(s) { if Self::VAR_RE.is_match(s) {
@ -115,7 +260,7 @@ impl FromStr for ExpandString {
inner: s.to_string(), inner: s.to_string(),
}) })
} else { } else {
Err(BadExpandStrError::new()) Err(ParseExpandStrError::new())
} }
} }
} }

View File

@ -1,5 +1,32 @@
use std::path::Path;
use wfassoc::extra::windows::*; 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] #[test]
fn test_cmd_args() { fn test_cmd_args() {
// Declare tester // Declare tester