1
0

feat: remove some optional in rust and cbindgen

This commit is contained in:
2026-05-27 13:16:51 +08:00
parent 77924b5937
commit 8c61aa1e1d
5 changed files with 105 additions and 69 deletions

View File

@@ -230,22 +230,15 @@ public:
return *this; return *this;
} }
std::optional<std::string> ResolveName() { std::string ResolveName() {
const char* name = nullptr; const char* name = nullptr;
_Check(wfassoc::WFProgramResolveName(_token, &name)); _Check(wfassoc::WFProgramResolveName(_token, &name));
if (name == nullptr) {
return std::nullopt;
} else {
return std::string(name); return std::string(name);
} }
}
std::optional<IconRc> ResolveIcon() { IconRc ResolveIcon() {
Token token = _INVALID_TOKEN(); Token token = _INVALID_TOKEN();
_Check(wfassoc::WFProgramResolveIcon(_token, &token)); _Check(wfassoc::WFProgramResolveIcon(_token, &token));
if (token == _INVALID_TOKEN()) {
return std::nullopt;
}
return IconRc(token); return IconRc(token);
} }

View File

@@ -350,8 +350,13 @@ bool WFProgramDestroy(Token in_program);
/** /**
* @brief Resolve the provided program name of this Program * @brief Resolve the provided program name of this Program
* *
* The name will be user specified first,
* then fallback to program manifest file specified name,
* and finally fallback to the file name of executable.
*
* @param[in] in_program Program token * @param[in] in_program Program token
* @param[out] out_name Pointer to receive the resolved name, or NULL if not found. * @param[out] out_name Pointer to receive the resolved name.
* There is no possibility that this value is NULL.
* This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer.
* @return true on success, false on failure * @return true on success, false on failure
*/ */
@@ -360,8 +365,12 @@ bool WFProgramResolveName(Token in_program, CStyleString *out_name);
/** /**
* @brief Resolve the Program icon resource * @brief Resolve the Program icon resource
* *
* The icon will be user specified first,
* the fallback to the first icon of program,
* and finally fallback to the system default executable icon.
*
* @param[in] in_program Program token * @param[in] in_program Program token
* @param[out] out_icon_rc Pointer to receive the icon resource token, or invalid token if not found. * @param[out] out_icon_rc Pointer to receive the icon resource token.
* The caller take the ownership of created icon resource object. * The caller take the ownership of created icon resource object.
* And it should be freed by calling WFIconRcDestroy() when it is no longer needed. * And it should be freed by calling WFIconRcDestroy() when it is no longer needed.
* @return true on success, false on failure * @return true on success, false on failure
@@ -471,7 +480,10 @@ bool WFProgramQueryExt(Token in_program, View in_view, size_t in_index, Token *o
bool WFExtStatusDestroy(Token in_ext_status); bool WFExtStatusDestroy(Token in_ext_status);
/** /**
* @brief Get the name from an extension status object * @brief Get the display name from an extension status object
*
* The display will be user specified first,
* the fallback to its ProgId verbatim.
* *
* @param[in] in_ext_status Extension status token * @param[in] in_ext_status Extension status token
* @param[out] out_name Pointer to receive the name. * @param[out] out_name Pointer to receive the name.
@@ -485,8 +497,11 @@ bool WFExtStatusGetName(Token in_ext_status, CStyleString *out_name);
/** /**
* @brief Get the icon from an extension status object * @brief Get the icon from an extension status object
* *
* The icon will be user specified first,
* the fallback to the system default file icon.
*
* @param[in] in_ext_status Extension status token * @param[in] in_ext_status Extension status token
* @param[out] out_icon Pointer to receive the icon handle, or INVALID_HICON if not available. * @param[out] out_icon Pointer to receive the icon handle.
* This icon handle will be freed once this icon resource object is destroyed. * This icon handle will be freed once this icon resource object is destroyed.
* Please make a copy immediately if you need to use it longer. * Please make a copy immediately if you need to use it longer.
* @return true on success, false on failure * @return true on success, false on failure
@@ -504,6 +519,9 @@ bool WFSelfExtStatusDestroy(Token in_self_ext_status);
/** /**
* @brief Get the display name from a self extension status object * @brief Get the display name from a self extension status object
* *
* The display will be user specified first,
* the fallback to its ProgId verbatim.
*
* @param[in] in_self_ext_status Self extension status token * @param[in] in_self_ext_status Self extension status token
* @param[out] out_name Pointer to receive the name string. * @param[out] out_name Pointer to receive the name string.
* There is no possibility that this value is NULL. * There is no possibility that this value is NULL.
@@ -515,8 +533,11 @@ bool WFSelfExtStatusGetName(Token in_self_ext_status, CStyleString *out_name);
/** /**
* @brief Get the icon from a self extension status object * @brief Get the icon from a self extension status object
* *
* The icon will be user specified first,
* the fallback to the system default file icon.
*
* @param[in] in_self_ext_status Self extension status token * @param[in] in_self_ext_status Self extension status token
* @param[out] out_icon Pointer to receive the icon handle, or INVALID_HICON if not available. * @param[out] out_icon Pointer to receive the icon handle.
* This icon handle will be freed once this self extension status object is destroyed. * This icon handle will be freed once this self extension status object is destroyed.
* Please make a copy immediately if you need to use it longer. * Please make a copy immediately if you need to use it longer.
* @return true on success, false on failure * @return true on success, false on failure

View File

@@ -492,16 +492,9 @@ pub extern "C" fn WFProgramResolveName(
let mut pool = pull_writer!(PROGRAM_POOL)?; let mut pool = pull_writer!(PROGRAM_POOL)?;
let program = pool.get_mut(in_program)?; let program = pool.get_mut(in_program)?;
let name = match program.resolve_name()? { let name = program.resolve_name()?;
Some(name) => {
cstr_ffi::set_ffi_string(&name)?; cstr_ffi::set_ffi_string(&name)?;
cstr_ffi::get_ffi_string() Ok(cstr_ffi::get_ffi_string())
},
None => {
std::ptr::null()
},
};
Ok(name)
}) })
} }
@@ -515,16 +508,8 @@ pub extern "C" fn WFProgramResolveIcon(
let program = pool.get_mut(in_program)?; let program = pool.get_mut(in_program)?;
let icon = program.resolve_icon()?; let icon = program.resolve_icon()?;
let token = match icon {
Some(icon) => {
let mut pool = pull_writer!(ICON_RC_POOL)?; let mut pool = pull_writer!(ICON_RC_POOL)?;
pool.allocate(icon)? Ok(pool.allocate(icon)?)
},
None => {
object_pool::invalid_token()
},
};
Ok(token)
}) })
} }
@@ -707,11 +692,8 @@ pub extern "C" fn WFExtStatusGetIcon(
let pool = pull_reader!(EXT_STATUS_POOL)?; let pool = pull_reader!(EXT_STATUS_POOL)?;
let ext_status = pool.get(in_ext_status)?; let ext_status = pool.get(in_ext_status)?;
let icon = match ext_status.get_icon() { let icon = ext_status.get_icon();
Some(icon) => icon.get_icon(), Ok(icon.get_icon())
None => ffi_types::INVALID_HICON,
};
Ok(icon)
}) })
} }
@@ -750,11 +732,8 @@ pub extern "C" fn WFSelfExtStatusGetIcon(
let pool = pull_reader!(SELF_EXT_STATUS_POOL)?; let pool = pull_reader!(SELF_EXT_STATUS_POOL)?;
let self_ext_status = pool.get(in_self_ext_status)?; let self_ext_status = pool.get(in_self_ext_status)?;
let icon = match self_ext_status.get_icon() { let icon = self_ext_status.get_icon();
Some(icon) => icon.get_icon(), Ok(icon.get_icon())
None => ffi_types::INVALID_HICON,
};
Ok(icon)
}) })
} }

View File

@@ -44,6 +44,8 @@ pub enum ParseProgramError {
pub enum ProgramError { pub enum ProgramError {
#[error("{0}")] #[error("{0}")]
Lowlevel(#[from] lowlevel::Error), Lowlevel(#[from] lowlevel::Error),
#[error("{0}")]
LoadIconRc(#[from] concept::LoadIconRcError),
#[error("given index is invalid")] #[error("given index is invalid")]
BadIndex, BadIndex,
} }
@@ -57,6 +59,7 @@ pub struct Program {
app_paths_key: lowlevel::AppPathsKey, app_paths_key: lowlevel::AppPathsKey,
applications_key: lowlevel::ApplicationsKey, applications_key: lowlevel::ApplicationsKey,
app_path: String, app_path: String,
app_file_name: String,
app_dir_path: String, app_dir_path: String,
name: Option<Arc<ProgramStr>>, name: Option<Arc<ProgramStr>>,
icon: Option<Arc<ProgramIcon>>, icon: Option<Arc<ProgramIcon>>,
@@ -254,6 +257,7 @@ impl Program {
app_paths_key, app_paths_key,
applications_key, applications_key,
app_path, app_path,
app_file_name,
app_dir_path, app_dir_path,
name, name,
icon, icon,
@@ -268,26 +272,43 @@ impl Program {
} }
impl Program { impl Program {
pub fn resolve_name(&self) -> Result<Option<String>, ProgramError> { pub fn resolve_name(&self) -> Result<String, ProgramError> {
// TODO: // Fecch from user specified name first
// Add fallback: fetch it from executable manifest file. let name = self
// Add fallback: use executable name directly.
Ok(self
.name .name
.as_ref() .as_ref()
.map(|name| name.inner.extract().ok()) .map(|name| name.inner.extract().ok())
.flatten()) .flatten();
if let Some(name) = name {
return Ok(name);
} }
pub fn resolve_icon(&self) -> Result<Option<concept::IconRc>, ProgramError> {
// TODO: // TODO:
// Add fallback: fetch it from the first icon of executable. // Fetch it from executable manifest file.
// Add fallback: use system default executable icon.
Ok(self // Finally fallback to use executable name.
Ok(self.app_file_name.clone())
}
pub fn resolve_icon(&self) -> Result<concept::IconRc, ProgramError> {
// Fetch from user specified icon first
let icon = self
.icon .icon
.as_ref() .as_ref()
.map(|icon| icon.inner.extract(concept::IconSizeKind::Small).ok()) .map(|icon| icon.inner.extract(concept::IconSizeKind::Small).ok())
.flatten()) .flatten();
if let Some(icon) = icon {
return Ok(icon);
}
// Fetch from the first icon of executable instead.
let icon = concept::IconRc::new(&self.app_path, 0, concept::IconSizeKind::Small).ok();
if let Some(icon) = icon {
return Ok(icon);
}
// Finally fallback to use system default executable icon.
Ok(concept::IconRc::GENERIC_APPLICATION(
concept::IconSizeKind::Small,
)?)
} }
pub fn exts_len(&self) -> usize { pub fn exts_len(&self) -> usize {
@@ -309,12 +330,14 @@ impl Program {
.inner .inner
.extract() .extract()
.unwrap_or(progid_ext_key.progid_key.inner().to_string()); .unwrap_or(progid_ext_key.progid_key.inner().to_string());
// Try to fetch icon // Try to fetch icon, and fallback to system default file icon.
let icon = progid_ext_key let icon = progid_ext_key
.icon .icon
.inner .inner
.extract(concept::IconSizeKind::Small) .extract(concept::IconSizeKind::Small)
.ok(); .unwrap_or(concept::IconRc::GENERIC_DOCUMENT(
concept::IconSizeKind::Small,
)?);
// Okey, return it // Okey, return it
Ok(ProgramSelfExtStatus::new( Ok(ProgramSelfExtStatus::new(
@@ -557,11 +580,14 @@ impl Program {
.flatten(); .flatten();
} }
let name = name.unwrap_or(progid_key.inner().to_string()); let name = name.unwrap_or(progid_key.inner().to_string());
// Now try to fetch icon. // Now try to fetch icon and fallback to system default file icon.
let icon = progid_key let icon = progid_key
.get_default_icon(view)? .get_default_icon(view)?
.map(|ico| ico.extract(concept::IconSizeKind::Small).ok()) .map(|ico| ico.extract(concept::IconSizeKind::Small).ok())
.flatten(); .flatten()
.unwrap_or(concept::IconRc::GENERIC_DOCUMENT(
concept::IconSizeKind::Small,
)?);
// Okey, return it. // Okey, return it.
Ok(Some(ProgramExtStatus::new(name, icon))) Ok(Some(ProgramExtStatus::new(name, icon)))
@@ -617,11 +643,11 @@ struct ProgramProgIdExtKey {
pub struct ProgramSelfExtStatus { pub struct ProgramSelfExtStatus {
ext: concept::Ext, ext: concept::Ext,
name: String, name: String,
icon: Option<concept::IconRc>, icon: concept::IconRc,
} }
impl ProgramSelfExtStatus { impl ProgramSelfExtStatus {
fn new(ext: concept::Ext, name: String, icon: Option<concept::IconRc>) -> Self { fn new(ext: concept::Ext, name: String, icon: concept::IconRc) -> Self {
Self { ext, name, icon } Self { ext, name, icon }
} }
@@ -646,8 +672,8 @@ impl ProgramSelfExtStatus {
/// Get the icon of this program. /// Get the icon of this program.
/// ///
/// Due to the icon is optional, if there is no icon, return None. /// Due to the icon is optional, if there is no icon, return None.
pub fn get_icon(&self) -> Option<&concept::IconRc> { pub fn get_icon(&self) -> &concept::IconRc {
self.icon.as_ref() &self.icon
} }
} }
@@ -656,11 +682,11 @@ impl ProgramSelfExtStatus {
/// The data including the diaplay name and icon. /// The data including the diaplay name and icon.
pub struct ProgramExtStatus { pub struct ProgramExtStatus {
name: String, name: String,
icon: Option<concept::IconRc>, icon: concept::IconRc,
} }
impl ProgramExtStatus { impl ProgramExtStatus {
fn new(name: String, icon: Option<concept::IconRc>) -> Self { fn new(name: String, icon: concept::IconRc) -> Self {
Self { name, icon } Self { name, icon }
} }
@@ -675,8 +701,8 @@ impl ProgramExtStatus {
/// Get the icon of this program. /// Get the icon of this program.
/// ///
/// Due to the icon is optional, if there is no icon, return None. /// Due to the icon is optional, if there is no icon, return None.
pub fn get_icon(&self) -> Option<&concept::IconRc> { pub fn get_icon(&self) -> &concept::IconRc {
self.icon.as_ref() &self.icon
} }
} }

View File

@@ -510,6 +510,20 @@ pub struct IconRc {
icon: HICON, icon: HICON,
} }
impl IconRc {
/// Get the icon preset representing generic document.
#[allow(non_snake_case)]
pub fn GENERIC_DOCUMENT(kind: IconSizeKind) -> Result<Self, LoadIconRcError> {
Self::new("shell32.dll", 0, kind)
}
/// Get the icon preset representing generic executable.
#[allow(non_snake_case)]
pub fn GENERIC_APPLICATION(kind: IconSizeKind) -> Result<Self, LoadIconRcError> {
Self::new("imageres.dll", 0, kind)
}
}
impl IconRc { impl IconRc {
/// Load icon from executable or `.ico` file. /// Load icon from executable or `.ico` file.
/// ///
@@ -867,16 +881,19 @@ pub struct Verb {
} }
impl Verb { impl Verb {
/// Get the verb representing Open.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn OPEN() -> Self { pub fn OPEN() -> Self {
Verb::new("open").expect("unexpected bad verb") Verb::new("open").expect("unexpected bad verb")
} }
/// Get the verb representing Edit.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn EDIT() -> Self { pub fn EDIT() -> Self {
Verb::new("edit").expect("unexpected bad verb") Verb::new("edit").expect("unexpected bad verb")
} }
/// Get the verb representing Play.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn PLAY() -> Self { pub fn PLAY() -> Self {
Verb::new("play").expect("unexpected bad verb") Verb::new("play").expect("unexpected bad verb")