dampen_core/codegen/
application.rs1use proc_macro2::TokenStream;
4use quote::quote;
5
6use super::CodegenError;
7use super::theme::generate_theme_code;
8use crate::ir::theme::{StyleClass, ThemeDocument};
9use std::collections::HashMap;
10
11pub fn generate_application_trait(
13 model_name: &str,
14 message_name: &str,
15) -> Result<TokenStream, super::CodegenError> {
16 let model_ident = syn::Ident::new(model_name, proc_macro2::Span::call_site());
17 let message_ident = syn::Ident::new(message_name, proc_macro2::Span::call_site());
18
19 Ok(quote! {
20 impl iced::Application for #model_ident {
21 type Executor = iced::executor::Default;
22 type Flags = ();
23 type Message = #message_ident;
24
25 fn new(_flags: ()) -> (Self, iced::Task<Self::Message>) {
26 (Self::default(), iced::Task::none())
27 }
28
29 fn title(&self) -> String {
30 "Dampen Application".to_string()
31 }
32 }
33 })
34}
35
36pub fn generate_application_with_theme(
39 model_name: &str,
40 message_name: &str,
41 theme_document: Option<&ThemeDocument>,
42 style_classes: &HashMap<String, StyleClass>,
43) -> Result<TokenStream, CodegenError> {
44 let model_ident = syn::Ident::new(model_name, proc_macro2::Span::call_site());
45 let message_ident = syn::Ident::new(message_name, proc_macro2::Span::call_site());
46
47 let theme_code = if let Some(doc) = theme_document {
48 match generate_theme_code(doc, style_classes, "app") {
49 Ok(generated) => generated.code,
50 Err(e) => {
51 return Err(CodegenError::ThemeError(e));
52 }
53 }
54 } else {
55 String::new()
56 };
57
58 Ok(quote! {
59 #theme_code
60
61 impl iced::Application for #model_ident {
62 type Executor = iced::executor::Default;
63 type Flags = ();
64 type Message = #message_ident;
65
66 fn new(_flags: ()) -> (Self, iced::Task<Self::Message>) {
67 (Self::default(), iced::Task::none())
68 }
69
70 fn title(&self) -> String {
71 "Dampen Application".to_string()
72 }
73
74 fn theme(&self) -> iced::Theme {
75 app_theme()
76 }
77 }
78 })
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use crate::ir::style::Color;
85 use crate::ir::theme::{SpacingScale, Theme, ThemeDocument, ThemePalette, Typography};
86
87 fn create_test_palette() -> ThemePalette {
88 ThemePalette {
89 primary: Some(Color::from_hex("#3498db").unwrap()),
90 secondary: Some(Color::from_hex("#2ecc71").unwrap()),
91 success: Some(Color::from_hex("#27ae60").unwrap()),
92 warning: Some(Color::from_hex("#f39c12").unwrap()),
93 danger: Some(Color::from_hex("#e74c3c").unwrap()),
94 background: Some(Color::from_hex("#ecf0f1").unwrap()),
95 surface: Some(Color::from_hex("#ffffff").unwrap()),
96 text: Some(Color::from_hex("#2c3e50").unwrap()),
97 text_secondary: Some(Color::from_hex("#7f8c8d").unwrap()),
98 }
99 }
100
101 fn create_test_theme(name: &str) -> Theme {
102 Theme {
103 name: name.to_string(),
104 palette: create_test_palette(),
105 typography: Typography {
106 font_family: Some("sans-serif".to_string()),
107 font_size_base: Some(16.0),
108 font_size_small: Some(12.0),
109 font_size_large: Some(24.0),
110 font_weight: crate::ir::theme::FontWeight::Normal,
111 line_height: Some(1.5),
112 },
113 spacing: SpacingScale { unit: Some(8.0) },
114 base_styles: std::collections::HashMap::new(),
115 extends: None,
116 }
117 }
118
119 #[test]
120 fn test_application_trait_generation() {
121 let result = generate_application_trait("MyModel", "MyMessage").unwrap();
122 let code = result.to_string();
123
124 assert!(code.contains("impl") && code.contains("Application") && code.contains("MyModel"));
125 assert!(code.contains("Message") && code.contains("MyMessage"));
126 }
127
128 #[test]
129 fn test_application_with_theme_generation() {
130 let doc = ThemeDocument {
131 themes: std::collections::HashMap::from([(
132 "light".to_string(),
133 create_test_theme("light"),
134 )]),
135 default_theme: Some("light".to_string()),
136 follow_system: true,
137 };
138
139 let style_classes = HashMap::new();
140 let result =
141 generate_application_with_theme("MyModel", "MyMessage", Some(&doc), &style_classes);
142 assert!(result.is_ok());
143 let code = result.unwrap().to_string();
144
145 assert!(code.contains("impl") && code.contains("Application"));
146 assert!(code.contains("app_theme()"));
147 assert!(code.contains("fn app_light()"));
148 }
149
150 #[test]
151 fn test_application_without_theme() {
152 let style_classes = HashMap::new();
153 let result = generate_application_with_theme("MyModel", "MyMessage", None, &style_classes);
154 assert!(result.is_ok());
155 let code = result.unwrap().to_string();
156
157 assert!(code.contains("impl") && code.contains("Application"));
158 }
159}