diff --git a/wfassoc/src/lib.rs b/wfassoc/src/lib.rs index 07346f5..a2eafb2 100644 --- a/wfassoc/src/lib.rs +++ b/wfassoc/src/lib.rs @@ -28,7 +28,9 @@ pub enum Error { // endregion -/// The scope where wfassoc will operate. +// region: Basic Types + +/// The scope where wfassoc will register and unregister. #[derive(Debug, Copy, Clone)] pub enum Scope { /// Scope for current user. @@ -37,6 +39,18 @@ pub enum Scope { System, } +/// The view when wfassoc querying infomations. +#[derive(Debug, Copy, Clone)] +pub enum View { + /// The view of current user. + User, + /// The view of system. + System, + /// Hybrid view of User and System. + /// It can be seen as that we use System first and then use User to override any existing items. + Hybrid, +} + /// Check whether current process has administrative privilege. /// /// It usually means that checking whether current process is running as Administrator. @@ -90,10 +104,13 @@ pub fn has_privilege() -> bool { is_member != 0 } +// endregion + // region: File Extension /// The struct representing an file extension which must start with dot (`.`) /// and followed by at least one arbitrary characters. +#[derive(Debug, Clone)] pub struct FileExt { /// The body of file extension (excluding dot). inner: String, @@ -110,14 +127,14 @@ impl FileExt { } } - pub fn query(&self, scope: Scope) -> Option { - FileExtAssoc::new(self, scope) + pub fn query(&self, view: View) -> Option { + FileExtAssoc::new(self, view) } } /// The error occurs when try parsing string into FileExt. #[derive(Debug, TeError)] -#[error("given file extension name is illegal")] +#[error("given file extension is illegal")] pub struct ParseFileExtError {} impl ParseFileExtError { @@ -141,34 +158,44 @@ impl FromStr for FileExt { } /// The association infomations of specific file extension. +#[derive(Debug)] pub struct FileExtAssoc { default: String, open_with_progids: Vec, } impl FileExtAssoc { - fn new(file_ext: &FileExt, scope: Scope) -> Option { - let hk = match scope { - Scope::User => winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER), - Scope::System => winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE), - }; - let classes = hk - .open_subkey_with_flags("Software\\Classes", winreg::enums::KEY_READ) - .unwrap(); - let thisext = - match classes.open_subkey_with_flags(file_ext.to_string(), winreg::enums::KEY_READ) { - Ok(v) => v, - Err(e) => return None, - }; + fn new(file_ext: &FileExt, view: View) -> Option { + use winreg::RegKey; + use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ}; - let default = thisext.get_value("").unwrap_or(String::new()); - let open_with_progids = if let Ok(progids) = - thisext.open_subkey_with_flags("OpenWithProdIds", winreg::enums::KEY_READ) - { - progids.enum_keys().map(|x| x.unwrap()).collect() - } else { - Vec::new() + // navigate to extension container + let hk = match view { + View::User => RegKey::predef(HKEY_CURRENT_USER), + View::System => RegKey::predef(HKEY_LOCAL_MACHINE), + View::Hybrid => RegKey::predef(HKEY_CLASSES_ROOT), }; + let classes = match view { + View::User | View::System => hk + .open_subkey_with_flags("Software\\Classes", KEY_READ) + .unwrap(), + View::Hybrid => hk.open_subkey_with_flags("", KEY_READ).unwrap(), + }; + + // open extension key if possible + let thisext = match classes.open_subkey_with_flags(file_ext.to_string(), KEY_READ) { + Ok(v) => v, + Err(_) => return None, + }; + + // fetch extension infos. + let default = thisext.get_value("").unwrap_or(String::new()); + let open_with_progids = + if let Ok(progids) = thisext.open_subkey_with_flags("OpenWithProdIds", KEY_READ) { + progids.enum_keys().map(|x| x.unwrap()).filter(|k| !k.is_empty()).collect() + } else { + Vec::new() + }; Some(Self { default, diff --git a/wfassoc_exec/src/main.rs b/wfassoc_exec/src/main.rs index 20195d7..3728e02 100644 --- a/wfassoc_exec/src/main.rs +++ b/wfassoc_exec/src/main.rs @@ -2,7 +2,7 @@ use clap::{Parser, Subcommand}; use comfy_table::Table; use std::process; use thiserror::Error as TeError; -use wfassoc::{Error as WfError, FileExt, Scope}; +use wfassoc::{Error as WfError, FileExt, Scope, View}; // region: Basic Types @@ -95,31 +95,36 @@ fn run_query(cli: Cli) -> Result<()> { ".kra", ".xcf", ".avif", ".qoi", ".apng", ".exr", ]; - let mut table = Table::new(); - table.set_header(["Extension", "Default Open", "Open With"]); for ext in exts.iter().map(|e| FileExt::new(e).unwrap()) { - if let Some(ext_assoc) = ext.query(Scope::User) { - if ext_assoc.len_open_with_progid() == 0 { - table.add_row([ext.to_string().as_str(), ext_assoc.get_default(), ""]); - } else { - for (i, open_with_entry) in ext_assoc.iter_open_with_progids().enumerate() { - if i == 0 { - table.add_row([ - ext.to_string().as_str(), - ext_assoc.get_default(), - open_with_entry, - ]); - } else { - table.add_row(["", "", open_with_entry]); - } - } - } - } else { - table.add_row([ext.to_string().as_str(), "", ""]); + if let Some(ext_assoc) = ext.query(View::Hybrid) { + println!("{:?}", ext_assoc) } } - println!("{table}"); + // let mut table = Table::new(); + // table.set_header(["Extension", "Default Open", "Open With"]); + // for ext in exts.iter().map(|e| FileExt::new(e).unwrap()) { + // if let Some(ext_assoc) = ext.query(View::Hybrid) { + // if ext_assoc.len_open_with_progid() == 0 { + // table.add_row([ext.to_string().as_str(), ext_assoc.get_default(), ""]); + // } else { + // for (i, open_with_entry) in ext_assoc.iter_open_with_progids().enumerate() { + // if i == 0 { + // table.add_row([ + // ext.to_string().as_str(), + // ext_assoc.get_default(), + // open_with_entry, + // ]); + // } else { + // table.add_row(["", "", open_with_entry]); + // } + // } + // } + // } else { + // table.add_row([ext.to_string().as_str(), "", ""]); + // } + // } + // println!("{table}"); // let program = Program::new(); // program.query()?;