oh shit this might actually be quite usable

This commit is contained in:
nora 2021-09-24 22:17:30 +02:00
parent c126dda2df
commit cdb67f070c
5 changed files with 221 additions and 48 deletions

View file

@ -3,4 +3,6 @@ A Rust fully type-safe argument parser without proc-macros
Usability comes after that
oh and it's also not even close to being usable in any way at all so there's that
oh and it's also not even close to being usable in any way at all so there's that
oh and also 0 dependencies btw

View file

@ -1,24 +1,14 @@
use badargs::{CliArg, CliArgInfo, Template};
use std::collections::HashMap;
use badargs::arg;
use badargs::CliArg;
#[derive(Default)]
struct OutFile;
arg!(OutFile: "output", "o" -> Option<String>);
arg!(Force: "force", "f" -> bool);
arg!(OLevel: "optimize" -> usize);
fn main() {
let args = badargs::badargs(Template {
options: {
let mut map = HashMap::new();
map.insert(
Box::new(OutFile),
CliArgInfo {
name: "output".to_string(),
allow_short: true,
takes_value: true,
},
);
map
},
});
let args = badargs::badargs::<(OutFile, (Force, OLevel))>().unwrap();
let outfile = args.get::<OutFile>();
let _outfile = args.get::<OutFile>();
let _force = args.get::<Force>();
let _o_level = args.get::<OLevel>();
}

View file

@ -1,43 +1,128 @@
use std::any::{Any, TypeId};
use std::collections::HashMap;
mod macros;
mod schema;
#[derive(Debug, Clone)]
pub enum CliOption {
Flag(bool),
Value(String),
use crate::parse::CliArgs;
use crate::schema::{IntoSchema, SchemaKind};
pub use error::ArgError;
pub use macros::*;
pub type Result<T> = std::result::Result<T, ArgError>;
pub trait CliReturnValue: sealed::SealedCliReturnValue {
fn kind() -> schema::SchemaKind;
}
macro_rules! impl_cli_return {
($(for $ty:ty => $type:ident);+) => {$(
impl CliReturnValue for $ty {
fn kind() -> SchemaKind {
SchemaKind::$type
}
}
)+};
}
impl_cli_return!(
for String => String;
for Option<String> => OptionString;
for bool => Bool;
for isize => INum;
for usize => UNum
);
mod sealed {
pub trait SealedCliReturnValue {}
macro_rules! impl_ {
($($name:ty),+) => {$(impl SealedCliReturnValue for $name{})+};
}
impl_!(String, Option<String>, bool, usize, isize);
}
pub trait CliArg {
type Content;
}
#[derive(Debug, Clone, Default)]
pub struct Template {
pub options: HashMap<Box<dyn Any>, CliArgInfo>,
}
pub struct CliArgInfo {
pub name: String,
pub allow_short: bool,
pub takes_value: bool,
type Content: CliReturnValue;
fn long() -> &'static str;
fn short() -> Option<&'static str>;
}
#[derive(Debug, Clone, Default)]
pub struct BadArgs {
options: HashMap<TypeId, CliArgInfo>,
args: CliArgs,
}
impl BadArgs {
pub fn get<T: Default>(&self) -> Option<&CliArgInfo> {
self.options.get(&T::type_id())
pub fn get<T>(&self) -> &T::Content
where
T: CliArg,
{
todo!()
}
}
pub fn badargs(template: Template) -> BadArgs {
let options = template
.options
.into_iter()
.map(|(key, value)| (key.type_id(), value))
.collect();
BadArgs { options }
pub fn badargs<S>() -> Result<BadArgs>
where
S: IntoSchema,
{
let arg_schema = schema::parse_schema::<S>()?;
let args = CliArgs::from_args(arg_schema, std::env::args_os())?;
Ok(BadArgs { args })
}
mod error {
#[derive(Debug, Clone)]
pub enum ArgError {
InvalidUtf8,
NameAlreadyExists(&'static str),
InvalidSchema(String),
IdkYet,
}
}
mod parse {
use super::Result;
use crate::schema::Schema;
use std::collections::HashMap;
use std::ffi::OsString;
#[derive(Debug, Clone, Default)]
pub struct CliArgs {
pub isize: HashMap<&'static str, isize>,
pub usize: HashMap<&'static str, isize>,
pub string: HashMap<&'static str, String>,
pub option_string: HashMap<&'static str, Option<String>>,
pub bool: HashMap<&'static str, bool>,
}
impl CliArgs {
pub fn from_args(_schema: Schema, args: impl Iterator<Item = OsString>) -> Result<Self> {
let mut result = Self::default();
let mut args = args;
while let Some(_arg) = args.next() {}
Ok(result)
}
}
}
#[cfg(test)]
mod test {
use crate::CliArg;
struct OutFile;
impl CliArg for OutFile {
type Content = Option<String>;
fn long() -> &'static str {
"output"
}
fn short() -> Option<&'static str> {
Some("o")
}
}
#[test]
fn get_single_schema() {}
}

25
src/macros.rs Normal file
View file

@ -0,0 +1,25 @@
#[macro_export]
macro_rules! arg {
($name:ident: $long:literal, $short:literal -> $result:ty) => {
arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result);
};
($name:ident: $long:literal -> $result:ty) => {
arg!(@$name: ($long, ::std::option::Option::None) -> $result);
};
(@$name:ident: ($long:literal, $short:expr) -> $result:ty) => {
#[derive(Default)]
struct $name;
impl ::badargs::CliArg for $name {
type Content = $result;
fn long() -> &'static str {
$long
}
fn short() -> Option<&'static str> {
$short
}
}
};
}

71
src/schema.rs Normal file
View file

@ -0,0 +1,71 @@
use super::Result;
use crate::{ArgError, CliArg, CliReturnValue};
use std::collections::HashMap;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SchemaKind {
String,
OptionString,
Bool,
INum,
UNum,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SchemaCommand {
short: Option<&'static str>,
kind: SchemaKind,
}
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct Schema {
commands: HashMap<&'static str, SchemaCommand>,
}
impl Schema {
pub fn add_command(&mut self, name: &'static str, command: SchemaCommand) -> Result<()> {
if let Some(_) = self.commands.insert(name, command) {
Err(ArgError::NameAlreadyExists(name))
} else {
Ok(())
}
}
}
pub trait IntoSchema {
fn add_schema(schema: &mut Schema) -> Result<()>;
}
/// Allow using multiple schema values, these tuples can be nested :D
impl<S1, S2> IntoSchema for (S1, S2)
where
S1: IntoSchema,
S2: IntoSchema,
{
fn add_schema(schema: &mut Schema) -> Result<()> {
S1::add_schema(schema)?;
S2::add_schema(schema)
}
}
pub fn parse_schema<S>() -> Result<Schema>
where
S: IntoSchema,
{
let mut schema = Schema::default();
S::add_schema(&mut schema)?;
Ok(schema)
}
/// Create the Schema from the CliArg type
impl<T> IntoSchema for T
where
T: CliArg,
{
fn add_schema(schema: &mut Schema) -> Result<()> {
let kind = T::Content::kind();
let name = T::long();
let short = T::short();
schema.add_command(name, SchemaCommand { short, kind })
}
}