use clap::Parser;
use core::ffi::{c_char, c_int};
use std::{path::PathBuf, sync::OnceLock};
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,
}
pub unsafe fn parse_args_from_c(
argc: c_int,
argv_pointer: *const *const *const c_char,
) -> Option<Vec<String>> {
use core::ffi::CStr;
if argv_pointer.is_null() {
return None;
}
let argv: *const *const c_char = argv_pointer as *const *const c_char;
if argv.is_null() {
return None;
}
let c_args: &[*const c_char] = unsafe { std::slice::from_raw_parts(argv, argc 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_owned());
}
Some(args)
}
#[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() -> () {
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(
argv.len() as i32,
argv.as_ptr() as *const *const *const c_char
),
Some(vec!["hello".to_owned()])
);
}
}
}