use proc_macro::TokenStream;
#[proc_macro_derive(Clappos)]
pub fn derive_snap(item: TokenStream) -> TokenStream {
println!("{item}");
let mut lines = item.to_string().split("\n").map(|s| s.trim().to_string()).collect::<Vec<String>>();
println!("{lines:?}");
let mut struct_comments = vec![];
while lines[0].starts_with("///") {
struct_comments.push(lines.remove(0));
}
let struct_name =
lines.remove(0)
.split(' ')
.map(|s| s.to_string())
.collect::<Vec<String>>()
.into_iter()
.find(|s| {
let first_char = s.as_bytes()[0];
first_char >= 0x41 && first_char <= 0x5a })
.expect("didn't find struct name?").to_string();
let mut idents = vec![];
let mut types = vec![];
let mut comments = vec![];
let mut type_expected = false;
let mut type_found = false;
lines.into_iter().for_each(|line| {
if line.starts_with("///") {
comments.push(line);
} else if line.len() > 1 {
let tokens = line.split(' ').map(|s| s.to_string()).collect::<Vec<String>>();
for token in tokens.iter() {
let mut token = token.trim().to_string();
println!("checking {token} for ident/type");
if type_found {
comments.push(tokens.join(" "));
type_found = false;
break;
} else if token.ends_with(':') {
token.remove(token.len() - 1);
idents.push(token);
type_expected = true;
} else if type_expected {
if token.ends_with(',') {
token.remove(token.len() - 1);
}
types.push(token);
type_expected = false;
type_found = true;
}
}
}
});
println!("STRUCT NAME: {struct_name}");
println!("IDENTS: {idents:?}");
println!("TYPES: {types:?}");
let mut stream =
format!(
"impl {struct_name} {{\n\
fn parse() -> Self {{\n\
let mut args = std::env::args().into_iter().filter(|a| !a.starts_with(\"--\")).map(|a| a).collect::<Vec<String>>();\n\
args.remove(0);
Self {{\n");
let mut arg_idx = 0;
for i in 0..idents.len() {
let ident = &idents[i];
let type_ = &types[i];
if type_.starts_with("Option<") {
let inner_type = &type_[7..(type_.len() - 1)];
if inner_type == "String" || inner_type == "bool" || inner_type == "char" {
stream.push_str(format!("{ident}: args.get({arg_idx}).cloned(),\n").as_str())
} else {
stream.push_str(format!("{ident}: Some(args[{arg_idx}].parse::<{type_}>().expect(\"failed to parse arg args[{arg_idx}] to {type_}\")),\n").as_str())
}
} else {
if type_ == "String" || type_ == "char" {
stream.push_str(format!("{ident}: args[{arg_idx}].clone(),\n").as_str());
} else if type_ == "bool" {
stream.push_str(format!("{ident}: match args[{arg_idx}].as_str() {{ \"true\" => true, \"false\" => false, x => panic!(\"expected bool, got {{x}}\") }},\n").as_str())
} else {
stream.push_str(format!("{ident}: args[{arg_idx}].parse::<{type_}>().expect(\"failed to parse arg args[{arg_idx}] to {type_}\").clone(),\n").as_str())
}
}
arg_idx += 1;
}
for arg in std::env::args() {
if !(arg_idx == 0 || arg.starts_with("--") || idents.len() == 0) {
}
arg_idx += 1;
}
println!("{item}\n{stream}");
stream.push_str("}}}");
stream.parse().unwrap()
}