use proc_macro2::TokenStream;
use quote::quote;
use crate::ir::theme::ThemeDocument;
#[derive(Debug, Clone)]
pub struct SubscriptionConfig {
pub system_theme: bool,
pub message_name: String,
pub system_theme_variant: Option<String>,
}
impl Default for SubscriptionConfig {
fn default() -> Self {
Self {
system_theme: false,
message_name: "Message".to_string(),
system_theme_variant: None,
}
}
}
impl SubscriptionConfig {
pub fn from_theme_document(theme_doc: Option<&ThemeDocument>, message_name: &str) -> Self {
let system_theme = theme_doc.map(|doc| doc.follow_system).unwrap_or(false);
Self {
system_theme,
message_name: message_name.to_string(),
system_theme_variant: if system_theme {
Some("SystemThemeChanged".to_string())
} else {
None
},
}
}
pub fn with_system_theme_variant(mut self, variant: impl Into<String>) -> Self {
self.system_theme_variant = Some(variant.into());
self.system_theme = true;
self
}
}
pub fn generate_subscription_function(config: &SubscriptionConfig) -> TokenStream {
let message_ident = syn::Ident::new(&config.message_name, proc_macro2::Span::call_site());
if let Some(ref variant_name) = config.system_theme_variant {
let variant_ident = syn::Ident::new(variant_name, proc_macro2::Span::call_site());
quote! {
pub fn subscription_model() -> iced::Subscription<#message_ident> {
if app_follows_system() {
dampen_iced::watch_system_theme()
.map(#message_ident::#variant_ident)
} else {
iced::Subscription::none()
}
}
}
} else {
quote! {
pub fn subscription_model() -> iced::Subscription<#message_ident> {
iced::Subscription::none()
}
}
}
}
pub fn generate_system_theme_variant(config: &SubscriptionConfig) -> Option<TokenStream> {
config.system_theme_variant.as_ref().map(|variant_name| {
let variant_ident = syn::Ident::new(variant_name, proc_macro2::Span::call_site());
quote! {
#variant_ident(String)
}
})
}
pub fn generate_system_theme_update_arm(config: &SubscriptionConfig) -> Option<TokenStream> {
let message_ident = syn::Ident::new(&config.message_name, proc_macro2::Span::call_site());
config.system_theme_variant.as_ref().map(|variant_name| {
let variant_ident = syn::Ident::new(variant_name, proc_macro2::Span::call_site());
quote! {
#message_ident::#variant_ident(theme_name) => {
app_set_current_theme(&theme_name);
iced::Task::none()
}
}
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::theme::{SpacingScale, Theme, ThemeDocument, ThemePalette, Typography};
use std::collections::HashMap;
fn create_test_theme_document(follow_system: bool) -> ThemeDocument {
let mut themes = HashMap::new();
themes.insert(
"light".to_string(),
Theme {
name: "light".to_string(),
palette: ThemePalette::light(),
typography: Typography {
font_family: None,
font_size_base: Some(16.0),
font_size_small: Some(12.0),
font_size_large: Some(24.0),
font_weight: crate::ir::theme::FontWeight::Normal,
line_height: Some(1.5),
},
spacing: SpacingScale { unit: Some(8.0) },
base_styles: HashMap::new(),
extends: None,
},
);
ThemeDocument {
themes,
default_theme: Some("light".to_string()),
follow_system,
}
}
#[test]
fn test_subscription_config_from_theme_document() {
let doc = create_test_theme_document(true);
let config = SubscriptionConfig::from_theme_document(Some(&doc), "Message");
assert!(config.system_theme);
assert_eq!(
config.system_theme_variant,
Some("SystemThemeChanged".to_string())
);
}
#[test]
fn test_subscription_config_no_follow_system() {
let doc = create_test_theme_document(false);
let config = SubscriptionConfig::from_theme_document(Some(&doc), "Message");
assert!(!config.system_theme);
assert_eq!(config.system_theme_variant, None);
}
#[test]
fn test_generate_subscription_function_with_system_theme() {
let config = SubscriptionConfig {
system_theme: true,
message_name: "Message".to_string(),
system_theme_variant: Some("SystemThemeChanged".to_string()),
};
let tokens = generate_subscription_function(&config);
let code = tokens.to_string();
assert!(code.contains("subscription_model"), "code: {}", code);
assert!(code.contains("app_follows_system"), "code: {}", code);
assert!(code.contains("watch_system_theme"), "code: {}", code);
assert!(code.contains("SystemThemeChanged"), "code: {}", code);
}
#[test]
fn test_generate_subscription_function_without_system_theme() {
let config = SubscriptionConfig::default();
let tokens = generate_subscription_function(&config);
let code = tokens.to_string();
assert!(code.contains("subscription_model"), "code: {}", code);
assert!(
code.contains("Subscription") && code.contains("none"),
"code: {}",
code
);
assert!(!code.contains("watch_system_theme"), "code: {}", code);
}
#[test]
fn test_generate_system_theme_variant() {
let config = SubscriptionConfig {
system_theme: true,
message_name: "Message".to_string(),
system_theme_variant: Some("SystemThemeChanged".to_string()),
};
let tokens = generate_system_theme_variant(&config);
assert!(tokens.is_some());
let code = tokens.unwrap().to_string();
assert!(code.contains("SystemThemeChanged"));
assert!(code.contains("String"));
}
#[test]
fn test_generate_system_theme_update_arm() {
let config = SubscriptionConfig {
system_theme: true,
message_name: "Message".to_string(),
system_theme_variant: Some("SystemThemeChanged".to_string()),
};
let tokens = generate_system_theme_update_arm(&config);
assert!(tokens.is_some());
let code = tokens.unwrap().to_string();
assert!(
code.contains("Message") && code.contains("SystemThemeChanged"),
"code: {}",
code
);
assert!(code.contains("theme_name"), "code: {}", code);
assert!(
code.contains("Task") && code.contains("none"),
"code: {}",
code
);
}
}