feat: add Windows-specific modules and enhance program registration
- Add new modules for Windows command line handling (wincmd) and registry extensions (winreg_extra) - Replace Manner struct with simple String and add identifier validation - Update WFAdd function signature and add new WFStartup/WFShutdown functions - Implement ExpandString wrapper for registry operations
This commit is contained in:
@ -6,11 +6,14 @@ compile_error!("Crate wfassoc is only supported on Windows.");
|
|||||||
|
|
||||||
pub mod assoc;
|
pub mod assoc;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
|
pub mod wincmd;
|
||||||
|
pub mod winreg_extra;
|
||||||
|
|
||||||
use indexmap::{IndexMap, IndexSet};
|
use indexmap::{IndexMap, IndexSet};
|
||||||
|
use regex::Regex;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
use thiserror::Error as TeError;
|
use thiserror::Error as TeError;
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::{
|
use winreg::enums::{
|
||||||
@ -31,6 +34,8 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error("no administrative privilege")]
|
#[error("no administrative privilege")]
|
||||||
NoPrivilege,
|
NoPrivilege,
|
||||||
|
#[error("given identifier \"{0}\" of application is invalid")]
|
||||||
|
BadIdentifier(String),
|
||||||
#[error("given full path to application is invalid")]
|
#[error("given full path to application is invalid")]
|
||||||
BadFullAppPath,
|
BadFullAppPath,
|
||||||
#[error("manner \"{0}\" is already registered")]
|
#[error("manner \"{0}\" is already registered")]
|
||||||
@ -118,33 +123,6 @@ impl From<Scope> for View {
|
|||||||
|
|
||||||
// endregion
|
// 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
|
// region: Program
|
||||||
|
|
||||||
/// The struct representing a complete program for registration and unregistration.
|
/// The struct representing a complete program for registration and unregistration.
|
||||||
@ -155,7 +133,7 @@ pub struct Program {
|
|||||||
/// The fully qualified path to the application.
|
/// The fully qualified path to the application.
|
||||||
full_path: PathBuf,
|
full_path: PathBuf,
|
||||||
/// The collection holding all manners of this program.
|
/// The collection holding all manners of this program.
|
||||||
manners: IndexSet<Manner>,
|
manners: IndexSet<String>,
|
||||||
/// The collection holding all file extensions supported by this program.
|
/// The collection holding all file extensions supported by this program.
|
||||||
/// The key is file estension and value is its associated manner for opening it.
|
/// The key is file estension and value is its associated manner for opening it.
|
||||||
exts: IndexMap<assoc::Ext, Token>,
|
exts: IndexMap<assoc::Ext, Token>,
|
||||||
@ -177,7 +155,12 @@ impl Program {
|
|||||||
///
|
///
|
||||||
/// `full_path` is the fully qualified path to the application.
|
/// `full_path` is the fully qualified path to the application.
|
||||||
pub fn new(identifier: &str, full_path: &Path) -> Result<Self> {
|
pub fn new(identifier: &str, full_path: &Path) -> Result<Self> {
|
||||||
// TODO: Add checker for identifier
|
// Check identifier
|
||||||
|
static RE: LazyLock<Regex> = 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 {
|
Ok(Self {
|
||||||
identifier: identifier.to_string(),
|
identifier: identifier.to_string(),
|
||||||
full_path: full_path.to_path_buf(),
|
full_path: full_path.to_path_buf(),
|
||||||
@ -188,8 +171,9 @@ impl Program {
|
|||||||
|
|
||||||
/// Add manner provided by this program.
|
/// Add manner provided by this program.
|
||||||
pub fn add_manner(&mut self, manner: &str) -> Result<Token> {
|
pub fn add_manner(&mut self, manner: &str) -> Result<Token> {
|
||||||
|
// TODO: Use wincmd::CmdArgs instead of String.
|
||||||
// Create manner from string
|
// Create manner from string
|
||||||
let manner = Manner::new(manner);
|
let manner = manner.to_string();
|
||||||
// Backup a stringfied manner for error output.
|
// Backup a stringfied manner for error output.
|
||||||
let manner_str = manner.to_string();
|
let manner_str = manner.to_string();
|
||||||
// Insert manner.
|
// Insert manner.
|
||||||
@ -197,19 +181,19 @@ impl Program {
|
|||||||
if self.manners.insert(manner) {
|
if self.manners.insert(manner) {
|
||||||
Ok(idx)
|
Ok(idx)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::DupExt(manner_str))
|
Err(Error::DupManner(manner_str))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the reference to manner with given token.
|
/// Get the string display of manner represented by given token
|
||||||
pub fn get_manner(&self, token: Token) -> Option<&Manner> {
|
pub fn get_manner_str(&self, token: Token) -> Option<String> {
|
||||||
self.manners.get_index(token)
|
self.manners.get_index(token).map(|s| s.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add file extension supported by this program and its associated manner.
|
/// Add file extension supported by this program and its associated manner.
|
||||||
pub fn add_ext(&mut self, ext: &str, token: Token) -> Result<Token> {
|
pub fn add_ext(&mut self, ext: &str, token: Token) -> Result<Token> {
|
||||||
// Check manner token
|
// Check manner token
|
||||||
if let None = self.get_manner(token) {
|
if let None = self.manners.get_index(token) {
|
||||||
return Err(Error::InvalidAssocManner);
|
return Err(Error::InvalidAssocManner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,9 +210,9 @@ impl Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the reference to file extension with given token.
|
/// Get the string display of file extension represented by given token
|
||||||
pub fn get_ext(&self, token: Token) -> Option<&assoc::Ext> {
|
pub fn get_ext_str(&self, token: Token) -> Option<String> {
|
||||||
self.exts.get_index(token).map(|p| p.0)
|
self.exts.get_index(token).map(|p| p.0.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
wfassoc/src/wincmd.rs
Normal file
18
wfassoc/src/wincmd.rs
Normal file
@ -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
|
||||||
74
wfassoc/src/winreg_extra.rs
Normal file
74
wfassoc/src/winreg_extra.rs
Normal file
@ -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<String> for ExpandString {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self::new(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExpandString> 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
|
||||||
@ -76,6 +76,22 @@ fn clear_last_error() {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn WFAdd(left: u32, right: u32) -> u32 {
|
pub extern "C" fn WFStartup() -> bool {
|
||||||
left + right
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user