use clap::Parser;
use core::ffi::{c_char, c_int};
use std::{path::PathBuf, sync::OnceLock};
#[cfg(target_family = "wasm")]
use wasm_bindgen::prelude::wasm_bindgen;
static ARGS: OnceLock<Args> = OnceLock::new();
#[derive(Parser, Debug, Clone)]
#[command(author, about, long_about = None)]
pub struct Args {
#[arg(short, long, default_value_t = false)]
pub server: bool,
#[arg(short, long, default_value_t = false)]
pub version: bool,
#[arg(short, long, default_value = "assets")]
pub assets: PathBuf,
#[cfg(all(feature = "appimage", not(feature = "no_lint")))]
#[arg(long, default_value_t = false)]
pub appimage_help: bool,
#[cfg(all(target_os = "linux", not(feature = "no_lint")))]
#[arg(long, default_value_t = false)]
pub install_desktop_files: bool,
#[cfg(all(target_os = "linux", not(feature = "no_lint")))]
#[arg(long, default_value_t = false)]
pub uninstall_desktop_files: bool,
#[arg(long, default_value_t = false)]
pub print_environment_variables: bool,
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
#[must_use] pub unsafe fn parse_args_from_c(
arg_count: c_int,
arg_vector_pointer: *const *const *const c_char,
) -> Option<Vec<String>> {
use core::ffi::CStr;
if arg_vector_pointer.is_null() {
return None;
}
let arg_vector: *const *const c_char = arg_vector_pointer.cast::<*const c_char>();
if arg_vector.is_null() {
return None;
}
#[allow(clippy::cast_sign_loss)]
let c_args: &[*const c_char] =
unsafe { std::slice::from_raw_parts(arg_vector, arg_count as usize) };
let mut args: Vec<String> = vec![];
for &arg in c_args {
let c_str: &CStr = unsafe { CStr::from_ptr(arg) };
let str_slice: &str = c_str.to_str().unwrap();
args.push(str_slice.to_string());
}
Some(args)
}
#[cfg_attr(target_family = "wasm", wasm_bindgen)]
pub fn set_parsed_args(args: Vec<String>) {
if ARGS.get().is_some() {
return;
}
let _ = ARGS.set(Args::parse_from(args.iter()));
}
pub fn get_args() -> Option<Args> {
ARGS.get().cloned()
}
#[cfg(test)]
mod tests {
use std::ffi::CString;
#[test]
fn test_parsing_args_from_c() {
use super::*;
unsafe {
assert_eq!(parse_args_from_c(999, core::ptr::null()), None);
}
unsafe {
let arg_one: CString = CString::new("hello").unwrap();
let arg_one_ptr: *const c_char = arg_one.as_ptr();
let argv = [arg_one_ptr];
assert_eq!(
parse_args_from_c(
i32::try_from(argv.len()).unwrap(),
argv.as_ptr().cast::<*const *const c_char>()
),
Some(vec!["hello".to_string()])
);
}
}
}