1
0

feat: finish highlevel register function

This commit is contained in:
2026-05-08 10:41:33 +08:00
parent 94f868ebda
commit 8f762928db
3 changed files with 152 additions and 81 deletions

View File

@@ -1,4 +1,4 @@
use crate::{lowlevel, utilities, win32::concept};
use crate::{lowlevel, utilities, win32::{self, concept}};
use regex::Regex;
use std::collections::HashMap;
use std::ffi::OsStr;
@@ -45,7 +45,31 @@ pub enum ParseProgramError {
/// Error occurs when operating with [Program].
#[derive(Debug, TeError)]
pub enum ProgramError {}
pub enum ProgramError {
#[error("{0}")]
Lowlevel(#[from] lowlevel::Error),
}
// endregion
// region: Utilities
/// The println macro only works on Debug mode
/// for tracing the execution of some important functions.
macro_rules! debug_println {
// For no argument.
() => {
if cfg!(debug_assertions) {
eprintln!();
}
};
// For one or more arguments like println!.
($($arg:tt)*) => {
if cfg!(debug_assertions) {
eprintln!($($arg)*);
}
};
}
// endregion
@@ -203,8 +227,8 @@ impl SchemaExt {
pub struct Program {
app_paths_key: lowlevel::AppPathsKey,
applications_key: lowlevel::ApplicationsKey,
file_name: String,
dir_name: String,
app_path: String,
app_dir_path: String,
name: Option<Rc<ProgramStr>>,
icon: Option<Rc<ProgramIcon>>,
behavior: Option<Rc<ProgramBehavior>>,
@@ -213,8 +237,7 @@ pub struct Program {
icons: Vec<Rc<ProgramIcon>>,
behaviors: Vec<Rc<ProgramBehavior>>,
progid_keys: Vec<Rc<ProgramProgIdKey>>,
ext_keys: Vec<ProgramExtKey>,
ext_keys: Vec<ProgramProgIdExtKey>,
}
impl TryFrom<Schema> for Program {
@@ -236,7 +259,7 @@ impl Program {
/// Extract the start in path from full path to application,
/// which basically is the stem of full path.
fn extract_dir_name(full_path: &Path) -> Result<&OsStr, ParseProgramError> {
fn extract_dir_path(full_path: &Path) -> Result<&OsStr, ParseProgramError> {
full_path
.parent()
.map(|p| p.as_os_str())
@@ -306,12 +329,13 @@ impl Program {
pub fn new(schema: Schema) -> Result<Self, ParseProgramError> {
// Extract file name part and directory name part respectively.
let schema_path = Path::new(&schema.path);
let file_name = Self::extract_file_name(schema_path)?;
let file_name = String::from(utilities::osstr_to_str(file_name)?);
let dir_name = Self::extract_dir_name(schema_path)?;
let dir_name = String::from(utilities::osstr_to_str(dir_name)?);
let app_path = schema.path.clone();
let app_file_name = Self::extract_file_name(schema_path)?;
let app_file_name = String::from(utilities::osstr_to_str(app_file_name)?);
let app_dir_path = Self::extract_dir_path(schema_path)?;
let app_dir_path = String::from(utilities::osstr_to_str(app_dir_path)?);
// Build app paths key and applications key respectively
let key = concept::FileName::new(&file_name)?;
let key = concept::FileName::new(&app_file_name)?;
let app_paths_key = lowlevel::AppPathsKey::new(key.clone());
let applications_key = lowlevel::ApplicationsKey::new(key.clone());
@@ -355,8 +379,7 @@ impl Program {
.transpose()?;
// We build ProgIdKey and ExtKey list at the same time
let mut progid_keys: Vec<Rc<ProgramProgIdKey>> = Vec::with_capacity(schema.exts.len());
let mut ext_keys: Vec<ProgramExtKey> = Vec::with_capacity(schema.exts.len());
let mut ext_keys: Vec<ProgramProgIdExtKey> = Vec::with_capacity(schema.exts.len());
for (key, value) in &schema.exts {
// Build ProgId first.
let progid = Self::build_progid(&schema.identifier, key.as_str())?;
@@ -368,44 +391,35 @@ impl Program {
let icon = Self::resolve_index(&value.icon, &icons, &icons_index_map)?;
let behavior = Self::resolve_index(&value.behavior, &behaviors, &behaviors_index_map)?;
// Create program ProgId key struct
let progid_key = ProgramProgIdKey {
key: progid_key,
name,
icon,
behavior,
};
// Create Rc style
let progid_key = Rc::new(progid_key);
// Build Ext.
let ext = concept::Ext::new(key.as_str())?;
let ext_key = lowlevel::ExtKey::new(ext);
// Create program Ext key struct
let ext_key = ProgramExtKey {
key: ext_key,
assoc: progid_key.clone(),
let progid_ext_key = ProgramProgIdExtKey {
ext_key,
progid_key,
name,
icon,
behavior,
};
// Add them into list
progid_keys.push(progid_key);
ext_keys.push(ext_key);
ext_keys.push(progid_ext_key);
}
// Everything is okey
Ok(Self {
app_paths_key,
applications_key,
file_name,
dir_name,
app_path,
app_dir_path,
name,
icon,
behavior,
strs,
icons,
behaviors,
progid_keys,
ext_keys,
})
}
@@ -414,16 +428,62 @@ impl Program {
impl Program {
/// Register this application.
///
/// If there is registration of this application,
/// this function will return error.
/// If there is complete or partial registration of this application
/// (partial registration may occurs when registration failed),
/// this function does nothing.
pub fn register(&mut self, scope: Scope) -> Result<(), ProgramError> {
todo!()
// Create App Paths subkey
debug_println!("Adding App Paths subkey...");
self.app_paths_key.ensure(scope)?;
// Write App Paths values
self.app_paths_key.set_default(scope, &self.app_path)?;
self.app_paths_key.set_path(scope, &self.app_dir_path)?;
// Create Applications subkey
debug_println!("Adding Applications subkey...");
self.applications_key.ensure(scope)?;
// Write Applications values
self.applications_key
.set_shell_verb(scope, self.behavior.as_ref().map(|beh| &beh.inner))?;
self.applications_key
.set_default_icon(scope, self.icon.as_ref().map(|ico| &ico.inner))?;
self.applications_key
.set_friendly_app_name(scope, self.name.as_ref().map(|name| &name.inner))?;
let exts: Vec<&concept::Ext> = self
.ext_keys
.iter()
.map(|key| key.ext_key.inner())
.collect();
self.applications_key
.set_supported_types(scope, Some(&exts))?;
self.applications_key.set_no_open_with(scope, true)?;
// Create ProgId subkeys one by one
debug_println!("Adding ProgId subkey...");
for program_progid_key in &mut self.ext_keys {
let progid_key = &mut program_progid_key.progid_key;
// Create ProgId subkey
debug_println!("Adding ProgId \"{0}\" subkey...", progid_key.inner().to_string());
progid_key.ensure(scope)?;
// Write ProgId values
let name = Some(&program_progid_key.name.inner);
progid_key.set_default(scope, name)?;
progid_key.set_shell_verb(scope, &program_progid_key.behavior.inner)?;
progid_key.set_friendly_type_name(scope, name)?;
progid_key.set_default_icon(scope, Some(&program_progid_key.icon.inner))?;
}
// Everything is okey.
// Notify changes and return
win32::utilities::notify_assoc_changed();
Ok(())
}
/// Unregister this application.
///
/// If there is no registration of this application,
/// this function will return error.
/// No matter whether there is registration of this application,
/// this function always make sure that there is no registration after running this function.
pub fn unregister(&mut self, scope: Scope) -> Result<(), ProgramError> {
todo!()
}
@@ -472,22 +532,20 @@ struct ProgramBehavior {
inner: lowlevel::ShellVerb,
}
/// Internal used struct presenting a Program ProgId.
/// Internal used struct presenting a Program ProgId and associated file extension.
///
/// Another reason combine ProgId and Ext is that we can't operate ProgId in mutable mode,
/// due to the internal immutable of Rc in Rust.
#[derive(Debug)]
struct ProgramProgIdKey {
key: lowlevel::ProgIdKey,
struct ProgramProgIdExtKey {
ext_key: lowlevel::ExtKey,
progid_key: lowlevel::ProgIdKey,
name: Rc<ProgramStr>,
icon: Rc<ProgramIcon>,
behavior: Rc<ProgramBehavior>,
}
/// Internal used struct presenting a Program file extension.
#[derive(Debug)]
struct ProgramExtKey {
key: lowlevel::ExtKey,
assoc: Rc<ProgramProgIdKey>,
}
// endregion
// endregion

View File

@@ -603,11 +603,11 @@ impl ApplicationsKey {
const NAMEOF_SHELL_VERB_PART1: &str = "shell";
const NAMEOF_SHELL_VERB_PART3: &str = "command";
pub fn get_shell_verb(&self, view: View) -> Result<ShellVerb, Error> {
pub fn get_shell_verb(&self, view: View) -> Result<Option<ShellVerb>, Error> {
todo!()
}
pub fn set_shell_verb(&mut self, scope: Scope, sv: &ShellVerb) -> Result<(), Error> {
pub fn set_shell_verb(&mut self, scope: Scope, sv: Option<&ShellVerb>) -> Result<(), Error> {
todo!()
}
@@ -618,7 +618,7 @@ impl ApplicationsKey {
}
pub fn set_default_icon(
&self,
&mut self,
scope: Scope,
icon: Option<&IconResVariant>,
) -> Result<(), Error> {
@@ -632,7 +632,7 @@ impl ApplicationsKey {
}
pub fn set_friendly_app_name(
&self,
&mut self,
scope: Scope,
name: Option<&StrResVariant>,
) -> Result<(), Error> {
@@ -646,10 +646,11 @@ impl ApplicationsKey {
}
pub fn set_supported_types(
&self,
&mut self,
scope: Scope,
tys: Option<&[concept::Ext]>,
tys: Option<&[&concept::Ext]>,
) -> Result<(), Error> {
// TODO: If given slice is empty, do not create this key.
todo!()
}
@@ -659,7 +660,7 @@ impl ApplicationsKey {
todo!()
}
pub fn set_no_open_with(&self, scope: Scope, flag: bool) -> Result<(), Error> {
pub fn set_no_open_with(&mut self, scope: Scope, flag: bool) -> Result<(), Error> {
todo!()
}
}
@@ -722,6 +723,18 @@ impl ExtKey {
Ok(OpenedKey::new(classes, this_ext))
}
pub fn is_exist(&self, view: View) -> Result<bool, Error> {
todo!()
}
pub fn ensure(&mut self, scope: Scope) -> Result<bool, Error> {
todo!()
}
pub fn delete(&mut self, scope: Scope) -> Result<(), Error> {
todo!()
}
// YYC MARK:
// We do not support "Content Type" and "PerceivedType"
// because current interface are enough to use,
@@ -795,6 +808,18 @@ impl ProgIdKey {
todo!()
}
pub fn is_exist(&self, view: View) -> Result<bool, Error> {
todo!()
}
pub fn ensure(&mut self, scope: Scope) -> Result<bool, Error> {
todo!()
}
pub fn delete(&mut self, scope: Scope) -> Result<(), Error> {
todo!()
}
// YYC MARK:
// Currently we only support (Default), FriendlyTypeName and DefaultIcon
// to just cover the basic usage.
@@ -805,6 +830,9 @@ impl ProgIdKey {
todo!()
}
///
///
/// The legacy way to set friendly name for this ProgId.
pub fn set_default(&mut self, scope: Scope, name: Option<&StrResVariant>) -> Result<(), Error> {
todo!()
}
@@ -826,8 +854,11 @@ impl ProgIdKey {
todo!()
}
///
///
/// Set this entry to a friendly name for the ProgID.
pub fn set_friendly_type_name(
&self,
&mut self,
scope: Scope,
name: Option<&StrResVariant>,
) -> Result<(), Error> {
@@ -841,7 +872,7 @@ impl ProgIdKey {
}
pub fn set_default_icon(
&self,
&mut self,
scope: Scope,
icon: Option<&IconResVariant>,
) -> Result<(), Error> {

View File

@@ -5,24 +5,6 @@ use std::iter::FusedIterator;
use std::path::Path;
use thiserror::Error as TeError;
/// The println macro only works on Debug mode
/// for tracing the execution of some important functions.
#[macro_export]
macro_rules! debug_println {
// For no argument.
() => {
if cfg!(debug_assertions) {
eprintln!();
}
};
// For one or more arguments like println!.
($($arg:tt)*) => {
if cfg!(debug_assertions) {
eprintln!($($arg)*);
}
};
}
// region: OS String Related
/// The error occurs when casting `OsStr` into `str`.