winrt_xaml_macros/
lib.rs

1//! Procedural macros for compile-time XAML parsing.
2//!
3//! This crate provides the `xaml!` macro which parses XAML at compile time
4//! and generates Rust code to create WinRT controls.
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{parse_macro_input, LitStr};
9use quick_xml::events::{Event, BytesStart};
10use quick_xml::Reader;
11use std::str;
12
13mod codegen;
14use codegen::*;
15
16/// Compile-time XAML parsing macro.
17///
18/// Parses XAML markup at compile time and generates Rust code to create
19/// the corresponding WinRT controls.
20///
21/// # Example
22///
23/// ```rust,ignore
24/// use winrt_xaml::xaml;
25///
26/// let button = xaml! {
27///     <Button Content="Click Me"
28///             Width="200"
29///             Height="50"
30///             Background="#FF0078D4"
31///             Foreground="#FFFFFFFF"
32///             CornerRadius="8" />
33/// };
34/// ```
35///
36/// This expands to:
37///
38/// ```rust,ignore
39/// {
40///     let button = XamlButton::new()?;
41///     button.set_content("Click Me")?;
42///     button.set_size(200.0, 50.0)?;
43///     button.set_background(0xFF0078D4)?;
44///     button.set_foreground(0xFFFFFFFF)?;
45///     button.set_corner_radius(8.0)?;
46///     button.as_uielement()
47/// }
48/// ```
49#[proc_macro]
50pub fn xaml(input: TokenStream) -> TokenStream {
51    let input_str = parse_macro_input!(input as LitStr);
52    let xaml_content = input_str.value();
53
54    // Parse XAML at compile time
55    match parse_xaml(&xaml_content) {
56        Ok(code) => code.into(),
57        Err(e) => {
58            let error_msg = format!("XAML parse error: {}", e);
59            quote! {
60                compile_error!(#error_msg)
61            }
62            .into()
63        }
64    }
65}
66
67/// Parse XAML and generate Rust code.
68fn parse_xaml(xaml: &str) -> Result<proc_macro2::TokenStream, String> {
69    let mut reader = Reader::from_str(xaml);
70    reader.config_mut().trim_text(true);
71
72    let mut buf = Vec::new();
73
74    loop {
75        match reader.read_event_into(&mut buf) {
76            Ok(Event::Start(e)) | Ok(Event::Empty(e)) => {
77                let name_bytes = e.name();
78                let element_name = str::from_utf8(name_bytes.as_ref())
79                    .map_err(|e| format!("Invalid UTF-8: {}", e))?;
80
81                return generate_code_for_element(&e, element_name);
82            }
83            Ok(Event::Eof) => break,
84            Err(e) => return Err(format!("XML parse error: {}", e)),
85            _ => (),
86        }
87        buf.clear();
88    }
89
90    Err("No root element found".to_string())
91}
92
93/// Generate Rust code for a XAML element.
94fn generate_code_for_element(
95    element: &BytesStart,
96    element_name: &str,
97) -> Result<proc_macro2::TokenStream, String> {
98    match element_name {
99        "Button" => generate_button_code(element),
100        "TextBlock" => generate_textblock_code(element),
101        "TextBox" => generate_textbox_code(element),
102        "StackPanel" => generate_stackpanel_code(element),
103        "Grid" => generate_grid_code(element),
104        "ScrollViewer" => generate_scrollviewer_code(element),
105        _ => Err(format!("Unsupported XAML element: {}", element_name)),
106    }
107}