initial example-config generator
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
c40d20cb95
commit
2a59a56eaa
3 changed files with 107 additions and 1 deletions
|
@ -5,6 +5,7 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use conduit_macros::config_example_generator;
|
||||||
use either::{
|
use either::{
|
||||||
Either,
|
Either,
|
||||||
Either::{Left, Right},
|
Either::{Left, Right},
|
||||||
|
@ -27,6 +28,7 @@ pub mod check;
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
|
|
||||||
/// all the config options for conduwuit
|
/// all the config options for conduwuit
|
||||||
|
#[config_example_generator]
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|
98
src/macros/config.rs
Normal file
98
src/macros/config.rs
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
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())
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
mod admin;
|
mod admin;
|
||||||
mod cargo;
|
mod cargo;
|
||||||
|
mod config;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod implement;
|
mod implement;
|
||||||
mod refutable;
|
mod refutable;
|
||||||
|
@ -9,7 +10,7 @@ mod utils;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, Parser},
|
parse::{Parse, Parser},
|
||||||
parse_macro_input, Error, Item, ItemConst, ItemEnum, ItemFn, Meta,
|
parse_macro_input, Error, Item, ItemConst, ItemEnum, ItemFn, ItemStruct, Meta,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -47,6 +48,11 @@ pub fn implement(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
attribute_macro::<ItemFn, _>(args, input, implement::implement)
|
attribute_macro::<ItemFn, _>(args, input, implement::implement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn config_example_generator(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
attribute_macro::<ItemStruct, _>(args, input, config::example_generator)
|
||||||
|
}
|
||||||
|
|
||||||
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
|
fn attribute_macro<I, F>(args: TokenStream, input: TokenStream, func: F) -> TokenStream
|
||||||
where
|
where
|
||||||
F: Fn(I, &[Meta]) -> Result<TokenStream>,
|
F: Fn(I, &[Meta]) -> Result<TokenStream>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue