forked from AbleOS/ableos_userland
211 lines
5.7 KiB
Rust
211 lines
5.7 KiB
Rust
//! # clparse
|
|
//! simple command line parser for ableOS
|
|
|
|
#![no_std]
|
|
|
|
extern crate alloc;
|
|
|
|
#[cfg(feature = "std")]
|
|
extern crate std;
|
|
|
|
use hashbrown;
|
|
use hashbrown::HashMap;
|
|
|
|
use alloc::string::String;
|
|
pub use toml;
|
|
|
|
pub type Argmap = HashMap<String, String>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Arguments {
|
|
pub arguments: Argmap,
|
|
}
|
|
|
|
impl Arguments {
|
|
// TODO: Maybe check if a key is truthy, like if it's value equals "true", "1", "on", etc.
|
|
// NOTE: The above is out of scope for this pr but will be considered -able
|
|
pub fn parse(command: String) -> Result<Arguments, CLparseErrors> {
|
|
// TODO: Parse one implicit arg.
|
|
// NOTE: The above is out of scope for this pr but will be considered -able
|
|
let mut cmd = Arguments {
|
|
arguments: HashMap::new(),
|
|
};
|
|
|
|
if !command.is_empty() {
|
|
let command_split = command.split(" ");
|
|
for kv_pair in command_split {
|
|
if kv_pair.contains("=") {
|
|
// println!("{kv_pair}");
|
|
let mut kv_split = kv_pair.split("=");
|
|
|
|
let key = kv_split.next().unwrap().to_string();
|
|
// println!("{key}");
|
|
let value = kv_split.next().unwrap().to_string();
|
|
// println!("{value}");
|
|
|
|
cmd.arguments.insert(key, value);
|
|
} else {
|
|
return Err(CLparseErrors::MissingEquals);
|
|
}
|
|
}
|
|
}
|
|
Ok(cmd)
|
|
}
|
|
pub fn parse_from_string<S: Into<String>>(
|
|
string: S,
|
|
) -> Result<(Value, Arguments), CLparseErrors> {
|
|
let config: toml::Value = Value::String("".to_string());
|
|
|
|
let args = Arguments::parse(string.into())?;
|
|
|
|
return Ok((config, args));
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
pub fn parse_from_args() -> Result<(Value, Arguments), CLparseErrors> {
|
|
use std::{
|
|
collections::{BTreeMap, HashMap},
|
|
env,
|
|
fs::File,
|
|
io::{Read, Write},
|
|
println,
|
|
};
|
|
|
|
use alloc::format;
|
|
|
|
let args = env::args();
|
|
let mut args_str = String::new();
|
|
|
|
let dir = env::current_dir().unwrap();
|
|
let mut config: toml::Value = Value::String("".to_string());
|
|
match env::current_exe() {
|
|
Ok(bin) => {
|
|
if let Some(exe_name) = bin.as_path().file_name() {
|
|
let exe_name = exe_name.to_str().unwrap();
|
|
|
|
// println!("{exe_name}");
|
|
|
|
let path = format!(
|
|
"/home/{}/configs/{}/config.toml",
|
|
env::var("USER").unwrap(),
|
|
exe_name
|
|
);
|
|
let path = std::path::Path::new(&path);
|
|
if path.exists() {
|
|
let display = path.display();
|
|
|
|
let mut file = match File::open(&path) {
|
|
Err(why) => panic!("couldn't open {}: {}", display, why),
|
|
Ok(file) => file,
|
|
};
|
|
|
|
let mut s = String::new();
|
|
match file.read_to_string(&mut s) {
|
|
Err(why) => panic!("couldn't read {}: {}", display, why),
|
|
Ok(_) => {
|
|
// print!("{} contains:\n{}", display, s)
|
|
}
|
|
}
|
|
|
|
config = toml::from_str(&s).unwrap();
|
|
} else {
|
|
let mut file = File::create(path);
|
|
|
|
println!("Make a file in your home dir called \"configs\"");
|
|
|
|
file.unwrap().write_all(b"Hello, world!").unwrap();
|
|
}
|
|
}
|
|
}
|
|
Err(_) => todo!(),
|
|
}
|
|
|
|
for (count, arg) in args.enumerate() {
|
|
if count == 0 || count == 1 {
|
|
// Ignore stdio + program name on unix
|
|
} else {
|
|
args_str.push_str(&arg);
|
|
args_str.push(' ');
|
|
}
|
|
}
|
|
// needed to remove the very last ` ` which is unneeded
|
|
args_str.pop();
|
|
// println!("{args_str}");
|
|
|
|
let ret = Arguments::parse(args_str)?;
|
|
return Ok((config, ret));
|
|
}
|
|
|
|
pub fn is_truthy(&self, key: &str) -> bool {
|
|
match self.arguments.get(key) {
|
|
Some(val) => {
|
|
let val = val.to_lowercase();
|
|
if val == "true" || val == "1" || val == "on" || val == "t" {
|
|
return true;
|
|
}
|
|
false
|
|
}
|
|
None => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum CLparseErrors {
|
|
MalformendArgument,
|
|
MissingEquals,
|
|
}
|
|
|
|
impl Display for CLparseErrors {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
write!(f, "{:?}", self)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
use core::fmt::Display;
|
|
|
|
use crate::alloc::string::ToString;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
use super::*;
|
|
#[test]
|
|
#[cfg(feature = "std")]
|
|
fn test_arg_parse() {
|
|
use std::println;
|
|
let argline = "abc=xyz".to_string();
|
|
|
|
match Arguments::parse(argline) {
|
|
Ok(ok) => {
|
|
println!("{ok:?}")
|
|
}
|
|
Err(err) => panic!("{err:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn fail_test_arg_parse() {
|
|
let argline = "abcxyz".to_string();
|
|
|
|
match Arguments::parse(argline) {
|
|
Ok(ok) => panic!("{ok:?}"),
|
|
Err(_) => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
use toml::Value;
|
|
|
|
#[test]
|
|
#[cfg(feature = "std")]
|
|
pub fn load_config() {
|
|
use std::println;
|
|
|
|
let toml_str = include_str!("../assets/example.toml");
|
|
|
|
let decoded: Value = toml::from_str(toml_str).unwrap();
|
|
println!("{}", decoded);
|
|
}
|