feat: finish highlevel register function
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -54,10 +78,10 @@ pub enum ProgramError {}
|
||||
// region: Schema Body
|
||||
|
||||
/// The sketchpad of complete [Program].
|
||||
///
|
||||
/// In suggested usage, we will create a [Schema] first,
|
||||
///
|
||||
/// In suggested usage, we will create a [Schema] first,
|
||||
/// fill some essential and optional properties,
|
||||
/// then add file extensions which we need.
|
||||
/// then add file extensions which we need.
|
||||
/// And finally convert it into immutable [Program] for formal using.
|
||||
#[derive(Debug)]
|
||||
pub struct Schema {
|
||||
@@ -92,7 +116,7 @@ impl Schema {
|
||||
}
|
||||
|
||||
/// Set the identifier of the schema.
|
||||
///
|
||||
///
|
||||
/// The identifier is used to build ProgId.
|
||||
/// So it should starts with an ASCII letter and followed by zero or more ASCII letters, digits, underline and hyphen.
|
||||
/// And it should not be empty.
|
||||
@@ -143,7 +167,7 @@ impl Schema {
|
||||
}
|
||||
|
||||
/// Add a file extension to the schema.
|
||||
///
|
||||
///
|
||||
/// The parameter `ext` is the file extension without leading dot `.`.
|
||||
pub fn add_ext(
|
||||
&mut self,
|
||||
@@ -162,7 +186,7 @@ impl Schema {
|
||||
}
|
||||
|
||||
/// Try converting [Schema] into [Program].
|
||||
///
|
||||
///
|
||||
/// This is equivalent to [Program::new].
|
||||
pub fn into_program(self) -> Result<Program, ParseProgramError> {
|
||||
Program::new(self)
|
||||
@@ -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())
|
||||
@@ -299,19 +322,20 @@ impl Program {
|
||||
}
|
||||
|
||||
/// Try converting [Schema] into [Program].
|
||||
///
|
||||
///
|
||||
/// During this process, some checks will be performed to ensure the validity of the data.
|
||||
/// For example, the reference to icon, name, or behavior must exist in their respective dictionaries.
|
||||
/// The identifier must be suit for building ProgId.
|
||||
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
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user