orbital-macros 0.1.1

Proc macros for Orbital component documentation and routing helpers
Documentation
use quote::quote;
use syn::{Data, DeriveInput, Fields};

pub(crate) fn expand_write_css_vars(input: DeriveInput) -> proc_macro2::TokenStream {
    let struct_name = &input.ident;
    let data = match input.data {
        Data::Struct(data) => data,
        _ => panic!("Expected a struct!"),
    };

    let mut css_var_names = vec![];
    let mut field_names = vec![];
    match data.fields {
        Fields::Named(fields) => {
            for field in fields.named {
                let field_name = field.ident.unwrap();
                css_var_names.push(format!(
                    "--{}: {{}};",
                    to_camel_case(field_name.to_string())
                ));
                field_names.push(field_name);
            }
        }
        _ => panic!("Expected named fields!"),
    };

    let field_names = field_names.iter();
    quote! {
        impl #struct_name {
            pub fn write_css_vars(&self, css_vars: &mut String) {
                #(css_vars.push_str(&format!(#css_var_names, self.#field_names));)*
            }
        }
    }
}

fn to_camel_case(s: String) -> String {
    let mut camel_case = String::new();
    let mut capitalize_next = false;
    for c in s.chars() {
        if c.is_alphanumeric() {
            if capitalize_next {
                camel_case.push(c.to_ascii_uppercase());
                capitalize_next = false;
            } else {
                camel_case.push(c.to_ascii_lowercase());
            }
        } else {
            capitalize_next = true;
        }
    }
    camel_case
}

#[cfg(test)]
mod tests {
    use super::to_camel_case;

    #[test]
    fn camel_case_converts_snake_case() {
        assert_eq!(to_camel_case("font_family_base".into()), "fontFamilyBase");
    }
}