first commit

This commit is contained in:
Soph :3 2025-09-09 10:15:15 +03:00
commit 0cb536b42b
38 changed files with 9044 additions and 0 deletions

188
src/commands/argument.rs Normal file
View file

@ -0,0 +1,188 @@
use std::{collections::HashMap, fmt};
#[derive(Debug, Clone)]
pub enum ArgumentType {
String,
Integer,
Float,
Boolean,
Enum(&'static [&'static str]),
GreedyString,
}
impl fmt::Display for ArgumentType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ArgumentType::String => write!(f, "String"),
ArgumentType::Integer => write!(f, "Integer"),
ArgumentType::Enum(variants) => write!(f, "Enum({:?})", variants),
ArgumentType::Float => write!(f, "Float"),
ArgumentType::Boolean => write!(f, "Boolean"),
ArgumentType::GreedyString => write!(f, "GreedyString"),
}
}
}
#[derive(Debug, Clone)]
pub struct ArgumentSpec {
pub name: &'static str,
pub arg_type: ArgumentType,
pub required: bool,
pub default: Option<&'static str>,
}
#[derive(Debug, Clone)]
pub enum ParsedArgument {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Enum(String),
GreedyString(String),
}
impl fmt::Display for ParsedArgument {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParsedArgument::String(s) => write!(f, "{}", s),
ParsedArgument::Integer(i) => write!(f, "{}", i),
ParsedArgument::Float(fl) => write!(f, "{}", fl),
ParsedArgument::Boolean(b) => write!(f, "{}", b),
ParsedArgument::Enum(e) => write!(f, "{}", e),
ParsedArgument::GreedyString(s) => write!(f, "{}", s),
}
}
}
pub fn parse_argument(arg: &str, spec: &ArgumentSpec) -> Result<ParsedArgument, String> {
match &spec.arg_type {
ArgumentType::String => Ok(ParsedArgument::String(arg.to_string())),
ArgumentType::Integer => arg
.parse::<i64>()
.map(ParsedArgument::Integer)
.map_err(|_| format!("Expected integer for '{}', got '{}'", spec.name, arg)),
ArgumentType::Float => arg
.parse::<f64>()
.map(ParsedArgument::Float)
.map_err(|_| format!("Expected float for '{}', got '{}'", spec.name, arg)),
ArgumentType::Boolean => match arg.to_lowercase().as_str() {
"true" | "yes" | "1" => Ok(ParsedArgument::Boolean(true)),
"false" | "no" | "0" => Ok(ParsedArgument::Boolean(false)),
_ => Err(format!(
"Expected boolean for '{}', got '{}'",
spec.name, arg
)),
},
ArgumentType::Enum(variants) => {
if variants.contains(&arg) {
Ok(ParsedArgument::Enum(arg.to_string()))
} else {
Err(format!(
"Argument '{}' must be one of {:?}, got '{}'",
spec.name, variants, arg
))
}
}
ArgumentType::GreedyString => Ok(ParsedArgument::GreedyString(arg.to_string())),
}
}
#[derive(Debug, Clone)]
pub struct ParsedArguments {
pub raw: Vec<String>,
pub parsed: HashMap<&'static str, ParsedArgument>,
}
impl ParsedArguments {
pub fn get(&self, name: &str) -> Option<&ParsedArgument> {
self.parsed.get(name)
}
}
pub fn parse_arguments(
specs: &[ArgumentSpec],
raw_args: &[String],
) -> Result<ParsedArguments, String> {
let mut parsed = HashMap::new();
let raw_vec = raw_args.to_vec();
let mut i = 0;
while i < specs.len() {
let spec = &specs[i];
let raw_value = match &spec.arg_type {
ArgumentType::GreedyString => {
if i >= raw_args.len() {
spec.default.map(|s| s.to_string())
} else {
Some(raw_args[i..].join(" "))
}
}
_ => raw_args
.get(i)
.cloned()
.or_else(|| spec.default.map(|s| s.to_string())),
};
match raw_value {
Some(ref value) => {
let parsed_arg = match &spec.arg_type {
ArgumentType::String => ParsedArgument::String(value.clone()),
ArgumentType::Integer => value
.parse::<i64>()
.map(ParsedArgument::Integer)
.map_err(|_| {
format!(
"Argument '{}' must be an integer, got '{}'",
spec.name, value
)
})?,
ArgumentType::Float => value
.parse::<f64>()
.map(ParsedArgument::Float)
.map_err(|_| {
format!("Argument '{}' must be a float, got '{}'", spec.name, value)
})?,
ArgumentType::Boolean => match value.to_lowercase().as_str() {
"true" | "yes" | "1" => ParsedArgument::Boolean(true),
"false" | "no" | "0" => ParsedArgument::Boolean(false),
_ => {
return Err(format!(
"Argument '{}' must be a boolean, got '{}'",
spec.name, value
));
}
},
ArgumentType::Enum(variants) => {
if variants.contains(&value.as_str()) {
ParsedArgument::Enum(value.clone())
} else {
return Err(format!(
"Argument '{}' must be one of {:?}, got '{}'",
spec.name, variants, value
));
}
}
ArgumentType::GreedyString => ParsedArgument::GreedyString(value.clone()),
};
parsed.insert(spec.name, parsed_arg);
if let ArgumentType::GreedyString = spec.arg_type {
break;
}
}
None if spec.required => {
return Err(format!("Missing required argument '{}'", spec.name));
}
None => {}
}
i += 1;
}
Ok(ParsedArguments {
raw: raw_vec,
parsed,
})
}