1
0

feat: add arbitrarily_delete_subkey_all for regext

This commit is contained in:
2026-05-29 08:58:15 +08:00
parent 119a4d0341
commit f0bd2c0b73
6 changed files with 87 additions and 31 deletions

View File

@@ -62,7 +62,7 @@ impl AppPathsKey {
Ok(key.is_some()) Ok(key.is_some())
} }
/// Ensure this application key is presented in App Paths. /// Ensure this application key is presented in App Paths key.
/// ///
/// Return true if we newly create this key, /// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key. /// otherwise false indicating there already is an existing key.
@@ -79,15 +79,16 @@ impl AppPathsKey {
} }
} }
/// Delete this application key from App Paths. /// Delete this application key from App Paths key.
/// ///
/// If there is no such key in App Paths, /// Return true if we successfully delete this key,
/// this function does nothing. /// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<()> { pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
key.parent_key Ok(regext::arbitrarily_delete_subkey_all(
.delete_subkey_all(regext::blank_path_guard(self.key_name.inner())?)?; &key.parent_key,
Ok(()) regext::blank_path_guard(self.key_name.inner())?,
)?)
} }
fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> { fn open_scope_for_getter(&self, scope: Scope) -> Result<RegKey> {

View File

@@ -68,6 +68,10 @@ impl ApplicationsKey {
Ok(key.is_some()) Ok(key.is_some())
} }
/// Ensure this application key is presented in Applications key.
///
/// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key.
pub fn ensure(&mut self, scope: Scope) -> Result<bool> { pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
if let None = key.this_key { if let None = key.this_key {
@@ -81,11 +85,16 @@ impl ApplicationsKey {
} }
} }
pub fn delete(&mut self, scope: Scope) -> Result<()> { /// Delete this application key from Applications key.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
key.parent_key Ok(regext::arbitrarily_delete_subkey_all(
.delete_subkey_all(regext::blank_path_guard(self.key_name.inner())?)?; &key.parent_key,
Ok(()) regext::blank_path_guard(self.key_name.inner())?,
)?)
} }
// YYC MARK: // YYC MARK:
@@ -176,7 +185,7 @@ impl ApplicationsKey {
} }
None => { None => {
// Delete shell and its all subkey. // Delete shell and its all subkey.
key.delete_subkey_all(Self::NAMEOF_SHELL_VERB_PART1)?; regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SHELL_VERB_PART1)?;
} }
} }
@@ -217,7 +226,7 @@ impl ApplicationsKey {
} }
None => { None => {
// Delete shell and its all subkey. // Delete shell and its all subkey.
key.delete_subkey_all(Self::NAMEOF_DEFAULT_ICON_PART1)?; regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_DEFAULT_ICON_PART1)?;
} }
} }
@@ -298,7 +307,7 @@ impl ApplicationsKey {
} }
None => { None => {
// Delete this subkey. // Delete this subkey.
key.delete_subkey_all(Self::NAMEOF_SUPPORTED_TYPES)?; regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SUPPORTED_TYPES)?;
} }
} }

View File

@@ -66,6 +66,10 @@ impl ExtKey {
Ok(key.is_some()) Ok(key.is_some())
} }
/// Ensure this file extension key is presented in Classes key.
///
/// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key.
pub fn ensure(&mut self, scope: Scope) -> Result<bool> { pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
if let None = key.this_key { if let None = key.this_key {
@@ -79,16 +83,21 @@ impl ExtKey {
} }
} }
pub fn delete(&mut self, scope: Scope) -> Result<()> { /// Delete this file extension key from Classes key.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
key.parent_key Ok(regext::arbitrarily_delete_subkey_all(
.delete_subkey_all(regext::blank_path_guard(self.ext.dotted_inner())?)?; &key.parent_key,
Ok(()) regext::blank_path_guard(self.ext.dotted_inner())?,
)?)
} }
// 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
// TODO: // TODO:
// We do not support "Content Type" and "PerceivedType" // We do not support "Content Type" and "PerceivedType"
// because current interface are enough to use, // because current interface are enough to use,

View File

@@ -66,6 +66,10 @@ impl ProgIdKey {
Ok(key.is_some()) Ok(key.is_some())
} }
/// Ensure this ProgId key is presented in Classes key.
///
/// Return true if we newly create this key,
/// otherwise false indicating there already is an existing key.
pub fn ensure(&mut self, scope: Scope) -> Result<bool> { pub fn ensure(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
if let None = key.this_key { if let None = key.this_key {
@@ -79,18 +83,23 @@ impl ProgIdKey {
} }
} }
pub fn delete(&mut self, scope: Scope) -> Result<()> { /// Delete this ProgId key from Classes key.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn delete(&mut self, scope: Scope) -> Result<bool> {
let key = self.open_scope_for_write(scope)?; let key = self.open_scope_for_write(scope)?;
key.parent_key Ok(regext::arbitrarily_delete_subkey_all(
.delete_subkey_all(regext::blank_path_guard(self.progid.to_string())?)?; &key.parent_key,
Ok(()) regext::blank_path_guard(self.progid.to_string())?,
)?)
} }
// 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 // Currently we only support (Default), FriendlyTypeName and DefaultIcon
// to just cover the basic usage. // to just cover the basic usage.
// We may expand these in future. // We may expand these in future.
@@ -209,7 +218,7 @@ impl ProgIdKey {
} }
None => { None => {
// Delete shell and its all subkey. // Delete shell and its all subkey.
key.delete_subkey_all(Self::NAMEOF_SHELL_VERB_PART1)?; regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_SHELL_VERB_PART1)?;
} }
} }
@@ -284,7 +293,7 @@ impl ProgIdKey {
} }
None => { None => {
// Delete shell and its all subkey. // Delete shell and its all subkey.
key.delete_subkey_all(Self::NAMEOF_DEFAULT_ICON_PART1)?; regext::arbitrarily_delete_subkey_all(&key, Self::NAMEOF_DEFAULT_ICON_PART1)?;
} }
} }

View File

@@ -61,12 +61,36 @@ pub fn try_get_value<T: FromRegValue, N: AsRef<OsStr>>(
} }
} }
/// Delete all tree of given path of given key anyway.
///
/// This function was invented to fix the shortcoming of [RegKey::delete_subkey_all].
/// This function always delete given path of given key no matter it is existing.
/// Oppositely, [RegKey::delete_subkey_all] will return error if there is no such path.
///
/// Return true if we successfully delete this key,
/// otherwise false indicating there is no such key (already deleted).
pub fn arbitrarily_delete_subkey_all<P: AsRef<OsStr>>(
regkey: &RegKey,
path: P,
) -> std::io::Result<bool> {
match regkey.delete_subkey_all(path) {
Ok(()) => Ok(true),
Err(e) => match e.raw_os_error() {
Some(errno) => match errno as u32 {
ERROR_FILE_NOT_FOUND => Ok(false),
_ => Err(e),
},
_ => Err(e),
},
}
}
/// Get the name of only subkey in given key. /// Get the name of only subkey in given key.
/// ///
/// If there is only one subkey in given key, the return value is its name. /// If there is only one subkey in given key, the return value is its name.
/// If there is no any subkey, or has multiple subkeys, return None instead. /// If there is no any subkey, or has multiple subkeys, return None instead.
/// If error occurs when fetching data, return Err(_). /// If error occurs when fetching data, return Err(_).
/// ///
/// This is usually used for ShellVerb fetching. /// This is usually used for ShellVerb fetching.
pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>> { pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>> {
let mut subkey_enumerator = regkey.enum_keys(); let mut subkey_enumerator = regkey.enum_keys();
@@ -85,7 +109,7 @@ pub fn get_sole_subkey_name(regkey: &RegKey) -> std::io::Result<Option<String>>
} }
/// Get the name list of all "string" subkeys in given key. /// Get the name list of all "string" subkeys in given key.
/// ///
/// This is usually used for "OpenWithProgIds" subkey. /// This is usually used for "OpenWithProgIds" subkey.
pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<String>> { pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<String>> {
regkey regkey
@@ -105,11 +129,14 @@ pub fn get_all_string_subkey_names(regkey: &RegKey) -> std::io::Result<Vec<Strin
} }
/// Delete all contents, including values and subkeys of given key. /// Delete all contents, including values and subkeys of given key.
/// ///
/// Deleting all contents of given key rely on giving a special parameter to [RegKey::delete_subkey_all]. /// Deleting all contents of given key rely on giving a special parameter to [RegKey::delete_subkey_all].
/// This is very dangerous and may be used by accident. /// This is very dangerous and may be used by accident.
/// So I create this to explicitly indicate this behavior and avoid any mis-type in code. /// So I create this to explicitly indicate this behavior and avoid any mis-type in code.
pub fn clean_all_contents(regkey: &RegKey) -> std::io::Result<()> { pub fn clean_all_contents(regkey: &RegKey) -> std::io::Result<()> {
// There is no possibility that this key do not existing,
// because what we are cleaning is self content.
// So directly use delete_subkey_all is okey.
regkey.delete_subkey_all("") regkey.delete_subkey_all("")
} }
@@ -135,7 +162,7 @@ impl BlankPathError {
/// Because it will cause unexpected behavior that returning key self, rather than subkey. /// Because it will cause unexpected behavior that returning key self, rather than subkey.
/// This is VERY dangerous especially for those registry delete functions. /// This is VERY dangerous especially for those registry delete functions.
/// So I create this function to prevent any harmful blank path was passed into registry function. /// So I create this function to prevent any harmful blank path was passed into registry function.
/// ///
/// This function MUST be used for the value, whose content can not be confirmed at compile time, /// This function MUST be used for the value, whose content can not be confirmed at compile time,
/// and it will be passed to get/set value, or create/delete key functions. /// and it will be passed to get/set value, or create/delete key functions.
pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankPathError> { pub fn blank_path_guard<P: AsRef<OsStr>>(path: P) -> std::result::Result<P, BlankPathError> {

View File

@@ -31,6 +31,7 @@ fn test_app_paths_key() {
// delete and ensure // delete and ensure
let rv = key.delete(scope); let rv = key.delete(scope);
eprintln!("{rv:?}");
assert!(rv.is_ok()); assert!(rv.is_ok());
let rv = key.is_exist(scope); let rv = key.is_exist(scope);
assert!(rv.is_ok()); assert!(rv.is_ok());