diff --git a/wfassoc/src/lowlevel.rs b/wfassoc/src/lowlevel.rs index 3260f3c..b47e29a 100644 --- a/wfassoc/src/lowlevel.rs +++ b/wfassoc/src/lowlevel.rs @@ -79,31 +79,119 @@ impl TryFrom for Scope { } } -impl Scope { - /// Check whether we have enough privilege when operating in current scope. - /// If we have, simply return, otherwise return error. - fn check_privilege(&self) -> Result<(), Error> { - if matches!(self, Self::System if !utilities::has_privilege()) { - Err(Error::NoPrivilege) - } else { - Ok(()) +// endregion + +// region: Losse Structs + +// region: Losse ProgId + +/// The enum representing a losse Programmatic Identifiers (ProgId). +/// +/// In real world, not all software developers are willing to following Microsoft suggestions to use ProgId. +/// They use string which do not have any regulation as ProgId. +/// This enum is designed for handling this scenario. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum LosseProgId { + Plain(String), + Strict(concept::ProgId), +} + +impl Display for LosseProgId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + LosseProgId::Plain(v) => write!(f, "{}", v), + LosseProgId::Strict(v) => write!(f, "{}", v), } } } +impl From<&str> for LosseProgId { + fn from(s: &str) -> Self { + // match it for standard ProgId first + if let Ok(v) = concept::ProgId::from_str(s) { + Self::Strict(v) + } else { + // fallback with other + Self::Plain(s.to_string()) + } + } +} + +impl From for LosseProgId { + fn from(value: concept::ProgId) -> Self { + Self::Strict(value) + } +} + +// endregion + // endregion // region: Utilities +/// The territory of opening key. +/// +/// Scope and View will finally be converted into this type, +/// and delivered to an uniform function to open key. +#[derive(Debug, Copy, Clone)] +enum OpenKeyTerritory { + System, + User, + Hybrid, +} + +impl From for OpenKeyTerritory { + fn from(value: Scope) -> Self { + match value { + Scope::User => Self::User, + Scope::System => Self::System, + } + } +} + +impl From for OpenKeyTerritory { + fn from(value: View) -> Self { + match value { + View::User => Self::User, + View::System => Self::System, + View::Hybrid => Self::Hybrid, + } + } +} + /// The purpose of opening this key. -enum OpenScopePurpose { +#[derive(Debug, Copy, Clone)] +enum OpenKeyPurpose { /// Only read something. Read, /// Need to write something. ReadWrite, } +/// Check whether we have enough privilege when operating in given territory and purpose. +/// If we have, simply return, otherwise return error. +fn check_privilege(territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<(), Error> { + match purpose { + // Read is okey for every territory. + OpenKeyPurpose::Read => Ok(()), + // Read and write do not works in Hybrid, + // and should check privilege in System. + OpenKeyPurpose::ReadWrite => match territory { + OpenKeyTerritory::System => { + if utilities::has_privilege() { + Ok(()) + } else { + Err(Error::NoPrivilege) + } + } + OpenKeyTerritory::User => Ok(()), + OpenKeyTerritory::Hybrid => Err(Error::NoPrivilege), + }, + } +} + /// Internal used struct representing the result of opening scope or view. +#[derive(Debug)] struct OpenedKey { /// The parent key of opened key which must be presented. parent_key: RegKey, @@ -142,21 +230,25 @@ impl AppPathsKey { impl AppPathsKey { const APP_PATHS: &str = "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"; - fn open_scope(&self, scope: Scope, purpose: OpenScopePurpose) -> Result { + fn open_key( + &self, + territory: OpenKeyTerritory, + purpose: OpenKeyPurpose, + ) -> Result { // check privilege - if let OpenScopePurpose::ReadWrite = purpose { - scope.check_privilege()?; - } + check_privilege(territory, purpose)?; // Fetch the permission let perms = match purpose { - OpenScopePurpose::Read => KEY_READ, - OpenScopePurpose::ReadWrite => KEY_READ | KEY_WRITE, + OpenKeyPurpose::Read => KEY_READ, + OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, }; // get the root key - let hk = match scope { - Scope::User => RegKey::predef(HKEY_CURRENT_USER), - Scope::System => RegKey::predef(HKEY_LOCAL_MACHINE), + let hk = match territory { + OpenKeyTerritory::User => RegKey::predef(HKEY_CURRENT_USER), + OpenKeyTerritory::System => RegKey::predef(HKEY_LOCAL_MACHINE), + // There is no way that territory is hybrid. + OpenKeyTerritory::Hybrid => panic!("unexpected hybrid key territory"), }; // navigate to App Paths let app_paths = hk.open_subkey_with_flags(Self::APP_PATHS, perms)?; @@ -172,11 +264,11 @@ impl AppPathsKey { } fn open_scope_for_read(&self, scope: Scope) -> Result { - self.open_scope(scope, OpenScopePurpose::Read) + self.open_key(scope.into(), OpenKeyPurpose::Read) } fn open_scope_for_write(&self, scope: Scope) -> Result { - self.open_scope(scope, OpenScopePurpose::ReadWrite) + self.open_key(scope.into(), OpenKeyPurpose::ReadWrite) } pub fn is_exist(&self, scope: Scope) -> Result { @@ -274,51 +366,34 @@ impl ApplicationsKey { const FULL_APPLICATIONS: &str = "Software\\Classes\\Applications"; const PARTIAL_APPLICATIONS: &str = "Applications"; - fn open_scope(&self, scope: Scope, purpose: OpenScopePurpose) -> Result { + fn open_key( + &self, + territory: OpenKeyTerritory, + purpose: OpenKeyPurpose, + ) -> Result { // check privilege - if let OpenScopePurpose::ReadWrite = purpose { - scope.check_privilege()?; - } + check_privilege(territory, purpose)?; // Fetch the permission let perms = match purpose { - OpenScopePurpose::Read => KEY_READ, - OpenScopePurpose::ReadWrite => KEY_READ | KEY_WRITE, + OpenKeyPurpose::Read => KEY_READ, + OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, }; // get the root key - let hk = match scope { - Scope::User => RegKey::predef(HKEY_CURRENT_USER), - Scope::System => RegKey::predef(HKEY_LOCAL_MACHINE), + let hk = match territory { + OpenKeyTerritory::User => RegKey::predef(HKEY_CURRENT_USER), + OpenKeyTerritory::System => RegKey::predef(HKEY_LOCAL_MACHINE), + OpenKeyTerritory::Hybrid => RegKey::predef(HKEY_CLASSES_ROOT), }; - // navigate to Applications - let applications = hk.open_subkey_with_flags(Self::FULL_APPLICATIONS, perms)?; - // open extension key if possible - let this_app = regext::try_open_subkey_with_flags( - &applications, - regext::blank_path_guard(self.key_name.inner())?, - perms, - )?; - // okey - Ok(OpenedKey::new(applications, this_app)) - } - - fn open_view(&self, view: View) -> Result { - // define the permission - let perms = KEY_READ; - - // 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 applications = match view { - View::User | View::System => { + let applications = match territory { + OpenKeyTerritory::User | OpenKeyTerritory::System => { hk.open_subkey_with_flags(Self::FULL_APPLICATIONS, perms)? } - View::Hybrid => hk.open_subkey_with_flags(Self::PARTIAL_APPLICATIONS, perms)?, + OpenKeyTerritory::Hybrid => { + hk.open_subkey_with_flags(Self::PARTIAL_APPLICATIONS, perms)? + } }; - // open extension key if possible + // open app key if possible let this_app = regext::try_open_subkey_with_flags( &applications, regext::blank_path_guard(self.key_name.inner())?, @@ -329,11 +404,11 @@ impl ApplicationsKey { } fn open_view_for_read(&self, view: View) -> Result { - self.open_view(view) + self.open_key(view.into(), OpenKeyPurpose::Read) } fn open_scope_for_write(&self, scope: Scope) -> Result { - self.open_scope(scope, OpenScopePurpose::ReadWrite) + self.open_key(scope.into(), OpenKeyPurpose::ReadWrite) } pub fn is_exist(&self, view: View) -> Result { @@ -372,6 +447,35 @@ impl ApplicationsKey { .this_key .ok_or(Error::InexistantKey) } + + const NAMEOF_DEFAULT_BEHAVIOR_PART1: &str = "shell"; + const NAMEOF_DEFAULT_BEHAVIOR_PART3: &str = "command"; + + // TODO: + // We temporarily use String and &str as the command line argument parameter. + // We may introduce a new complete Rust struct for replacing this arbitrary string. + + pub fn get_default_behavior(&self, view: View) -> Result<(concept::Verb, String), Error> { + todo!() + } + + pub fn set_default_behavior( + &mut self, + scope: Scope, + beh: (concept::Verb, &str), + ) -> Result<(), Error> { + todo!() + } + + const NAMEOF_DEFAULT_ICON: &str = "DefaultIcon"; + + pub fn get_default_icon(&self, view: View) -> Result<(), Error> { + todo!() + } + + pub fn set_default_icon(&self, scope: Scope, icon: ()) -> Result<(), Error> { + todo!() + } } // endregion @@ -383,55 +487,6 @@ pub struct ExtKey { ext: concept::Ext, } -impl ExtKey { - const CLASSES: &str = "Software\\Classes"; - - fn open_scope(&self, scope: Scope) -> Result, Error> { - use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE}; - - // check privilege - scope.check_privilege()?; - // get the root key - let hk = match scope { - Scope::User => RegKey::predef(HKEY_CURRENT_USER), - Scope::System => RegKey::predef(HKEY_LOCAL_MACHINE), - }; - // navigate to classes - let classes = hk.open_subkey_with_flags(Self::CLASSES, KEY_READ | KEY_WRITE)?; - // open extension key if possible - let this_ext = regext::try_open_subkey_with_flags( - &classes, - regext::blank_path_guard(self.ext.dotted_inner())?, - KEY_READ | KEY_WRITE, - )?; - // okey - Ok(this_ext) - } - - fn open_view(&self, view: View) -> Result, Error> { - use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_READ}; - - // 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(Self::CLASSES, KEY_READ)?, - View::Hybrid => hk.open_subkey_with_flags("", KEY_READ)?, - }; - // open extension key if possible - let this_ext = regext::try_open_subkey_with_flags( - &classes, - regext::blank_path_guard(self.ext.dotted_inner())?, - KEY_READ, - )?; - // okey - Ok(this_ext) - } -} - impl ExtKey { pub fn new(inner: concept::Ext) -> Self { Self { ext: inner } @@ -442,47 +497,43 @@ impl ExtKey { } } -// endregion +impl ExtKey { + const FULL_CLASSES: &str = "Software\\Classes"; + const PARTIAL_CLASSES: &str = ""; -// region: ProgId Key + fn open_key( + &self, + territory: OpenKeyTerritory, + purpose: OpenKeyPurpose, + ) -> Result { + // check privilege + check_privilege(territory, purpose)?; + // Fetch the permission + let perms = match purpose { + OpenKeyPurpose::Read => KEY_READ, + OpenKeyPurpose::ReadWrite => KEY_READ | KEY_WRITE, + }; -// region: Losse ProgId - -/// The enum representing a losse Programmatic Identifiers (ProgId). -/// -/// In real world, not all software developers are willing to following Microsoft suggestions to use ProgId. -/// They use string which do not have any regulation as ProgId. -/// This enum is designed for handling this scenario. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum LosseProgId { - Plain(String), - Strict(concept::ProgId), -} - -impl Display for LosseProgId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - LosseProgId::Plain(v) => write!(f, "{}", v), - LosseProgId::Strict(v) => write!(f, "{}", v), - } - } -} - -impl From<&str> for LosseProgId { - fn from(s: &str) -> Self { - // match it for standard ProgId first - if let Ok(v) = concept::ProgId::from_str(s) { - Self::Strict(v) - } else { - // fallback with other - Self::Plain(s.to_string()) - } - } -} - -impl From for LosseProgId { - fn from(value: concept::ProgId) -> Self { - Self::Strict(value) + // navigate to extension container + let hk = match territory { + OpenKeyTerritory::User => RegKey::predef(HKEY_CURRENT_USER), + OpenKeyTerritory::System => RegKey::predef(HKEY_LOCAL_MACHINE), + OpenKeyTerritory::Hybrid => RegKey::predef(HKEY_CLASSES_ROOT), + }; + let classes = match territory { + OpenKeyTerritory::User | OpenKeyTerritory::System => { + hk.open_subkey_with_flags(Self::FULL_CLASSES, perms)? + } + OpenKeyTerritory::Hybrid => hk.open_subkey_with_flags(Self::PARTIAL_CLASSES, perms)?, + }; + // open extension key if possible + let this_ext = regext::try_open_subkey_with_flags( + &classes, + regext::blank_path_guard(self.ext.dotted_inner())?, + perms, + )?; + // okey + Ok(OpenedKey::new(classes, this_ext)) } } @@ -506,5 +557,3 @@ impl ProgIdKey { } // endregion - -// endregion