feat: introduce territory in lowlevel
This commit is contained in:
@@ -79,31 +79,119 @@ impl TryFrom<View> 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<concept::ProgId> 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<Scope> for OpenKeyTerritory {
|
||||
fn from(value: Scope) -> Self {
|
||||
match value {
|
||||
Scope::User => Self::User,
|
||||
Scope::System => Self::System,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<View> 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<OpenedKey, Error> {
|
||||
fn open_key(
|
||||
&self,
|
||||
territory: OpenKeyTerritory,
|
||||
purpose: OpenKeyPurpose,
|
||||
) -> Result<OpenedKey, Error> {
|
||||
// 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<OpenedKey, Error> {
|
||||
self.open_scope(scope, OpenScopePurpose::Read)
|
||||
self.open_key(scope.into(), OpenKeyPurpose::Read)
|
||||
}
|
||||
|
||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey, Error> {
|
||||
self.open_scope(scope, OpenScopePurpose::ReadWrite)
|
||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||
}
|
||||
|
||||
pub fn is_exist(&self, scope: Scope) -> Result<bool, Error> {
|
||||
@@ -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<OpenedKey, Error> {
|
||||
fn open_key(
|
||||
&self,
|
||||
territory: OpenKeyTerritory,
|
||||
purpose: OpenKeyPurpose,
|
||||
) -> Result<OpenedKey, Error> {
|
||||
// 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<OpenedKey, Error> {
|
||||
// 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<OpenedKey, Error> {
|
||||
self.open_view(view)
|
||||
self.open_key(view.into(), OpenKeyPurpose::Read)
|
||||
}
|
||||
|
||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey, Error> {
|
||||
self.open_scope(scope, OpenScopePurpose::ReadWrite)
|
||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||
}
|
||||
|
||||
pub fn is_exist(&self, view: View) -> Result<bool, Error> {
|
||||
@@ -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<Option<RegKey>, 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<Option<RegKey>, 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<OpenedKey, Error> {
|
||||
// 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),
|
||||
// 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)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<concept::ProgId> for LosseProgId {
|
||||
fn from(value: concept::ProgId) -> Self {
|
||||
Self::Strict(value)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user