use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
use crate::parse::{BorderInheritanceKind, FieldMeta, InheritMeta, LayerMeta};
pub(crate) fn gen_inherit(
opt_name: &Ident,
fields: &[FieldMeta],
_layer: &LayerMeta,
) -> TokenStream {
let stmts = gen_inherit_stmts(fields);
quote! {
impl #opt_name {
pub(crate) fn resolve_from_defaults(&mut self, defaults: &crate::model::ThemeDefaults) {
#stmts
}
}
}
}
fn gen_inherit_stmts(fields: &[FieldMeta]) -> TokenStream {
let mut stmts = Vec::new();
for f in fields {
if let Some(ref source) = f.inherit_from {
let ident = &f.ident;
let accessor = parse_defaults_path(source);
stmts.push(quote! {
if self.#ident.is_none() {
self.#ident = #accessor;
}
});
}
}
quote! { #(#stmts)* }
}
fn parse_defaults_path(path: &str) -> TokenStream {
let stripped = path.strip_prefix("defaults.").unwrap_or(path);
let segments: Vec<Ident> = stripped
.split('.')
.map(|s| format_ident!("{}", s))
.collect();
quote! { defaults.#(#segments).* }
}
fn widget_name_from_ident(opt_name: &Ident) -> String {
let s = opt_name.to_string();
let base = s.strip_suffix("Theme").unwrap_or(&s);
let mut out = String::with_capacity(base.len().saturating_add(4));
for (i, ch) in base.chars().enumerate() {
if ch.is_uppercase() && i > 0 {
out.push('_');
}
out.push(ch.to_ascii_lowercase());
}
out
}
pub(crate) fn gen_border_inherit(opt_name: &Ident, inherit: &InheritMeta) -> TokenStream {
let Some(kind) = inherit.border_kind else {
return TokenStream::new();
};
let widget_name = widget_name_from_ident(opt_name);
let kind_str = match kind {
BorderInheritanceKind::Full => "full",
BorderInheritanceKind::FullLg => "full_lg",
BorderInheritanceKind::Partial => "partial",
};
let body = match kind {
BorderInheritanceKind::Full => quote! {
let border = self
.border
.get_or_insert_with(crate::model::border::WidgetBorderSpec::default);
if border.color.is_none() {
border.color = defaults_border.color;
}
if border.corner_radius.is_none() {
border.corner_radius = defaults_border.corner_radius;
}
if border.line_width.is_none() {
border.line_width = defaults_border.line_width;
}
if border.shadow_enabled.is_none() {
border.shadow_enabled = defaults_border.shadow_enabled;
}
},
BorderInheritanceKind::FullLg => quote! {
let border = self
.border
.get_or_insert_with(crate::model::border::WidgetBorderSpec::default);
if border.color.is_none() {
border.color = defaults_border.color;
}
if border.corner_radius.is_none() {
border.corner_radius = defaults_border.corner_radius_lg;
}
if border.line_width.is_none() {
border.line_width = defaults_border.line_width;
}
if border.shadow_enabled.is_none() {
border.shadow_enabled = defaults_border.shadow_enabled;
}
},
BorderInheritanceKind::Partial => quote! {
let border = self
.border
.get_or_insert_with(crate::model::border::WidgetBorderSpec::default);
if border.color.is_none() {
border.color = defaults_border.color;
}
if border.line_width.is_none() {
border.line_width = defaults_border.line_width;
}
},
};
quote! {
impl #opt_name {
pub(crate) fn resolve_border_from_defaults(
&mut self,
defaults_border: &crate::model::border::DefaultsBorderSpec,
) {
#body
}
}
inventory::submit!(crate::resolve::BorderInheritanceInfo {
widget_name: #widget_name,
kind: #kind_str,
});
}
}
pub(crate) fn gen_font_inherit(opt_name: &Ident, inherit: &InheritMeta) -> TokenStream {
if inherit.font.is_empty() {
return TokenStream::new();
}
let widget_name = widget_name_from_ident(opt_name);
let blocks: Vec<TokenStream> = inherit
.font
.iter()
.map(|field_ident| {
quote! {
match &mut self.#field_ident {
None => {
self.#field_ident = Some(defaults_font.clone());
}
Some(font) => {
if font.family.is_none() {
font.family = defaults_font.family.clone();
}
if font.size.is_none() {
font.size = defaults_font.size;
}
if font.weight.is_none() {
font.weight = defaults_font.weight;
}
if font.style.is_none() {
font.style = defaults_font.style;
}
if font.color.is_none() {
font.color = defaults_font.color;
}
}
}
}
})
.collect();
let submissions: Vec<TokenStream> = inherit
.font
.iter()
.map(|field_ident| {
let field_str = field_ident.to_string();
quote! {
inventory::submit!(crate::resolve::FontInheritanceInfo {
widget_name: #widget_name,
font_field: #field_str,
});
}
})
.collect();
quote! {
impl #opt_name {
pub(crate) fn resolve_font_from_defaults(
&mut self,
defaults_font: &crate::model::FontSpec,
) {
#(#blocks)*
}
}
#(#submissions)*
}
}