doc: add docstring for lowlevel
This commit is contained in:
@@ -22,7 +22,7 @@ pub const INVALID_INDEX: usize = usize::MAX;
|
|||||||
/// However, I don't want to add it as this crate's dependency,
|
/// However, I don't want to add it as this crate's dependency,
|
||||||
/// because I don't use anything within it except this type.
|
/// because I don't use anything within it except this type.
|
||||||
/// So I check Microsoft document, re-define it in there for this crate.
|
/// So I check Microsoft document, re-define it in there for this crate.
|
||||||
/// Reference: https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types
|
/// Reference: <https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types>
|
||||||
pub type HICON = *mut c_void;
|
pub type HICON = *mut c_void;
|
||||||
|
|
||||||
/// The invalid value of Win32 HICON handle.
|
/// The invalid value of Win32 HICON handle.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! The module gives a convenient highlevel wrapper for lowlevel file association operations.
|
||||||
|
|
||||||
use crate::lowlevel;
|
use crate::lowlevel;
|
||||||
|
|
||||||
// region: Utilities
|
// region: Utilities
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! This crate provide utilities fetching and manilupating Windows file association.
|
//! This crate provide utilities fetching and manilupating Windows file association.
|
||||||
//! All code under crate are following Microsoft document: https://learn.microsoft.com/en-us/windows/win32/shell/customizing-file-types-bumper
|
//! All code under crate are following Microsoft document: <https://learn.microsoft.com/en-us/windows/win32/shell/customizing-file-types-bumper>
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
compile_error!("Crate wfassoc is only supported on Windows.");
|
compile_error!("Crate wfassoc is only supported on Windows.");
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! The module containing lowlevel operation involving file association.
|
||||||
|
|
||||||
use crate::win32::{concept, regext, utilities};
|
use crate::win32::{concept, regext, utilities};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@@ -58,6 +60,7 @@ pub enum Scope {
|
|||||||
pub struct TryFromViewError {}
|
pub struct TryFromViewError {}
|
||||||
|
|
||||||
impl TryFromViewError {
|
impl TryFromViewError {
|
||||||
|
/// Creates a new `TryFromViewError`.
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
@@ -106,9 +109,13 @@ impl From<Scope> for View {
|
|||||||
|
|
||||||
/// The enum representing a losse Programmatic Identifiers (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.
|
/// In real world, not all software developers are willing
|
||||||
/// They use string which do not have any regulation as ProgId.
|
/// to following Microsoft suggestions to use ProgId.
|
||||||
|
/// They frequently use string which do not have any regulation as ProgId.
|
||||||
/// This enum is designed for handling this scenario.
|
/// This enum is designed for handling this scenario.
|
||||||
|
///
|
||||||
|
/// You can utilize [FromStr] trait to build this struct.
|
||||||
|
/// Standard ProgId and any casual string are both acceptable.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum LosseProgId {
|
pub enum LosseProgId {
|
||||||
Plain(String),
|
Plain(String),
|
||||||
@@ -151,6 +158,11 @@ impl From<concept::ProgId> for LosseProgId {
|
|||||||
/// In real usage, programmer can use Icon Reference String,
|
/// In real usage, programmer can use Icon Reference String,
|
||||||
/// or a plain string pointing to a icon file as the icon setting value.
|
/// or a plain string pointing to a icon file as the icon setting value.
|
||||||
/// This enum is designed for handling this scenario.
|
/// This enum is designed for handling this scenario.
|
||||||
|
///
|
||||||
|
/// You can utilize [FromStr] trait to build this struct.
|
||||||
|
/// You can pass a standard Icon Reference String or a path to specific icon file.
|
||||||
|
/// However, if you choose the last one, you must make sure
|
||||||
|
/// that your path is must be unquoted, but expand string is allowed.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum IconResVariant {
|
pub enum IconResVariant {
|
||||||
Plain(String),
|
Plain(String),
|
||||||
@@ -158,7 +170,12 @@ pub enum IconResVariant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IconResVariant {
|
impl IconResVariant {
|
||||||
|
/// Extracts the icon resource with the given size kind.
|
||||||
pub fn extract(&self, kind: concept::IconSizeKind) -> Result<concept::IconRc> {
|
pub fn extract(&self, kind: concept::IconSizeKind) -> Result<concept::IconRc> {
|
||||||
|
// TODO:
|
||||||
|
// Once we implement quote mechanism inside IconRefStr,
|
||||||
|
// there is no need to preserve this unquote code in there.
|
||||||
|
// However the expand string mechanism should be kept.
|
||||||
let rc = match self {
|
let rc = match self {
|
||||||
IconResVariant::Plain(v) => concept::IconRc::with_ico_file(v.as_str(), kind)?,
|
IconResVariant::Plain(v) => concept::IconRc::with_ico_file(v.as_str(), kind)?,
|
||||||
IconResVariant::RefStr(v) => {
|
IconResVariant::RefStr(v) => {
|
||||||
@@ -214,6 +231,9 @@ impl From<concept::IconRefStr> for IconResVariant {
|
|||||||
/// In real usage, programmer can use String Reference String,
|
/// In real usage, programmer can use String Reference String,
|
||||||
/// or a plain string as the string setting value.
|
/// or a plain string as the string setting value.
|
||||||
/// This enum is designed for handling this scenario.
|
/// This enum is designed for handling this scenario.
|
||||||
|
///
|
||||||
|
/// You can utilize [FromStr] trait to build this struct.
|
||||||
|
/// You can pass a standard String Reference String or a plain string.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum StrResVariant {
|
pub enum StrResVariant {
|
||||||
Plain(String),
|
Plain(String),
|
||||||
@@ -221,7 +241,12 @@ pub enum StrResVariant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StrResVariant {
|
impl StrResVariant {
|
||||||
|
/// Resolves this string resource variant into a plain string.
|
||||||
pub fn extract(&self) -> Result<String> {
|
pub fn extract(&self) -> Result<String> {
|
||||||
|
// TODO:
|
||||||
|
// Once we implement quote mechanism inside StrRefStr,
|
||||||
|
// there is no need to preserve this unquote code in there.
|
||||||
|
// However the expand string mechanism should be kept.
|
||||||
let rv = match self {
|
let rv = match self {
|
||||||
// For plain string, we just simply clone it.
|
// For plain string, we just simply clone it.
|
||||||
StrResVariant::Plain(v) => v.clone(),
|
StrResVariant::Plain(v) => v.clone(),
|
||||||
@@ -276,6 +301,12 @@ impl From<concept::StrRefStr> for StrResVariant {
|
|||||||
// region: Shell Verb
|
// region: Shell Verb
|
||||||
|
|
||||||
/// The struct representing a shell verb pair.
|
/// The struct representing a shell verb pair.
|
||||||
|
///
|
||||||
|
/// Shell verb is frequently used in Windows to describe how to use an application.
|
||||||
|
/// It can be used to define the default behavior of application,
|
||||||
|
/// or the way to use this application for opening a specific file.
|
||||||
|
///
|
||||||
|
/// You can use [ShellVerb::new] with given verb and command line infos to build this struct.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ShellVerb {
|
pub struct ShellVerb {
|
||||||
verb: concept::Verb,
|
verb: concept::Verb,
|
||||||
@@ -283,14 +314,17 @@ pub struct ShellVerb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ShellVerb {
|
impl ShellVerb {
|
||||||
|
/// Creates a new `ShellVerb` from a verb and a command.
|
||||||
pub fn new(verb: concept::Verb, command: concept::CmdLine) -> Self {
|
pub fn new(verb: concept::Verb, command: concept::CmdLine) -> Self {
|
||||||
Self { verb, command }
|
Self { verb, command }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the verb of this shell verb pair.
|
||||||
pub fn get_verb(&self) -> &concept::Verb {
|
pub fn get_verb(&self) -> &concept::Verb {
|
||||||
&self.verb
|
&self.verb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the command of this shell verb pair.
|
||||||
pub fn get_command(&self) -> &concept::CmdLine {
|
pub fn get_command(&self) -> &concept::CmdLine {
|
||||||
&self.command
|
&self.command
|
||||||
}
|
}
|
||||||
@@ -304,7 +338,7 @@ impl ShellVerb {
|
|||||||
|
|
||||||
// region: Opened Key
|
// region: Opened Key
|
||||||
|
|
||||||
/// Internal used struct representing the result of opening scope or view.
|
/// Internal used struct representing the result about opening registry key in given scope or view.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct OpenedKey {
|
struct OpenedKey {
|
||||||
/// The parent key of opened key which must be presented.
|
/// The parent key of opened key which must be presented.
|
||||||
@@ -314,6 +348,7 @@ struct OpenedKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpenedKey {
|
impl OpenedKey {
|
||||||
|
/// Creates a new `OpenedKey` with the given parent key and optional opened key.
|
||||||
fn new(parent_key: RegKey, this_key: Option<RegKey>) -> Self {
|
fn new(parent_key: RegKey, this_key: Option<RegKey>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
parent_key,
|
parent_key,
|
||||||
@@ -326,10 +361,10 @@ impl OpenedKey {
|
|||||||
|
|
||||||
// region: Open Key Territory
|
// region: Open Key Territory
|
||||||
|
|
||||||
/// The territory of opening key.
|
/// Internal used enum representing the territory of opening registry key.
|
||||||
///
|
///
|
||||||
/// Scope and View will finally be converted into this type,
|
/// [Scope] and [View] will finally be converted into this type,
|
||||||
/// and delivered to an uniform function to open key.
|
/// and delivered to an uniform function to open registry key.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
enum OpenKeyTerritory {
|
enum OpenKeyTerritory {
|
||||||
System,
|
System,
|
||||||
@@ -360,7 +395,7 @@ impl From<View> for OpenKeyTerritory {
|
|||||||
|
|
||||||
// region: Open Key Purpose
|
// region: Open Key Purpose
|
||||||
|
|
||||||
/// The purpose of opening this key.
|
/// The purpose of opening given registry key.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
enum OpenKeyPurpose {
|
enum OpenKeyPurpose {
|
||||||
/// Only read something.
|
/// Only read something.
|
||||||
@@ -370,6 +405,7 @@ enum OpenKeyPurpose {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpenKeyPurpose {
|
impl OpenKeyPurpose {
|
||||||
|
/// Converts this purpose to the corresponding registry permission flags.
|
||||||
fn to_permission(&self) -> u32 {
|
fn to_permission(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
OpenKeyPurpose::Read => PERM_R,
|
OpenKeyPurpose::Read => PERM_R,
|
||||||
@@ -411,7 +447,16 @@ fn check_privilege(territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Resu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove quote pair if possible.
|
// TODO:
|
||||||
|
// This function should be eliminated or moved to another place,
|
||||||
|
// once we finish the quote mechanism in StrRefStr and IconRefStr.
|
||||||
|
|
||||||
|
/// Internal function removing quote pair if possible.
|
||||||
|
///
|
||||||
|
/// # Todo
|
||||||
|
///
|
||||||
|
/// This function actually is the compromise about the current design of this crate.
|
||||||
|
/// This function will be optimized in future.
|
||||||
///
|
///
|
||||||
/// In some cases, the path part of [concept::IconRefStr] or [concept::StrRefStr] is quoted by quote.
|
/// In some cases, the path part of [concept::IconRefStr] or [concept::StrRefStr] is quoted by quote.
|
||||||
/// This can no be recognized by Win32 functions.
|
/// This can no be recognized by Win32 functions.
|
||||||
@@ -431,6 +476,9 @@ fn strip_quote<'a>(s: &'a str) -> &'a str {
|
|||||||
|
|
||||||
// region: Registry Keys
|
// region: Registry Keys
|
||||||
|
|
||||||
|
// YYC MARK:
|
||||||
|
// Load submodules and re-export them.
|
||||||
|
|
||||||
mod app_path_key;
|
mod app_path_key;
|
||||||
mod applications_key;
|
mod applications_key;
|
||||||
mod ext_key;
|
mod ext_key;
|
||||||
|
|||||||
@@ -5,16 +5,30 @@ use crate::win32::{concept, regext};
|
|||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
use winreg::enums::{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
|
/// A registry key wrapper for `Software\Microsoft\Windows\CurrentVersion\App Paths\<APP>`.
|
||||||
|
///
|
||||||
|
/// This key can only be opened under a Scope (not a View), since
|
||||||
|
/// "App Paths" only exists under HKCU and HKLM, not under a merged HKCR view.
|
||||||
|
///
|
||||||
|
/// This struct provides [Self::is_exist], [Self::ensure] and [Self::delete] to
|
||||||
|
/// check whether this key is in Registry, make sure this key is presneted in Registry,
|
||||||
|
/// and delete self from Registry respectively.
|
||||||
|
///
|
||||||
|
/// And there are some getter and setter in this struct,
|
||||||
|
/// before calling them, you must use [Self::ensure] make sure that this key is presented in Registry,
|
||||||
|
/// otherwise these functions will return errors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct AppPathsKey {
|
pub struct AppPathsKey {
|
||||||
key_name: concept::FileName,
|
key_name: concept::FileName,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppPathsKey {
|
impl AppPathsKey {
|
||||||
|
/// Creates a new `AppPathsKey` with the given file name.
|
||||||
pub fn new(inner: concept::FileName) -> Self {
|
pub fn new(inner: concept::FileName) -> Self {
|
||||||
Self { key_name: inner }
|
Self { key_name: inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the inner file name of this application path key.
|
||||||
pub fn inner(&self) -> &concept::FileName {
|
pub fn inner(&self) -> &concept::FileName {
|
||||||
&self.key_name
|
&self.key_name
|
||||||
}
|
}
|
||||||
@@ -23,6 +37,7 @@ impl AppPathsKey {
|
|||||||
impl AppPathsKey {
|
impl AppPathsKey {
|
||||||
const APP_PATHS: &str = "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
|
const APP_PATHS: &str = "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
|
||||||
|
|
||||||
|
/// Attempts to open the registry key for this application path with the given territory and purpose.
|
||||||
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
||||||
// check privilege
|
// check privilege
|
||||||
check_privilege(territory, purpose)?;
|
check_privilege(territory, purpose)?;
|
||||||
@@ -49,14 +64,18 @@ impl AppPathsKey {
|
|||||||
Ok(OpenedKey::new(app_paths, this_app))
|
Ok(OpenedKey::new(app_paths, this_app))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-only access under the given scope.
|
||||||
fn open_scope_for_read(&self, scope: Scope) -> Result<OpenedKey> {
|
fn open_scope_for_read(&self, scope: Scope) -> Result<OpenedKey> {
|
||||||
self.open_key(scope.into(), OpenKeyPurpose::Read)
|
self.open_key(scope.into(), OpenKeyPurpose::Read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-write access under the given scope.
|
||||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
||||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether this application path key exists in the registry under the given scope.
|
||||||
|
/// Return true if it is presented in registry, otherwise false.
|
||||||
pub fn is_exist(&self, scope: Scope) -> Result<bool> {
|
pub fn is_exist(&self, scope: Scope) -> Result<bool> {
|
||||||
let key = self.open_scope_for_read(scope)?.this_key;
|
let key = self.open_scope_for_read(scope)?.this_key;
|
||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
@@ -91,55 +110,63 @@ impl AppPathsKey {
|
|||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YYC MARK:
|
||||||
|
// Reference: <https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-app-paths-subkey>
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// We only support these keys in there because current interface are enough to use.
|
||||||
|
// We may expand these in future.
|
||||||
|
|
||||||
|
/// Opens the registry key for getter reading, returning an error if the key does not exist.
|
||||||
fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
self.open_scope_for_read(scope)?
|
self.open_scope_for_read(scope)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for setter writing, returning an error if the key does not exist.
|
||||||
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
self.open_scope_for_write(scope)?
|
self.open_scope_for_write(scope)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-app-paths-subkey
|
|
||||||
|
|
||||||
const NAMEOF_DEFAULT: &str = "";
|
const NAMEOF_DEFAULT: &str = "";
|
||||||
|
|
||||||
|
/// Gets the default value of this application path key.
|
||||||
///
|
///
|
||||||
///
|
/// The content of this key should point to the fully qualified path of the application.
|
||||||
/// This field point to the fully qualified path to the application.
|
|
||||||
pub fn get_default(&self, scope: Scope) -> Result<String> {
|
pub fn get_default(&self, scope: Scope) -> Result<String> {
|
||||||
let key = self.open_scope_for_getter(scope)?;
|
let key = self.open_scope_for_getter(scope)?;
|
||||||
Ok(key.get_value(Self::NAMEOF_DEFAULT)?)
|
Ok(key.get_value(Self::NAMEOF_DEFAULT)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Sets the default value of this application path key.
|
||||||
///
|
|
||||||
/// This field should be filled with fully qualified path to the application.
|
|
||||||
pub fn set_default(&mut self, scope: Scope, value: &str) -> Result<()> {
|
pub fn set_default(&mut self, scope: Scope, value: &str) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
key.set_value(Self::NAMEOF_DEFAULT, &value)?;
|
key.set_value(Self::NAMEOF_DEFAULT, &value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// This key may be REG_SZ or REG_EXPAND_SZ.
|
||||||
|
// Currently we see them as one type.
|
||||||
|
// This should be improved in future.
|
||||||
|
|
||||||
const NAMEOF_PATH: &str = "Path";
|
const NAMEOF_PATH: &str = "Path";
|
||||||
|
|
||||||
|
/// Gets the `Path` value of this application path key.
|
||||||
///
|
///
|
||||||
///
|
/// The content of this key will be added to PATH environment variable,
|
||||||
/// This field point to the added path for PATH environment variable.
|
/// to enable that application can find its dependencies conveniently.
|
||||||
/// Usually it is the path to application directory.
|
/// If it contains multiple pathes, semicolon-separated form is required.
|
||||||
|
/// Usually this is the path to application directory.
|
||||||
pub fn get_path(&self, scope: Scope) -> Result<String> {
|
pub fn get_path(&self, scope: Scope) -> Result<String> {
|
||||||
let key = self.open_scope_for_getter(scope)?;
|
let key = self.open_scope_for_getter(scope)?;
|
||||||
Ok(key.get_value(Self::NAMEOF_PATH)?)
|
Ok(key.get_value(Self::NAMEOF_PATH)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Sets the `Path` value of this application path key.
|
||||||
///
|
|
||||||
/// This field should be the added path for PATH environment variable.
|
|
||||||
/// Usually it is the path to application directory.
|
|
||||||
pub fn set_path(&mut self, scope: Scope, value: &str) -> Result<()> {
|
pub fn set_path(&mut self, scope: Scope, value: &str) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
key.set_value(Self::NAMEOF_PATH, &value)?;
|
key.set_value(Self::NAMEOF_PATH, &value)?;
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ use crate::win32::{concept, regext};
|
|||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
|
/// A registry key wrapper for `Software\Classes\Applications\<APP>`.
|
||||||
|
///
|
||||||
|
/// This struct provides [Self::is_exist], [Self::ensure] and [Self::delete] to
|
||||||
|
/// check whether this key is in Registry, make sure this key is presneted in Registry,
|
||||||
|
/// and delete self from Registry respectively.
|
||||||
|
///
|
||||||
|
/// And there are some getter and setter in this struct,
|
||||||
|
/// before calling them, you must use [Self::ensure] make sure that this key is presented in Registry,
|
||||||
|
/// otherwise these functions will return errors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ApplicationsKey {
|
pub struct ApplicationsKey {
|
||||||
key_name: concept::FileName,
|
key_name: concept::FileName,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApplicationsKey {
|
impl ApplicationsKey {
|
||||||
|
/// Creates a new `ApplicationsKey` with the given file name.
|
||||||
pub fn new(inner: concept::FileName) -> Self {
|
pub fn new(inner: concept::FileName) -> Self {
|
||||||
Self { key_name: inner }
|
Self { key_name: inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the inner file name of this applications key.
|
||||||
pub fn inner(&self) -> &concept::FileName {
|
pub fn inner(&self) -> &concept::FileName {
|
||||||
&self.key_name
|
&self.key_name
|
||||||
}
|
}
|
||||||
@@ -25,6 +36,7 @@ impl ApplicationsKey {
|
|||||||
const FULL_APPLICATIONS: &str = "Software\\Classes\\Applications";
|
const FULL_APPLICATIONS: &str = "Software\\Classes\\Applications";
|
||||||
const PARTIAL_APPLICATIONS: &str = "Applications";
|
const PARTIAL_APPLICATIONS: &str = "Applications";
|
||||||
|
|
||||||
|
/// Attempts to open the registry key for this application with the given territory and purpose.
|
||||||
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
||||||
// check privilege
|
// check privilege
|
||||||
check_privilege(territory, purpose)?;
|
check_privilege(territory, purpose)?;
|
||||||
@@ -55,14 +67,18 @@ impl ApplicationsKey {
|
|||||||
Ok(OpenedKey::new(applications, this_app))
|
Ok(OpenedKey::new(applications, this_app))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-only access under the given view.
|
||||||
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
||||||
self.open_key(view.into(), OpenKeyPurpose::Read)
|
self.open_key(view.into(), OpenKeyPurpose::Read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-write access under the given scope.
|
||||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
||||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether this applications key exists in the registry under the given view.
|
||||||
|
/// Return true if it is presented in registry, otherwise false.
|
||||||
pub fn is_exist(&self, view: View) -> Result<bool> {
|
pub fn is_exist(&self, view: View) -> Result<bool> {
|
||||||
let key = self.open_view_for_read(view)?.this_key;
|
let key = self.open_view_for_read(view)?.this_key;
|
||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
@@ -98,24 +114,41 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-applications-subkey
|
// Reference: <https://learn.microsoft.com/en-us/windows/win32/shell/app-registration#using-the-applications-subkey>
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// We only support these keys in there because current interface are enough to use.
|
||||||
|
// We may expand these in future.
|
||||||
|
|
||||||
|
/// Opens the registry key for getter reading, returning an error if the key does not exist.
|
||||||
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
||||||
self.open_view_for_read(view)?
|
self.open_view_for_read(view)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for setter writing, returning an error if the key does not exist.
|
||||||
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
self.open_scope_for_write(scope)?
|
self.open_scope_for_write(scope)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Support mutiple ShellVerb in future.
|
||||||
|
|
||||||
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
|
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
|
||||||
const NAMEOF_SHELL_VERB_PART3: &str = "command";
|
const NAMEOF_SHELL_VERB_PART3: &str = "command";
|
||||||
const NAMEOF_SHELL_VERB_PART4: &str = "";
|
const NAMEOF_SHELL_VERB_PART4: &str = "";
|
||||||
|
|
||||||
|
/// Gets the shell verb registered for this application under the given view.
|
||||||
|
/// Return `None` if there is no any shell verb.
|
||||||
|
///
|
||||||
|
/// # Todo
|
||||||
|
///
|
||||||
|
/// Currently we only support single shell verb.
|
||||||
|
/// So if there is multiple shell verb, this function still return `None`.
|
||||||
|
/// This issue will be resolved in future.
|
||||||
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>> {
|
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
|
|
||||||
@@ -166,6 +199,15 @@ impl ApplicationsKey {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the shell verb for this application under the given scope.
|
||||||
|
/// Pass `None` as shell verb to remove the shell verb.
|
||||||
|
///
|
||||||
|
/// # Todo
|
||||||
|
///
|
||||||
|
/// Currently we only support single shell verb.
|
||||||
|
/// So if there is multiple shell verb, all of them will be deleted at first,
|
||||||
|
/// and then write your given shell verb inside it.
|
||||||
|
/// This issue will be resolved in future.
|
||||||
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<()> {
|
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
@@ -195,6 +237,10 @@ impl ApplicationsKey {
|
|||||||
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
|
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
|
||||||
const NAMEOF_DEFAULT_ICON_PART2: &str = "";
|
const NAMEOF_DEFAULT_ICON_PART2: &str = "";
|
||||||
|
|
||||||
|
/// Gets the default icon registered for this application under the given view.
|
||||||
|
/// Return `None` if there is no such set default icon.
|
||||||
|
///
|
||||||
|
/// This icon will be used to represent the application instead of the first icon stored in the .exe file.
|
||||||
pub fn get_default_icon(&self, view: View) -> Result<Option<IconResVariant>> {
|
pub fn get_default_icon(&self, view: View) -> Result<Option<IconResVariant>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get default icon subkey
|
// Get default icon subkey
|
||||||
@@ -213,6 +259,8 @@ impl ApplicationsKey {
|
|||||||
Ok(default_icon_default_value.map(|v| IconResVariant::from(v.as_str())))
|
Ok(default_icon_default_value.map(|v| IconResVariant::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the default icon for this application under the given scope.
|
||||||
|
/// Pass `None` as icon to remove the default icon.
|
||||||
pub fn set_default_icon(&mut self, scope: Scope, icon: Option<&IconResVariant>) -> Result<()> {
|
pub fn set_default_icon(&mut self, scope: Scope, icon: Option<&IconResVariant>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
@@ -235,6 +283,10 @@ impl ApplicationsKey {
|
|||||||
|
|
||||||
const NAMEOF_FRIENDLY_APP_NAME: &str = "FriendlyAppName";
|
const NAMEOF_FRIENDLY_APP_NAME: &str = "FriendlyAppName";
|
||||||
|
|
||||||
|
/// Gets the friendly app name registered for this application under the given view.
|
||||||
|
/// Return `None` if there is no such set friendly app name.
|
||||||
|
///
|
||||||
|
/// This name will be used to display for an application instead of just the version information appearing.
|
||||||
pub fn get_friendly_app_name(&self, view: View) -> Result<Option<StrResVariant>> {
|
pub fn get_friendly_app_name(&self, view: View) -> Result<Option<StrResVariant>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get value of it
|
// Get value of it
|
||||||
@@ -243,6 +295,8 @@ impl ApplicationsKey {
|
|||||||
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the friendly app name for this application under the given scope.
|
||||||
|
/// Pass `None` to remove the friendly app name value.
|
||||||
pub fn set_friendly_app_name(
|
pub fn set_friendly_app_name(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
@@ -266,6 +320,10 @@ impl ApplicationsKey {
|
|||||||
|
|
||||||
const NAMEOF_SUPPORTED_TYPES: &str = "SupportedTypes";
|
const NAMEOF_SUPPORTED_TYPES: &str = "SupportedTypes";
|
||||||
|
|
||||||
|
/// Gets the supported file types registered for this application under the given view.
|
||||||
|
/// Return `None` if application doesn't set this (because this is suggested but not forced).
|
||||||
|
///
|
||||||
|
/// This is a list defining the file types that the application supports.
|
||||||
pub fn get_supported_types(&self, view: View) -> Result<Option<Vec<concept::Ext>>> {
|
pub fn get_supported_types(&self, view: View) -> Result<Option<Vec<concept::Ext>>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get supported types subkey
|
// Get supported types subkey
|
||||||
@@ -285,6 +343,8 @@ impl ApplicationsKey {
|
|||||||
Ok(Some(exts))
|
Ok(Some(exts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the supported file types for this application under the given scope.
|
||||||
|
/// Pass `None` to remove this list.
|
||||||
pub fn set_supported_types(
|
pub fn set_supported_types(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
@@ -316,6 +376,7 @@ impl ApplicationsKey {
|
|||||||
|
|
||||||
const NAMEOF_NO_OPEN_WITH: &str = "NoOpenWith";
|
const NAMEOF_NO_OPEN_WITH: &str = "NoOpenWith";
|
||||||
|
|
||||||
|
/// Checks whether the "NoOpenWith" flag is set for this application under the given view.
|
||||||
pub fn get_no_open_with(&self, view: View) -> Result<bool> {
|
pub fn get_no_open_with(&self, view: View) -> Result<bool> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
match regext::try_get_value::<String, _>(&key, Self::NAMEOF_NO_OPEN_WITH)? {
|
match regext::try_get_value::<String, _>(&key, Self::NAMEOF_NO_OPEN_WITH)? {
|
||||||
@@ -324,6 +385,11 @@ impl ApplicationsKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets or clears the "NoOpenWith" flag for this application under the given scope.
|
||||||
|
///
|
||||||
|
/// Microsoft document said that this key indicates that no application is specified for opening this file type.
|
||||||
|
/// But I totally doesn't understand this. However, most of application set this.
|
||||||
|
/// So let we set it together.
|
||||||
pub fn set_no_open_with(&mut self, scope: Scope, flag: bool) -> Result<()> {
|
pub fn set_no_open_with(&mut self, scope: Scope, flag: bool) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
if flag {
|
if flag {
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ use crate::win32::{concept, regext};
|
|||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
|
/// A registry key wrapper for `Software\Classes\<EXTENSION>`.
|
||||||
|
///
|
||||||
|
/// This struct provides [Self::is_exist], [Self::ensure] and [Self::delete] to
|
||||||
|
/// check whether this key is in Registry, make sure this key is presneted in Registry,
|
||||||
|
/// and delete self from Registry respectively.
|
||||||
|
///
|
||||||
|
/// And there are some getter and setter in this struct,
|
||||||
|
/// before calling them, you must use [Self::ensure] make sure that this key is presented in Registry,
|
||||||
|
/// otherwise these functions will return errors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ExtKey {
|
pub struct ExtKey {
|
||||||
ext: concept::Ext,
|
ext: concept::Ext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtKey {
|
impl ExtKey {
|
||||||
|
/// Creates a new `ExtKey` with the given file extension.
|
||||||
pub fn new(inner: concept::Ext) -> Self {
|
pub fn new(inner: concept::Ext) -> Self {
|
||||||
Self { ext: inner }
|
Self { ext: inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the inner file extension of this extension key.
|
||||||
pub fn inner(&self) -> &concept::Ext {
|
pub fn inner(&self) -> &concept::Ext {
|
||||||
&self.ext
|
&self.ext
|
||||||
}
|
}
|
||||||
@@ -25,6 +36,7 @@ impl ExtKey {
|
|||||||
const FULL_CLASSES: &str = "Software\\Classes";
|
const FULL_CLASSES: &str = "Software\\Classes";
|
||||||
const PARTIAL_CLASSES: &str = "";
|
const PARTIAL_CLASSES: &str = "";
|
||||||
|
|
||||||
|
/// Attempts to open the registry key for this file extension with the given territory and purpose.
|
||||||
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
||||||
// check privilege
|
// check privilege
|
||||||
check_privilege(territory, purpose)?;
|
check_privilege(territory, purpose)?;
|
||||||
@@ -53,14 +65,18 @@ impl ExtKey {
|
|||||||
Ok(OpenedKey::new(classes, this_ext))
|
Ok(OpenedKey::new(classes, this_ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-only access under the given view.
|
||||||
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
||||||
self.open_key(view.into(), OpenKeyPurpose::Read)
|
self.open_key(view.into(), OpenKeyPurpose::Read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-write access under the given scope.
|
||||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
||||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether this file extension key exists in the registry under the given view.
|
||||||
|
/// Return true if it is presented in registry, otherwise false.
|
||||||
pub fn is_exist(&self, view: View) -> Result<bool> {
|
pub fn is_exist(&self, view: View) -> Result<bool> {
|
||||||
let key = self.open_view_for_read(view)?.this_key;
|
let key = self.open_view_for_read(view)?.this_key;
|
||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
@@ -96,20 +112,21 @@ impl ExtKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes
|
// Reference: <https://learn.microsoft.com/en-us/windows/win32/shell/fa-file-types#setting-optional-subkeys-and-file-type-extension-attributes>
|
||||||
|
// And we explicitly do not support "OpenWithList", because it is obsolete in modern Windows.
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// We do not support "Content Type" and "PerceivedType"
|
// We only support these keys in there because current interface are enough to use.
|
||||||
// because current interface are enough to use,
|
|
||||||
// and these types has not been made as concept struct in Rust.
|
|
||||||
// We may expand these in future.
|
// We may expand these in future.
|
||||||
|
|
||||||
|
/// Opens the registry key for getter reading, returning an error if the key does not exist.
|
||||||
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
||||||
self.open_view_for_read(view)?
|
self.open_view_for_read(view)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for setter writing, returning an error if the key does not exist.
|
||||||
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
self.open_scope_for_write(scope)?
|
self.open_scope_for_write(scope)?
|
||||||
.this_key
|
.this_key
|
||||||
@@ -118,6 +135,10 @@ impl ExtKey {
|
|||||||
|
|
||||||
const NAMEOF_DEFAULT: &str = "";
|
const NAMEOF_DEFAULT: &str = "";
|
||||||
|
|
||||||
|
/// Gets the default value of this file extension under the given view.
|
||||||
|
/// Return `None` if there is no association for this file extension.
|
||||||
|
///
|
||||||
|
/// The content of this key is this file extension associated ProgId.
|
||||||
pub fn get_default(&self, view: View) -> Result<Option<LosseProgId>> {
|
pub fn get_default(&self, view: View) -> Result<Option<LosseProgId>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get value of it
|
// Get value of it
|
||||||
@@ -126,6 +147,8 @@ impl ExtKey {
|
|||||||
Ok(value.map(|v| LosseProgId::from(v.as_str())))
|
Ok(value.map(|v| LosseProgId::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the default value (ProgId association) for this file extension under the given scope.
|
||||||
|
/// Pass `None` to remove the association.
|
||||||
pub fn set_default(&mut self, scope: Scope, pid: Option<&LosseProgId>) -> Result<()> {
|
pub fn set_default(&mut self, scope: Scope, pid: Option<&LosseProgId>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
@@ -145,6 +168,11 @@ impl ExtKey {
|
|||||||
|
|
||||||
const NAMEOF_OPEN_WITH_PROGIDS: &str = "OpenWithProgIds";
|
const NAMEOF_OPEN_WITH_PROGIDS: &str = "OpenWithProgIds";
|
||||||
|
|
||||||
|
/// Gets the list of ProgIds registered in the "OpenWithProgIds" subkey under the given view.
|
||||||
|
/// Return `None` is there is no this subkey.
|
||||||
|
///
|
||||||
|
/// This subkey contains a list of alternate ProgId for this file type.
|
||||||
|
/// In Windows Explorer, these ProgId should appear in the Open with menu in right click menu of this file extension.
|
||||||
pub fn get_open_with_progids(&self, view: View) -> Result<Option<Vec<LosseProgId>>> {
|
pub fn get_open_with_progids(&self, view: View) -> Result<Option<Vec<LosseProgId>>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get OpenWithProgIds subkey
|
// Get OpenWithProgIds subkey
|
||||||
@@ -165,7 +193,7 @@ impl ExtKey {
|
|||||||
Ok(Some(progids))
|
Ok(Some(progids))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Checks whether the given ProgId is in the "OpenWithProgIds" subkey under the given view.
|
||||||
///
|
///
|
||||||
/// If there is no "OpenWithProgIds" subkey, this function return false.
|
/// If there is no "OpenWithProgIds" subkey, this function return false.
|
||||||
pub fn is_in_open_with_progids(&self, view: View, pid: &LosseProgId) -> Result<bool> {
|
pub fn is_in_open_with_progids(&self, view: View, pid: &LosseProgId) -> Result<bool> {
|
||||||
@@ -185,7 +213,7 @@ impl ExtKey {
|
|||||||
.is_some())
|
.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Adds the given ProgId into the "OpenWithProgIds" subkey under the given scope.
|
||||||
///
|
///
|
||||||
/// If there is no "OpenWithProgIds" subkey, this function will create it first,
|
/// If there is no "OpenWithProgIds" subkey, this function will create it first,
|
||||||
/// then add your given ProgId into it.
|
/// then add your given ProgId into it.
|
||||||
@@ -199,9 +227,9 @@ impl ExtKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the given ProgId from the "OpenWithProgIds" subkey under the given scope.
|
||||||
///
|
///
|
||||||
///
|
/// If there is no "OpenWithProgIds" subkey, this function does nothing.
|
||||||
/// If there is no "OpenWithProgIds" subkey, this function do nothing.
|
|
||||||
pub fn remove_from_open_with_progids(&mut self, scope: Scope, pid: &LosseProgId) -> Result<()> {
|
pub fn remove_from_open_with_progids(&mut self, scope: Scope, pid: &LosseProgId) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
// Try get subkey
|
// Try get subkey
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ use crate::win32::regext;
|
|||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
use winreg::enums::{HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
|
/// A registry key wrapper for `Software\Classes\<PROGID>`.
|
||||||
|
///
|
||||||
|
/// This struct provides [Self::is_exist], [Self::ensure] and [Self::delete] to
|
||||||
|
/// check whether this key is in Registry, make sure this key is presneted in Registry,
|
||||||
|
/// and delete self from Registry respectively.
|
||||||
|
///
|
||||||
|
/// And there are some getter and setter in this struct,
|
||||||
|
/// before calling them, you must use [Self::ensure] make sure that this key is presented in Registry,
|
||||||
|
/// otherwise these functions will return errors.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ProgIdKey {
|
pub struct ProgIdKey {
|
||||||
progid: LosseProgId,
|
progid: LosseProgId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgIdKey {
|
impl ProgIdKey {
|
||||||
|
/// Creates a new `ProgIdKey` with the given ProgId.
|
||||||
pub fn new(inner: LosseProgId) -> Self {
|
pub fn new(inner: LosseProgId) -> Self {
|
||||||
Self { progid: inner }
|
Self { progid: inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the inner ProgId of this ProgId key.
|
||||||
pub fn inner(&self) -> &LosseProgId {
|
pub fn inner(&self) -> &LosseProgId {
|
||||||
&self.progid
|
&self.progid
|
||||||
}
|
}
|
||||||
@@ -25,6 +36,7 @@ impl ProgIdKey {
|
|||||||
const FULL_CLASSES: &str = "Software\\Classes";
|
const FULL_CLASSES: &str = "Software\\Classes";
|
||||||
const PARTIAL_CLASSES: &str = "";
|
const PARTIAL_CLASSES: &str = "";
|
||||||
|
|
||||||
|
/// Attempts to open the registry key for this ProgId with the given territory and purpose.
|
||||||
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
fn open_key(&self, territory: OpenKeyTerritory, purpose: OpenKeyPurpose) -> Result<OpenedKey> {
|
||||||
// check privilege
|
// check privilege
|
||||||
check_privilege(territory, purpose)?;
|
check_privilege(territory, purpose)?;
|
||||||
@@ -53,14 +65,18 @@ impl ProgIdKey {
|
|||||||
Ok(OpenedKey::new(classes, this_progid))
|
Ok(OpenedKey::new(classes, this_progid))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-only access under the given view.
|
||||||
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
fn open_view_for_read(&self, view: View) -> Result<OpenedKey> {
|
||||||
self.open_key(view.into(), OpenKeyPurpose::Read)
|
self.open_key(view.into(), OpenKeyPurpose::Read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for read-write access under the given scope.
|
||||||
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
fn open_scope_for_write(&self, scope: Scope) -> Result<OpenedKey> {
|
||||||
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
self.open_key(scope.into(), OpenKeyPurpose::ReadWrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether this ProgId key exists in the registry under the given view.
|
||||||
|
/// Return true if it is presented in registry, otherwise false.
|
||||||
pub fn is_exist(&self, view: View) -> Result<bool> {
|
pub fn is_exist(&self, view: View) -> Result<bool> {
|
||||||
let key = self.open_view_for_read(view)?.this_key;
|
let key = self.open_view_for_read(view)?.this_key;
|
||||||
Ok(key.is_some())
|
Ok(key.is_some())
|
||||||
@@ -96,19 +112,20 @@ impl ProgIdKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// YYC MARK:
|
// YYC MARK:
|
||||||
// Reference: https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids#programmatic-identifier-elements-used-by-file-associations
|
// Reference: <https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids#programmatic-identifier-elements-used-by-file-associations>
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// Currently we only support (Default), FriendlyTypeName and DefaultIcon
|
// We only support these keys in there because current interface are enough to use.
|
||||||
// to just cover the basic usage.
|
|
||||||
// We may expand these in future.
|
// We may expand these in future.
|
||||||
|
|
||||||
|
/// Opens the registry key for getter reading, returning an error if the key does not exist.
|
||||||
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
fn open_view_for_getter(&self, view: View) -> Result<RegKey> {
|
||||||
self.open_view_for_read(view)?
|
self.open_view_for_read(view)?
|
||||||
.this_key
|
.this_key
|
||||||
.ok_or(Error::InexistantKey)
|
.ok_or(Error::InexistantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens the registry key for setter writing, returning an error if the key does not exist.
|
||||||
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
fn open_scope_for_setter(&self, scope: Scope) -> Result<RegKey> {
|
||||||
self.open_scope_for_write(scope)?
|
self.open_scope_for_write(scope)?
|
||||||
.this_key
|
.this_key
|
||||||
@@ -117,6 +134,11 @@ impl ProgIdKey {
|
|||||||
|
|
||||||
const NAMEOF_DEFAULT: &str = "";
|
const NAMEOF_DEFAULT: &str = "";
|
||||||
|
|
||||||
|
/// Gets the default value of this ProgId under the given view.
|
||||||
|
///
|
||||||
|
/// The content of this key is the legacy way to introduce the friendly name of application.
|
||||||
|
/// If you are fetching friendly name, please choose "FriendlyTypeName" at first, then fallback to this.
|
||||||
|
/// If you are setting friendly name, please set this together.
|
||||||
pub fn get_default(&self, view: View) -> Result<Option<StrResVariant>> {
|
pub fn get_default(&self, view: View) -> Result<Option<StrResVariant>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get value of it
|
// Get value of it
|
||||||
@@ -125,9 +147,7 @@ impl ProgIdKey {
|
|||||||
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Sets the default value (friendly name) for this ProgId under the given scope.
|
||||||
///
|
|
||||||
/// The legacy way to set friendly name for this ProgId.
|
|
||||||
pub fn set_default(&mut self, scope: Scope, name: Option<&StrResVariant>) -> Result<()> {
|
pub fn set_default(&mut self, scope: Scope, name: Option<&StrResVariant>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
@@ -145,10 +165,21 @@ impl ProgIdKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Support mutiple ShellVerb in future.
|
||||||
|
|
||||||
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
|
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
|
||||||
const NAMEOF_SHELL_VERB_PART3: &str = "command";
|
const NAMEOF_SHELL_VERB_PART3: &str = "command";
|
||||||
const NAMEOF_SHELL_VERB_PART4: &str = "";
|
const NAMEOF_SHELL_VERB_PART4: &str = "";
|
||||||
|
|
||||||
|
/// Gets the shell verb registered for this ProgId under the given view.
|
||||||
|
/// Return `None` if there is no any shell verb.
|
||||||
|
///
|
||||||
|
/// # Todo
|
||||||
|
///
|
||||||
|
/// Currently we only support single shell verb.
|
||||||
|
/// So if there is multiple shell verb, this function still return `None`.
|
||||||
|
/// This issue will be resolved in future.
|
||||||
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>> {
|
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
|
|
||||||
@@ -199,6 +230,15 @@ impl ProgIdKey {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the shell verb for this ProgId under the given scope.
|
||||||
|
/// Pass `None` as shell verb to remove the shell verb.
|
||||||
|
///
|
||||||
|
/// # Todo
|
||||||
|
///
|
||||||
|
/// Currently we only support single shell verb.
|
||||||
|
/// So if there is multiple shell verb, all of them will be deleted at first,
|
||||||
|
/// and then write your given shell verb inside it.
|
||||||
|
/// This issue will be resolved in future.
|
||||||
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<()> {
|
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
@@ -225,8 +265,17 @@ impl ProgIdKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// This key may be REG_SZ or REG_EXPAND_SZ.
|
||||||
|
// Currently we see them as one type.
|
||||||
|
// This should be improved in future.
|
||||||
|
|
||||||
const NAMEOF_FRIENDLY_TYPE_NAME: &str = "FriendlyTypeName";
|
const NAMEOF_FRIENDLY_TYPE_NAME: &str = "FriendlyTypeName";
|
||||||
|
|
||||||
|
/// Gets the friendly type name registered for this ProgId under the given view.
|
||||||
|
/// Return `None` if there is no such key.
|
||||||
|
///
|
||||||
|
/// The content of this key is the file type name which is suitable to display to the user.
|
||||||
pub fn get_friendly_type_name(&self, view: View) -> Result<Option<StrResVariant>> {
|
pub fn get_friendly_type_name(&self, view: View) -> Result<Option<StrResVariant>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get value of it
|
// Get value of it
|
||||||
@@ -235,9 +284,8 @@ impl ProgIdKey {
|
|||||||
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
Ok(value.map(|v| StrResVariant::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Sets the friendly type name for this ProgId under the given scope.
|
||||||
///
|
/// Pass `None` to remove friendly type name.
|
||||||
/// Set this entry to a friendly name for the ProgID.
|
|
||||||
pub fn set_friendly_type_name(
|
pub fn set_friendly_type_name(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
@@ -259,9 +307,18 @@ impl ProgIdKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// This key may be REG_SZ or REG_EXPAND_SZ.
|
||||||
|
// Currently we see them as one type.
|
||||||
|
// This should be improved in future.
|
||||||
|
|
||||||
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
|
const NAMEOF_DEFAULT_ICON_PART1: &str = "DefaultIcon";
|
||||||
const NAMEOF_DEFAULT_ICON_PART2: &str = "";
|
const NAMEOF_DEFAULT_ICON_PART2: &str = "";
|
||||||
|
|
||||||
|
/// Gets the default icon registered for this ProgId under the given view.
|
||||||
|
/// Return `None` if there is no default icon.
|
||||||
|
///
|
||||||
|
/// The content of this key will be displayed for file types associated with this ProgId.
|
||||||
pub fn get_default_icon(&self, view: View) -> Result<Option<IconResVariant>> {
|
pub fn get_default_icon(&self, view: View) -> Result<Option<IconResVariant>> {
|
||||||
let key = self.open_view_for_getter(view)?;
|
let key = self.open_view_for_getter(view)?;
|
||||||
// Get default icon subkey
|
// Get default icon subkey
|
||||||
@@ -280,6 +337,11 @@ impl ProgIdKey {
|
|||||||
Ok(default_icon_default_value.map(|v| IconResVariant::from(v.as_str())))
|
Ok(default_icon_default_value.map(|v| IconResVariant::from(v.as_str())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the default icon for this ProgId under the given scope.
|
||||||
|
/// Pass `None` to remove the default icon subkey.
|
||||||
|
///
|
||||||
|
/// Although this function give the ability that do not set this key,
|
||||||
|
/// however, I strongly suggest that set this to let file associated with this ProgId have an icon.
|
||||||
pub fn set_default_icon(&mut self, scope: Scope, icon: Option<&IconResVariant>) -> Result<()> {
|
pub fn set_default_icon(&mut self, scope: Scope, icon: Option<&IconResVariant>) -> Result<()> {
|
||||||
let key = self.open_scope_for_setter(scope)?;
|
let key = self.open_scope_for_setter(scope)?;
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ use std::iter::FusedIterator;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use thiserror::Error as TeError;
|
use thiserror::Error as TeError;
|
||||||
|
|
||||||
// region: OS String Related
|
// region: OS String Casting
|
||||||
|
|
||||||
/// The error occurs when casting `OsStr` into `str`.
|
/// The error occurs when casting [OsStr] into [str].
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
#[error("fail to cast OS string into Rust string")]
|
#[error("fail to cast OS string into Rust string")]
|
||||||
pub struct CastOsStrError {}
|
pub struct CastOsStrError {}
|
||||||
@@ -32,6 +32,8 @@ pub fn osstr_to_str(osstr: &OsStr) -> Result<&str, CastOsStrError> {
|
|||||||
|
|
||||||
// region: Capitalize First ASCII Letter
|
// region: Capitalize First ASCII Letter
|
||||||
|
|
||||||
|
/// The struct transforming accept an iterator and perform as an iterator
|
||||||
|
/// whose transform the first character of accepted iterator into capital form (ASCII char only).
|
||||||
struct CapitalizeFirstAscii<T>
|
struct CapitalizeFirstAscii<T>
|
||||||
where
|
where
|
||||||
T: Iterator<Item = char>,
|
T: Iterator<Item = char>,
|
||||||
@@ -72,6 +74,7 @@ where
|
|||||||
|
|
||||||
impl<T> FusedIterator for CapitalizeFirstAscii<T> where T: Iterator<Item = char> + FusedIterator {}
|
impl<T> FusedIterator for CapitalizeFirstAscii<T> where T: Iterator<Item = char> + FusedIterator {}
|
||||||
|
|
||||||
|
/// Accept an string slice, transform its first char (ASCII only) as capital form and output.
|
||||||
pub fn capitalize_first_ascii(s: &str) -> String {
|
pub fn capitalize_first_ascii(s: &str) -> String {
|
||||||
CapitalizeFirstAscii::new(s.chars()).collect()
|
CapitalizeFirstAscii::new(s.chars()).collect()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//! The module containing Win32 stuff related to file association operation,
|
||||||
|
//! including Win32 concept, Registry operation helper and etc.
|
||||||
|
|
||||||
pub mod concept;
|
pub mod concept;
|
||||||
pub mod utilities;
|
pub mod utilities;
|
||||||
pub mod regext;
|
pub mod regext;
|
||||||
@@ -154,8 +154,8 @@ impl BadProgIdPartError {
|
|||||||
/// outputs legal ProgId in string form.
|
/// outputs legal ProgId in string form.
|
||||||
///
|
///
|
||||||
/// Reference:
|
/// Reference:
|
||||||
/// - https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids
|
/// - <https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids>
|
||||||
/// - https://learn.microsoft.com/en-us/windows/win32/com/-progid--key
|
/// - <https://learn.microsoft.com/en-us/windows/win32/com/-progid--key>
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ProgId {
|
pub struct ProgId {
|
||||||
/// The vendor part of ProgId.
|
/// The vendor part of ProgId.
|
||||||
@@ -168,6 +168,9 @@ pub struct ProgId {
|
|||||||
|
|
||||||
impl ProgId {
|
impl ProgId {
|
||||||
/// Create a new ProgId with given parts.
|
/// Create a new ProgId with given parts.
|
||||||
|
///
|
||||||
|
/// Empty string or string with embedded dot is not allowed
|
||||||
|
/// for vendor and component parts.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
vendor: &str,
|
vendor: &str,
|
||||||
component: &str,
|
component: &str,
|
||||||
@@ -279,7 +282,7 @@ pub struct Clsid {
|
|||||||
|
|
||||||
impl Clsid {
|
impl Clsid {
|
||||||
/// Create new CLSID from underlying UUID.
|
/// Create new CLSID from underlying UUID.
|
||||||
fn new(uuid: &Uuid) -> Self {
|
pub fn new(uuid: &Uuid) -> Self {
|
||||||
Self { inner: *uuid }
|
Self { inner: *uuid }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -897,6 +900,7 @@ impl FromStr for ExpandString {
|
|||||||
|
|
||||||
// region: File Name
|
// region: File Name
|
||||||
|
|
||||||
|
/// The error occurs when constructing FileName with bad file name.
|
||||||
pub type BadFileNameError = ParseFileNameError;
|
pub type BadFileNameError = ParseFileNameError;
|
||||||
|
|
||||||
/// The struct representing a legal Windows file name.
|
/// The struct representing a legal Windows file name.
|
||||||
@@ -928,7 +932,7 @@ impl FileName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error occurs when constructing FileName with bad file name.
|
/// The error occurs when parsing FileName with bad file name.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
#[error("given file name is illegal in Windows")]
|
#[error("given file name is illegal in Windows")]
|
||||||
pub enum ParseFileNameError {
|
pub enum ParseFileNameError {
|
||||||
@@ -975,6 +979,7 @@ impl FromStr for FileName {
|
|||||||
|
|
||||||
// region: Verb
|
// region: Verb
|
||||||
|
|
||||||
|
/// The error occurs when constructing Verb with bad verb name.
|
||||||
pub type BadVerbError = ParseVerbError;
|
pub type BadVerbError = ParseVerbError;
|
||||||
|
|
||||||
/// The struct representing a verb when manipulating file
|
/// The struct representing a verb when manipulating file
|
||||||
@@ -1024,7 +1029,7 @@ impl Verb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error occurs when constructing Verb with bad verb name.
|
/// The error occurs when parsing Verb with bad verb name.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
#[error("given verb \"{inner}\" is illegal")]
|
#[error("given verb \"{inner}\" is illegal")]
|
||||||
pub struct ParseVerbError {
|
pub struct ParseVerbError {
|
||||||
@@ -1071,11 +1076,21 @@ impl FromStr for Verb {
|
|||||||
|
|
||||||
/// The error occurs when constructing CmdLine with bad arguments.
|
/// The error occurs when constructing CmdLine with bad arguments.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
#[error("given file extension body \"{inner}\" is invalid")]
|
#[error("given command line argument \"{inner}\" is invalid")]
|
||||||
pub struct BadCmdLineError {
|
pub struct BadCmdLineError {
|
||||||
inner: String,
|
inner: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BadCmdLineError {
|
||||||
|
// TODO: Remove this dead_code attribute once we need use this error type.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn new(inner: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: inner.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The struct representing a Windows command line.
|
/// The struct representing a Windows command line.
|
||||||
///
|
///
|
||||||
/// If you have a complete Windows command line expressed in string form,
|
/// If you have a complete Windows command line expressed in string form,
|
||||||
@@ -1145,7 +1160,7 @@ impl CmdLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The error occurs when constructing CmdLine with bad syntax.
|
/// The error occurs when parsing CmdLine with bad syntax.
|
||||||
#[derive(Debug, TeError)]
|
#[derive(Debug, TeError)]
|
||||||
#[error("given command line \"{inner}\" is invalid")]
|
#[error("given command line \"{inner}\" is invalid")]
|
||||||
pub struct ParseCmdLineError {
|
pub struct ParseCmdLineError {
|
||||||
@@ -1153,9 +1168,7 @@ pub struct ParseCmdLineError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ParseCmdLineError {
|
impl ParseCmdLineError {
|
||||||
|
|
||||||
// TODO: Remove this dead_code attribute once we need use this error type.
|
// TODO: Remove this dead_code attribute once we need use this error type.
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn new(inner: &str) -> Self {
|
fn new(inner: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
/// It usually means that checking whether current process is running as Administrator.
|
/// It usually means that checking whether current process is running as Administrator.
|
||||||
/// Return true if it is, otherwise false.
|
/// Return true if it is, otherwise false.
|
||||||
///
|
///
|
||||||
/// Reference: https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership
|
/// Reference: <https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership>
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user