Skip to content

Commit 8ec1fb6

Browse files
committed
Initial treesap implementation
1 parent c1c73ac commit 8ec1fb6

File tree

5 files changed

+229
-0
lines changed

5 files changed

+229
-0
lines changed

Cargo.lock

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ lto = true
2323
codegen-units = 1
2424
strip = true
2525
panic = "abort"
26+
27+
[dev-dependencies]
28+
anyhow = "1.0.100"
29+
bitflags = "2.10.0"
30+
itoa = "1.0.15"

treesap/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,14 @@ name = "treesap"
33
version = "0.1.0"
44
edition = "2024"
55

6+
[lib]
7+
proc-macro = true
8+
69
[dependencies]
10+
darling = "0.21.3"
11+
quote = "1.0.41"
12+
syn = { version = "2.0.108", features = ["full"] }
13+
14+
[dev-dependencies]
15+
anyhow = "1.0.100"
16+
sap = { path = ".." }

treesap/examples/derive.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use anyhow::Result;
2+
use treesap::Parser;
3+
4+
#[derive(Debug, Parser)]
5+
struct Args {
6+
#[arg(short, long)]
7+
verbose: bool,
8+
}
9+
10+
fn main() -> Result<()> {
11+
let args = Args::parse()?;
12+
13+
dbg!(args);
14+
15+
Ok(())
16+
}

treesap/src/lib.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,81 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
use syn::{Data, DataStruct, DeriveInput, parse_macro_input};
14

5+
#[proc_macro_derive(Parser, attributes(arg, command))]
6+
pub fn derive_parser(input: TokenStream) -> TokenStream {
7+
let input = parse_macro_input!(input as DeriveInput);
8+
let ident = input.ident;
9+
10+
match input.data {
11+
Data::Struct(DataStruct { fields, .. }) => {
12+
let mut field_declarations = Vec::new();
13+
let mut field_setters = Vec::new();
14+
let mut field_getters = Vec::new();
15+
16+
for field in fields {
17+
let ident = field.ident.unwrap();
18+
let ty = field.ty;
19+
let option = ident.to_string();
20+
21+
field_declarations.push(quote! {
22+
let mut #ident: Option<#ty> = None;
23+
});
24+
25+
field_setters.push(quote! {
26+
sap::Argument::Long(#option) => { #ident = Some(true) }
27+
});
28+
29+
field_getters.push(quote! {
30+
#ident: #ident.unwrap()
31+
});
32+
}
33+
34+
quote! {
35+
impl #ident {
36+
const HELP: &str = "";
37+
38+
pub fn parse() -> ::sap::Result<Self> {
39+
use std::io::Write as _;
40+
41+
let mut arg_parser = sap::Parser::from_env()?;
42+
43+
#(#field_declarations)*
44+
45+
while let Some(arg) = arg_parser.forward()? {
46+
47+
match arg {
48+
// Long("version") => {
49+
// stdout
50+
// .write_all(
51+
// "wc (puppyutils) 0.0.1\nLicensed under the European Union Public Licence (EUPL) <https://eupl.eu/>\n"
52+
// .as_bytes(),
53+
// )?;
54+
// stdout.flush()?;
55+
// std::process::exit(0);
56+
// }
57+
// Long("help") => {
58+
// stdout
59+
// .write_all(
60+
// "Usage: wc [OPTION]... [FILE]...\nPrint newline, word, and byte counts for each FILE. A word is a nonempty \nsequence of non white space delimited by white space characters or by start \nor end of input.\n\n -c, --bytes print the byte counts\n -m, --chars print the character counts\n -l, --lines print the newline counts\n -w, --words print the word counts\n --help display this help and exit\n --version output version information and exit\n\nWith no FILE, or when FILE is -, read standard input.\n"
61+
// .as_bytes(),
62+
// )?;
63+
// stdout.flush()?;
64+
// std::process::exit(0);
65+
// }
66+
#(#field_setters),*
67+
arg => return Err(arg.into_error(None)),
68+
}
69+
}
70+
71+
Ok(Self { #(#field_getters),* })
72+
}
73+
}
74+
75+
}
76+
.into()
77+
}
78+
Data::Enum(_data_enum) => todo!(),
79+
Data::Union(_data_union) => todo!(),
80+
}
81+
}

0 commit comments

Comments
 (0)