feat(windows): implement file extension and ProgId parsing structs
- Add Ext struct for handling file extensions with validation - Implement ProgId struct following Microsoft's suggested format - Create Clsid struct for handling CLSID with UUID parsing - Add ExpandString struct for environment variable expansion - Include comprehensive tests for new structs and parsing logic - Remove duplicate ExpandString implementation from windows.rs - Organize code regions for better readability and maintenance
This commit is contained in:
@ -236,11 +236,3 @@ impl Display for Clsid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region: Icon Resource
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region: String Resource
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|||||||
@ -9,9 +9,383 @@ use std::path::Path;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use thiserror::Error as TeError;
|
use thiserror::Error as TeError;
|
||||||
|
use uuid::Uuid;
|
||||||
use widestring::{WideCString, WideChar, WideStr};
|
use widestring::{WideCString, WideChar, WideStr};
|
||||||
use windows_sys::Win32::UI::WindowsAndMessaging::HICON;
|
use windows_sys::Win32::UI::WindowsAndMessaging::HICON;
|
||||||
|
|
||||||
|
// region: File Extension
|
||||||
|
|
||||||
|
/// The error occurs when constructing Ext with bad body.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given file extension body \"{inner}\" is invalid")]
|
||||||
|
pub struct BadExtBodyError {
|
||||||
|
/// The clone of string which is not a valid file extension body.
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BadExtBodyError {
|
||||||
|
/// Create new error instance.
|
||||||
|
fn new(inner: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: inner.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The struct representing an file extension which must start with dot (`.`)
|
||||||
|
/// and followed by at least one arbitrary characters.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Ext {
|
||||||
|
/// The body of file extension (excluding dot).
|
||||||
|
body: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ext {
|
||||||
|
/// Create an new file extension.
|
||||||
|
///
|
||||||
|
/// `body` is the body of file extension (excluding dot).
|
||||||
|
/// If you want to create this struct with ordinary extension string like `.jpg`,
|
||||||
|
/// please use `from_str()` instead.
|
||||||
|
pub fn new(body: &str) -> Result<Self, BadExtBodyError> {
|
||||||
|
// Check whether given body has dot or empty
|
||||||
|
if body.is_empty() || body.contains('.') {
|
||||||
|
Err(BadExtBodyError::new(body))
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
body: body.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the body part of file extension (excluding dot)
|
||||||
|
pub fn inner(&self) -> &str {
|
||||||
|
&self.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error occurs when try parsing string into FileExt.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given file extension name \"{inner}\" is invalid")]
|
||||||
|
pub struct ParseExtError {
|
||||||
|
/// The clone of string which is not a valid file extension.
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseExtError {
|
||||||
|
/// Create new error instance.
|
||||||
|
fn new(inner: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: inner.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Ext {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, ".{}", self.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Ext {
|
||||||
|
type Err = ParseExtError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\.([^\.]+)$").unwrap());
|
||||||
|
match RE.captures(s) {
|
||||||
|
Some(v) => Ok(Self::new(&v[1]).expect("unexpected dot in Ext body")),
|
||||||
|
None => Err(ParseExtError::new(s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Programmatic Identifiers (ProgId)
|
||||||
|
|
||||||
|
/// The error occurs when constructing ProgId.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given ProgId part \"{inner}\" is invalid")]
|
||||||
|
pub struct BadProgIdPartError {
|
||||||
|
/// The clone of string which is not a valid ProgId part.
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BadProgIdPartError {
|
||||||
|
/// Create new error instance.
|
||||||
|
fn new(s: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: s.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The ProgId exactly follows Microsoft suggested
|
||||||
|
/// `[Vendor or Application].[Component].[Version]` format.
|
||||||
|
///
|
||||||
|
/// However, most of applications do no follow this standard,
|
||||||
|
/// this scenario is not convered by this struct in there.
|
||||||
|
/// It should be done in other place.
|
||||||
|
/// Additionally, `[Version]` part is optional.
|
||||||
|
///
|
||||||
|
/// Reference:
|
||||||
|
/// - https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids
|
||||||
|
/// - https://learn.microsoft.com/en-us/windows/win32/com/-progid--key
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct ProgId {
|
||||||
|
/// The vendor part of ProgId.
|
||||||
|
vendor: String,
|
||||||
|
/// The component part of ProgId.
|
||||||
|
component: String,
|
||||||
|
/// The optional version part of ProgId.
|
||||||
|
version: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgId {
|
||||||
|
/// Create a new ProgId with given parts.
|
||||||
|
pub fn new(
|
||||||
|
vendor: &str,
|
||||||
|
component: &str,
|
||||||
|
version: Option<u32>,
|
||||||
|
) -> Result<Self, BadProgIdPartError> {
|
||||||
|
// Check whether vendor or component part is empty or has dot
|
||||||
|
if vendor.is_empty() || vendor.contains('.') {
|
||||||
|
Err(BadProgIdPartError::new(vendor))
|
||||||
|
} else if component.is_empty() || component.contains('.') {
|
||||||
|
Err(BadProgIdPartError::new(component))
|
||||||
|
} else {
|
||||||
|
Ok(Self {
|
||||||
|
vendor: vendor.to_string(),
|
||||||
|
component: component.to_string(),
|
||||||
|
version,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the vendor part of standard ProgId.
|
||||||
|
pub fn get_vendor(&self) -> &str {
|
||||||
|
&self.vendor
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the component part of standard ProgId.
|
||||||
|
pub fn get_component(&self) -> &str {
|
||||||
|
&self.component
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the version part of standard ProgId.
|
||||||
|
pub fn get_version(&self) -> Option<u32> {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error occurs when parsing ProgId.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given ProgId \"{inner}\" is invalid")]
|
||||||
|
pub struct ParseProgIdError {
|
||||||
|
/// The clone of string which is not a valid ProgId.
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseProgIdError {
|
||||||
|
/// Create new error instance.
|
||||||
|
fn new(s: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: s.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ProgId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match &self.version {
|
||||||
|
Some(version) => write!(f, "{}.{}.{}", self.vendor, self.component, version),
|
||||||
|
None => write!(f, "{}.{}", self.vendor, self.component),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ProgId {
|
||||||
|
type Err = ParseProgIdError;
|
||||||
|
|
||||||
|
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 vendor = &caps[1];
|
||||||
|
let component = &caps[2];
|
||||||
|
let version = match caps.get(4) {
|
||||||
|
Some(sv) => Some(
|
||||||
|
sv.as_str()
|
||||||
|
.parse::<u32>()
|
||||||
|
.map_err(|_| ParseProgIdError::new(s))?,
|
||||||
|
),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
Ok(Self::new(vendor, component, version).expect("unexpected bad part of ProgId"))
|
||||||
|
} else {
|
||||||
|
Err(ParseProgIdError::new(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: CLSID
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Clsid {
|
||||||
|
inner: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clsid {
|
||||||
|
pub fn new(uuid: &str) -> Result<Self, ParseClsidError> {
|
||||||
|
Self::from_str(uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: May add CLSID generator in there.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The error occurs when parsing CLSID
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given string \"{inner}\" is invalid for uuid")]
|
||||||
|
pub struct ParseClsidError {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseClsidError {
|
||||||
|
fn new(s: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: s.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Clsid {
|
||||||
|
type Err = ParseClsidError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self {
|
||||||
|
inner: Uuid::parse_str(s).map_err(|_| ParseClsidError::new(s))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Clsid {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.inner.braced().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: Expand String
|
||||||
|
|
||||||
|
/// Error occurs when creating Expand String.
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("given string is not an expand string")]
|
||||||
|
pub struct ParseExpandStrError {}
|
||||||
|
|
||||||
|
impl ParseExpandStrError {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error occurs when expand Expand String
|
||||||
|
#[derive(Debug, TeError)]
|
||||||
|
#[error("error occurs when expanding expand string")]
|
||||||
|
pub enum ExpandEnvVarError {
|
||||||
|
/// Given string has embedded NUL.
|
||||||
|
EmbeddedNul(#[from] widestring::error::ContainsNul<WideChar>),
|
||||||
|
/// The encoding of string is invalid.
|
||||||
|
BadEncoding(#[from] widestring::error::Utf16Error),
|
||||||
|
/// Error occurs when int type casting.
|
||||||
|
BadIntCast(#[from] std::num::TryFromIntError),
|
||||||
|
/// Integeral arithmatic downflow.
|
||||||
|
Underflow,
|
||||||
|
/// Error occurs when executing Win32 expand function.
|
||||||
|
ExpandFunction,
|
||||||
|
/// Some environment vairable are not expanded.
|
||||||
|
NoEnvVar,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The struct representing an Expand String,
|
||||||
|
/// which contain environment variable in string,
|
||||||
|
/// like `%LOCALAPPDATA%\SomeApp.exe`.
|
||||||
|
pub struct ExpandString {
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpandString {
|
||||||
|
const VAR_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"%[a-zA-Z0-9_]+%").unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpandString {
|
||||||
|
/// Create a new expand string
|
||||||
|
pub fn new(s: &str) -> Result<Self, ParseExpandStrError> {
|
||||||
|
Self::from_str(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expand the variables located in this string
|
||||||
|
/// and produce the final usable string.
|
||||||
|
pub fn expand_string(&self) -> Result<String, ExpandEnvVarError> {
|
||||||
|
use windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW;
|
||||||
|
|
||||||
|
// Fetch the size of expand result
|
||||||
|
let source = WideCString::from_str(self.inner.as_str())?;
|
||||||
|
let size = unsafe { ExpandEnvironmentStringsW(source.as_ptr(), std::ptr::null_mut(), 0) };
|
||||||
|
if size == 0 {
|
||||||
|
return Err(ExpandEnvVarError::ExpandFunction);
|
||||||
|
}
|
||||||
|
let size_no_nul = size.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||||
|
|
||||||
|
// Allocate buffer for it.
|
||||||
|
let len: usize = size.try_into()?;
|
||||||
|
let len_no_nul = len.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||||
|
let mut buffer = vec![0; len];
|
||||||
|
// Receive result
|
||||||
|
let size =
|
||||||
|
unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size_no_nul) };
|
||||||
|
if size == 0 {
|
||||||
|
return Err(ExpandEnvVarError::ExpandFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast result as Rust string
|
||||||
|
let wstr = unsafe { WideStr::from_ptr(buffer.as_ptr(), len_no_nul) };
|
||||||
|
let rv = wstr.to_string()?;
|
||||||
|
|
||||||
|
// If the final string still has environment variable,
|
||||||
|
// we think we fail to expand it.
|
||||||
|
if Self::VAR_RE.is_match(rv.as_str()) {
|
||||||
|
Err(ExpandEnvVarError::NoEnvVar)
|
||||||
|
} else {
|
||||||
|
Ok(rv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ExpandString {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ExpandString {
|
||||||
|
type Err = ParseExpandStrError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if Self::VAR_RE.is_match(s) {
|
||||||
|
Ok(Self {
|
||||||
|
inner: s.to_string(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(ParseExpandStrError::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
// region: Windows Resource Reference String
|
// region: Windows Resource Reference String
|
||||||
|
|
||||||
// region: Icon Reference String
|
// region: Icon Reference String
|
||||||
@ -359,114 +733,6 @@ impl StrRc {
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region: Expand String
|
|
||||||
|
|
||||||
/// Error occurs when creating Expand String.
|
|
||||||
#[derive(Debug, TeError)]
|
|
||||||
#[error("given string is not an expand string")]
|
|
||||||
pub struct ParseExpandStrError {}
|
|
||||||
|
|
||||||
impl ParseExpandStrError {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error occurs when expand Expand String
|
|
||||||
#[derive(Debug, TeError)]
|
|
||||||
#[error("error occurs when expanding expand string")]
|
|
||||||
pub enum ExpandEnvVarError {
|
|
||||||
/// Given string has embedded NUL.
|
|
||||||
EmbeddedNul(#[from] widestring::error::ContainsNul<WideChar>),
|
|
||||||
/// The encoding of string is invalid.
|
|
||||||
BadEncoding(#[from] widestring::error::Utf16Error),
|
|
||||||
/// Error occurs when int type casting.
|
|
||||||
BadIntCast(#[from] std::num::TryFromIntError),
|
|
||||||
/// Integeral arithmatic downflow.
|
|
||||||
Underflow,
|
|
||||||
/// Error occurs when executing Win32 expand function.
|
|
||||||
ExpandFunction,
|
|
||||||
/// Some environment vairable are not expanded.
|
|
||||||
NoEnvVar,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The struct representing an Expand String,
|
|
||||||
/// which contain environment variable in string,
|
|
||||||
/// like `%LOCALAPPDATA%\SomeApp.exe`.
|
|
||||||
pub struct ExpandString {
|
|
||||||
inner: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExpandString {
|
|
||||||
const VAR_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"%[a-zA-Z0-9_]+%").unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExpandString {
|
|
||||||
/// Create a new expand string
|
|
||||||
pub fn new(s: &str) -> Result<Self, ParseExpandStrError> {
|
|
||||||
Self::from_str(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expand the variables located in this string
|
|
||||||
/// and produce the final usable string.
|
|
||||||
pub fn expand_string(&self) -> Result<String, ExpandEnvVarError> {
|
|
||||||
use windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW;
|
|
||||||
|
|
||||||
// Fetch the size of expand result
|
|
||||||
let source = WideCString::from_str(self.inner.as_str())?;
|
|
||||||
let size = unsafe { ExpandEnvironmentStringsW(source.as_ptr(), std::ptr::null_mut(), 0) };
|
|
||||||
if size == 0 {
|
|
||||||
return Err(ExpandEnvVarError::ExpandFunction);
|
|
||||||
}
|
|
||||||
let size_no_nul = size.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
|
||||||
|
|
||||||
// Allocate buffer for it.
|
|
||||||
let len: usize = size.try_into()?;
|
|
||||||
let len_no_nul = len.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
|
||||||
let mut buffer = vec![0; len];
|
|
||||||
// Receive result
|
|
||||||
let size =
|
|
||||||
unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size_no_nul) };
|
|
||||||
if size == 0 {
|
|
||||||
return Err(ExpandEnvVarError::ExpandFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast result as Rust string
|
|
||||||
let wstr = unsafe { WideStr::from_ptr(buffer.as_ptr(), len_no_nul) };
|
|
||||||
let rv = wstr.to_string()?;
|
|
||||||
|
|
||||||
// If the final string still has environment variable,
|
|
||||||
// we think we fail to expand it.
|
|
||||||
if Self::VAR_RE.is_match(rv.as_str()) {
|
|
||||||
Err(ExpandEnvVarError::NoEnvVar)
|
|
||||||
} else {
|
|
||||||
Ok(rv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ExpandString {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for ExpandString {
|
|
||||||
type Err = ParseExpandStrError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
if Self::VAR_RE.is_match(s) {
|
|
||||||
Ok(Self {
|
|
||||||
inner: s.to_string(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(ParseExpandStrError::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region: Windows Commandline
|
// region: Windows Commandline
|
||||||
|
|
||||||
// region: Cmd Lexer
|
// region: Cmd Lexer
|
||||||
|
|||||||
@ -1,6 +1,90 @@
|
|||||||
use std::path::Path;
|
use std::{path::Path, str::FromStr};
|
||||||
use wfassoc::extra::windows::*;
|
use wfassoc::extra::windows::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ex_new() {
|
||||||
|
fn ok_tester(s: &str, probe: &str) {
|
||||||
|
let rv = Ext::new(s);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = rv.unwrap();
|
||||||
|
assert_eq!(rv.to_string(), probe);
|
||||||
|
}
|
||||||
|
fn err_tester(s: &str) {
|
||||||
|
let rv = Ext::new(s);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_tester("jpg", ".jpg");
|
||||||
|
err_tester(".jpg");
|
||||||
|
err_tester("");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ext_parse() {
|
||||||
|
fn ok_tester(s: &str, probe: &str) {
|
||||||
|
let rv = Ext::from_str(s);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = rv.unwrap();
|
||||||
|
assert_eq!(rv.inner(), probe);
|
||||||
|
}
|
||||||
|
fn err_tester(s: &str) {
|
||||||
|
let rv = Ext::from_str(s);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_tester(".jpg", "jpg");
|
||||||
|
err_tester(".jar.disabled");
|
||||||
|
err_tester("jar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prog_id_new() {
|
||||||
|
fn ok_tester(vendor: &str, component: &str, version: Option<u32>, probe: &str) {
|
||||||
|
let rv = ProgId::new(vendor, component, version);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv = rv.unwrap();
|
||||||
|
assert_eq!(rv.to_string(), probe);
|
||||||
|
}
|
||||||
|
fn err_tester(vendor: &str, component: &str, version: Option<u32>) {
|
||||||
|
let rv = ProgId::new(vendor, component, version);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_tester("PowerPoint", "Template", Some(12), "PowerPoint.Template.12");
|
||||||
|
err_tester("", "MyApp", None);
|
||||||
|
err_tester("Me", "", None);
|
||||||
|
err_tester("M.e", "MyApp", None);
|
||||||
|
err_tester("Me", "My.App", None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prog_id_parse() {
|
||||||
|
fn ok_tester(s: &str, probe_vendor: &str, probe_component: &str, probe_version: Option<u32>) {
|
||||||
|
let rv = ProgId::from_str(s);
|
||||||
|
assert!(rv.is_ok());
|
||||||
|
let rv =rv.unwrap();
|
||||||
|
assert_eq!(rv.get_vendor(), probe_vendor);
|
||||||
|
assert_eq!(rv.get_component(), probe_component);
|
||||||
|
assert_eq!(rv.get_version(), probe_version);
|
||||||
|
}
|
||||||
|
fn err_tester(s: &str) {
|
||||||
|
let rv = ProgId::from_str(s);
|
||||||
|
assert!(rv.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
ok_tester("VSCode.c++", "VSCode", "c++", None);
|
||||||
|
ok_tester("PowerPoint.Template.12", "PowerPoint", "Template", Some(12));
|
||||||
|
err_tester("Me.MyApp.");
|
||||||
|
err_tester("WMP11.AssocFile.3G2");
|
||||||
|
err_tester("What the f*ck?");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_clsid() {
|
||||||
|
fn ok_tester(s: &str) {}
|
||||||
|
fn err_tester(s: &str) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_icon_ref_str() {
|
fn test_icon_ref_str() {
|
||||||
fn ok_tester(s: &str, probe: (&str, u32)) {
|
fn ok_tester(s: &str, probe: (&str, u32)) {
|
||||||
@ -15,7 +99,10 @@ fn test_icon_ref_str() {
|
|||||||
assert!(rv.is_err());
|
assert!(rv.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
ok_tester(r#"%SystemRoot%\System32\imageres.dll,-72"#, (r#"%SystemRoot%\System32\imageres.dll"#, 72));
|
ok_tester(
|
||||||
|
r#"%SystemRoot%\System32\imageres.dll,-72"#,
|
||||||
|
(r#"%SystemRoot%\System32\imageres.dll"#, 72),
|
||||||
|
);
|
||||||
err_tester(r#"C:\Windows\Cursors\aero_arrow.cur"#);
|
err_tester(r#"C:\Windows\Cursors\aero_arrow.cur"#);
|
||||||
err_tester(r#"@%SystemRoot%\System32\shell32.dll,-30596"#);
|
err_tester(r#"@%SystemRoot%\System32\shell32.dll,-30596"#);
|
||||||
}
|
}
|
||||||
@ -34,7 +121,10 @@ fn test_str_ref_str() {
|
|||||||
assert!(rv.is_err());
|
assert!(rv.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
ok_tester(r#"@%SystemRoot%\System32\shell32.dll,-30596"#, (r#"%SystemRoot%\System32\shell32.dll"#, 30596));
|
ok_tester(
|
||||||
|
r#"@%SystemRoot%\System32\shell32.dll,-30596"#,
|
||||||
|
(r#"%SystemRoot%\System32\shell32.dll"#, 30596),
|
||||||
|
);
|
||||||
err_tester(r#"This is my application, OK?"#);
|
err_tester(r#"This is my application, OK?"#);
|
||||||
err_tester(r#"%SystemRoot%\System32\imageres.dll,-72"#);
|
err_tester(r#"%SystemRoot%\System32\imageres.dll,-72"#);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user