Skip to content

Instantly share code, notes, and snippets.

@sahandevs
Last active August 22, 2022 13:33
Show Gist options
  • Select an option

  • Save sahandevs/125344a08ed97cda1091f384769a3e2f to your computer and use it in GitHub Desktop.

Select an option

Save sahandevs/125344a08ed97cda1091f384769a3e2f to your computer and use it in GitHub Desktop.
proc_macro_demo
[package]
name = "proc_macro_demo_1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
proc-macro = true
[dependencies]
walkdir = "2"
syn = { version = "*", features = ["full"] }
use proc_macro_demo_1::add_describe;
fn main() {
let a = MyStruct { a: 0, b: String::new() };
a.describe(); // a: 0, b: ""
}
struct MyStruct {
pub a: u32,
pub b: String,
}
add_describe!(MyStruct);
use proc_macro::TokenStream;
use walkdir::WalkDir;
#[proc_macro]
pub fn add_describe(input: TokenStream) -> TokenStream {
let struct_name = input.to_string();
let current_dir = std::env::current_dir().unwrap();
let src_dir = current_dir.join("src");
let mut fields = Vec::new();
'outer: for item in WalkDir::new(src_dir) {
let item = item.unwrap();
if item
.file_name()
.to_str()
.unwrap_or_default()
.ends_with(".rs")
{
let content = std::fs::read_to_string(item.path()).unwrap();
let result = syn::parse_str::<syn::File>(&content).unwrap();
for item in result.items {
match item {
syn::Item::Struct(x) => {
if x.ident.to_string() == struct_name {
fields.extend(x.fields.into_iter());
break 'outer;
}
}
_ => {}
}
}
}
}
if fields.is_empty() {
panic!("undefined struct {}", struct_name)
};
/*
struct MyStruct {
a: u32,
b: String,
}
impl MyStruct {
pub fn describe(&self) {
let mut result = String::from("MyStruct:");
result.push_str("\ta:u32");
result.push_str("\tb:String");
println!("{}", result);
}
}
*/
let mut describe_inner = String::new();
describe_inner.push_str(&format!(
"let mut result = String::from(\"{}:\\n\");",
struct_name
));
for field in fields {
let ty = match field.ty {
syn::Type::Path(x) => x
.path
.segments
.iter()
.map(|x| x.ident.to_string())
.collect::<Vec<_>>()
.join("::"),
_ => todo!(),
};
describe_inner.push_str(&format!(
"result.push_str(\"\\t{}:{}\\n\");",
field.ident.unwrap().to_string(),
ty
));
}
let result = format!(
r#"
impl {} {{
pub fn describe(&self) {{
{}
println!("{{}}", result);
}}
}}
"#,
struct_name, describe_inner
);
let result: TokenStream = result.parse().unwrap();
result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment