diff --git a/wfassoc/src/lib.rs b/wfassoc/src/lib.rs index 4fc6a0a..333d476 100644 --- a/wfassoc/src/lib.rs +++ b/wfassoc/src/lib.rs @@ -6,11 +6,14 @@ compile_error!("Crate wfassoc is only supported on Windows."); pub mod assoc; pub mod utilities; +pub mod wincmd; +pub mod winreg_extra; use indexmap::{IndexMap, IndexSet}; +use regex::Regex; use std::ffi::OsStr; -use std::fmt::Display; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use thiserror::Error as TeError; use winreg::RegKey; use winreg::enums::{ @@ -31,6 +34,8 @@ pub enum Error { #[error("no administrative privilege")] NoPrivilege, + #[error("given identifier \"{0}\" of application is invalid")] + BadIdentifier(String), #[error("given full path to application is invalid")] BadFullAppPath, #[error("manner \"{0}\" is already registered")] @@ -118,33 +123,6 @@ impl From for View { // endregion -// region: Manner - -/// The struct representing a program manner. -/// Manner usually mean the way to open files, -/// or more preciously, the consititution of command arguments passed to program. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Manner { - // TODO: use specialized WinArg instead. - argv: String, -} - -impl Manner { - fn new(argv: &str) -> Self { - Self { - argv: argv.to_string(), - } - } -} - -impl Display for Manner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.argv) - } -} - -// endregion - // region: Program /// The struct representing a complete program for registration and unregistration. @@ -155,7 +133,7 @@ pub struct Program { /// The fully qualified path to the application. full_path: PathBuf, /// The collection holding all manners of this program. - manners: IndexSet, + manners: IndexSet, /// The collection holding all file extensions supported by this program. /// The key is file estension and value is its associated manner for opening it. exts: IndexMap, @@ -168,16 +146,21 @@ impl Program { /// If should only contain digits and alphabet chars, /// and should not start with any digits. /// For example, "MyApp" is okey but following names are not okey: - /// + /// /// - `My App` /// - `3DViewer` /// - `我的Qt程序` (means "My Qt App" in English) - /// + /// /// More preciously, `identifier` will be used as the vendor part of ProgId. - /// + /// /// `full_path` is the fully qualified path to the application. pub fn new(identifier: &str, full_path: &Path) -> Result { - // TODO: Add checker for identifier + // Check identifier + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9]*$").unwrap()); + if !RE.is_match(identifier) { + return Err(Error::BadIdentifier(identifier.to_string())); + } + // Everything is okey, build self. Ok(Self { identifier: identifier.to_string(), full_path: full_path.to_path_buf(), @@ -188,8 +171,9 @@ impl Program { /// Add manner provided by this program. pub fn add_manner(&mut self, manner: &str) -> Result { + // TODO: Use wincmd::CmdArgs instead of String. // Create manner from string - let manner = Manner::new(manner); + let manner = manner.to_string(); // Backup a stringfied manner for error output. let manner_str = manner.to_string(); // Insert manner. @@ -197,19 +181,19 @@ impl Program { if self.manners.insert(manner) { Ok(idx) } else { - Err(Error::DupExt(manner_str)) + Err(Error::DupManner(manner_str)) } } - /// Get the reference to manner with given token. - pub fn get_manner(&self, token: Token) -> Option<&Manner> { - self.manners.get_index(token) + /// Get the string display of manner represented by given token + pub fn get_manner_str(&self, token: Token) -> Option { + self.manners.get_index(token).map(|s| s.clone()) } /// Add file extension supported by this program and its associated manner. pub fn add_ext(&mut self, ext: &str, token: Token) -> Result { // Check manner token - if let None = self.get_manner(token) { + if let None = self.manners.get_index(token) { return Err(Error::InvalidAssocManner); } @@ -226,9 +210,9 @@ impl Program { } } - /// Get the reference to file extension with given token. - pub fn get_ext(&self, token: Token) -> Option<&assoc::Ext> { - self.exts.get_index(token).map(|p| p.0) + /// Get the string display of file extension represented by given token + pub fn get_ext_str(&self, token: Token) -> Option { + self.exts.get_index(token).map(|p| p.0.to_string()) } } @@ -369,7 +353,7 @@ impl Program { } /// Remove this program from the default "open with" of given extension. - /// + /// /// If the default "open with" of given extension is not our program, /// this function do nothing. pub fn unlink_ext(&self, ext: Token) -> Result<()> { diff --git a/wfassoc/src/wincmd.rs b/wfassoc/src/wincmd.rs new file mode 100644 index 0000000..e264544 --- /dev/null +++ b/wfassoc/src/wincmd.rs @@ -0,0 +1,18 @@ +//! This module involve Windows command line stuff, like argument splittor and path, +//! because they are different with POSIX standard. + +// region: Cmd Path + +pub struct CmdPath { + +} + +// endregion + +// region: Cmd Arguments + +pub struct CmdArgs { + +} + +// endregion diff --git a/wfassoc/src/winreg_extra.rs b/wfassoc/src/winreg_extra.rs new file mode 100644 index 0000000..58c2d46 --- /dev/null +++ b/wfassoc/src/winreg_extra.rs @@ -0,0 +1,74 @@ +//! This module expand `winreg` crate to make it more suit for this crate. + +// region: Expand String + +/// The struct basically is the alias of String, but make a slight difference with it, +/// to make they are different when use it with String as generic argument. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ExpandString(String); + +impl ExpandString { + /// Construct new ExpandString. + pub fn new(s: String) -> Self { + Self(s) + } + + /// Create from &str + pub fn from_str(s: &str) -> Self { + Self(s.to_string()) + } + + /// Get reference to internal String. + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Get mutable reference to internal String. + pub fn as_mut_str(&mut self) -> &mut String { + &mut self.0 + } + + /// Comsule self, return internal String. + pub fn into_inner(self) -> String { + self.0 + } +} + +// Implement Deref trait to make it can be used like &str +use std::ops::Deref; +impl Deref for ExpandString { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +// Implement DerefMut trait +use std::ops::DerefMut; +impl DerefMut for ExpandString { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +// Implement From/Into trait for explicit convertion +impl From for ExpandString { + fn from(s: String) -> Self { + Self::new(s) + } +} + +impl From for String { + fn from(expand: ExpandString) -> Self { + expand.into_inner() + } +} + +impl From<&str> for ExpandString { + fn from(s: &str) -> Self { + Self::from_str(s) + } +} + +// endregion diff --git a/wfassoc_dylib/src/lib.rs b/wfassoc_dylib/src/lib.rs index 0804cdc..5661133 100644 --- a/wfassoc_dylib/src/lib.rs +++ b/wfassoc_dylib/src/lib.rs @@ -76,6 +76,22 @@ fn clear_last_error() { // endregion #[unsafe(no_mangle)] -pub extern "C" fn WFAdd(left: u32, right: u32) -> u32 { - left + right +pub extern "C" fn WFStartup() -> bool { + false +} + +#[unsafe(no_mangle)] +pub extern "C" fn WFShutdown() -> bool { + false +} + +#[unsafe(no_mangle)] +pub extern "C" fn WFGetLastError() -> *const c_char { + get_last_error() +} + +#[unsafe(no_mangle)] +pub extern "C" fn WFAdd(left: u32, right: u32, rv: *mut u32) -> bool { + unsafe { *rv = left + right; } + return true; }