continuwuity/src/macros/config.rs
Jason Volk ba253738dc initial example-config generator
Signed-off-by: Jason Volk <jason@zemos.net>
2024-10-21 22:09:22 +00:00

98 lines
1.7 KiB
Rust

use std::fmt::Write;
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{Expr, ExprLit, Field, Fields, FieldsNamed, ItemStruct, Lit, Meta, MetaNameValue, Type, TypePath};
use crate::{utils::is_cargo_build, Result};
#[allow(clippy::needless_pass_by_value)]
pub(super) fn example_generator(input: ItemStruct, args: &[Meta]) -> Result<TokenStream> {
if is_cargo_build() {
generate_example(&input, args)?;
}
Ok(input.to_token_stream().into())
}
#[allow(clippy::needless_pass_by_value)]
#[allow(unused_variables)]
fn generate_example(input: &ItemStruct, _args: &[Meta]) -> Result<()> {
if let Fields::Named(FieldsNamed {
named,
..
}) = &input.fields
{
for field in named {
let Some(ident) = &field.ident else {
continue;
};
let Some(doc) = get_doc_comment(field) else {
continue;
};
let Some(type_name) = get_type_name(field) else {
continue;
};
//println!("{:?} {type_name:?}\n{doc}", ident.to_string());
}
}
Ok(())
}
fn get_doc_comment(field: &Field) -> Option<String> {
let mut out = String::new();
for attr in &field.attrs {
let Meta::NameValue(MetaNameValue {
path,
value,
..
}) = &attr.meta
else {
continue;
};
if !path
.segments
.iter()
.next()
.is_some_and(|s| s.ident == "doc")
{
continue;
}
let Expr::Lit(ExprLit {
lit,
..
}) = &value
else {
continue;
};
let Lit::Str(token) = &lit else {
continue;
};
writeln!(&mut out, "# {}", token.value()).expect("wrote to output string buffer");
}
(!out.is_empty()).then_some(out)
}
fn get_type_name(field: &Field) -> Option<String> {
let Type::Path(TypePath {
path,
..
}) = &field.ty
else {
return None;
};
path.segments
.iter()
.next()
.map(|segment| segment.ident.to_string())
}