Skip to main content

azul_layout/
fmt.rs

1//! C-compatible string formatting via `strfmt`.
2//!
3//! Provides [`FmtValue`], [`FmtArg`], and [`FmtArgVec`] for passing
4//! heterogeneous format arguments across FFI, and [`fmt_string`] as the
5//! main entry point. Used by `fluent.rs` and `icu.rs` for localization.
6
7use std::fmt;
8
9use azul_css::{AzString, StringVec, impl_option, impl_option_inner};
10
11/// A format argument value that can hold any primitive type or string.
12/// Used in [`FmtArg`] to pass typed values into `strfmt`-based formatting.
13#[derive(Debug, Clone, PartialEq, PartialOrd)]
14#[repr(C, u8)]
15pub enum FmtValue {
16    Bool(bool),
17    Uchar(u8),
18    Schar(i8),
19    Ushort(u16),
20    Sshort(i16),
21    Uint(u32),
22    Sint(i32),
23    Ulong(u64),
24    Slong(i64),
25    Isize(isize),
26    Usize(usize),
27    Float(f32),
28    Double(f64),
29    Str(AzString),
30    StrVec(StringVec),
31}
32
33impl strfmt::DisplayStr for FmtValue {
34    fn display_str(&self, f: &mut strfmt::Formatter<'_, '_>) -> strfmt::Result<()> {
35        use strfmt::DisplayStr;
36        match self {
37            FmtValue::Bool(v) => format!("{v}").display_str(f),
38            FmtValue::Uchar(v) => v.display_str(f),
39            FmtValue::Schar(v) => v.display_str(f),
40            FmtValue::Ushort(v) => v.display_str(f),
41            FmtValue::Sshort(v) => v.display_str(f),
42            FmtValue::Uint(v) => v.display_str(f),
43            FmtValue::Sint(v) => v.display_str(f),
44            FmtValue::Ulong(v) => v.display_str(f),
45            FmtValue::Slong(v) => v.display_str(f),
46            FmtValue::Isize(v) => v.display_str(f),
47            FmtValue::Usize(v) => v.display_str(f),
48            FmtValue::Float(v) => v.display_str(f),
49            FmtValue::Double(v) => v.display_str(f),
50            FmtValue::Str(v) => v.as_str().display_str(f),
51            FmtValue::StrVec(sv) => {
52                "[".display_str(f)?;
53                for (i, s) in sv.as_ref().iter().enumerate() {
54                    if i != 0 {
55                        ", ".display_str(f)?;
56                    }
57                    s.as_str().display_str(f)?;
58                }
59                "]".display_str(f)?;
60                Ok(())
61            }
62        }
63    }
64}
65
66impl fmt::Display for FmtValue {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        match self {
69            FmtValue::Bool(v) => v.fmt(f),
70            FmtValue::Uchar(v) => v.fmt(f),
71            FmtValue::Schar(v) => v.fmt(f),
72            FmtValue::Ushort(v) => v.fmt(f),
73            FmtValue::Sshort(v) => v.fmt(f),
74            FmtValue::Uint(v) => v.fmt(f),
75            FmtValue::Sint(v) => v.fmt(f),
76            FmtValue::Ulong(v) => v.fmt(f),
77            FmtValue::Slong(v) => v.fmt(f),
78            FmtValue::Isize(v) => v.fmt(f),
79            FmtValue::Usize(v) => v.fmt(f),
80            FmtValue::Float(v) => v.fmt(f),
81            FmtValue::Double(v) => v.fmt(f),
82            FmtValue::Str(v) => v.as_str().fmt(f),
83            FmtValue::StrVec(sv) => {
84                use std::fmt::Debug;
85                let vec: Vec<&str> = sv.as_ref().iter().map(|s| s.as_str()).collect();
86                vec.fmt(f)
87            }
88        }
89    }
90}
91
92/// A key-value pair mapping a format placeholder name to its value.
93#[derive(Debug, Clone, PartialEq, PartialOrd)]
94#[repr(C)]
95pub struct FmtArg {
96    pub key: AzString,
97    pub value: FmtValue,
98}
99
100azul_css::impl_option!(FmtArg, OptionFmtArg, copy = false, [Debug, Clone, PartialEq, PartialOrd]);
101azul_css::impl_vec!(FmtArg, FmtArgVec, FmtArgVecDestructor, FmtArgVecDestructorType, FmtArgVecSlice, OptionFmtArg);
102azul_css::impl_vec_clone!(FmtArg, FmtArgVec, FmtArgVecDestructor);
103azul_css::impl_vec_debug!(FmtArg, FmtArgVec);
104azul_css::impl_vec_partialeq!(FmtArg, FmtArgVec);
105azul_css::impl_vec_partialord!(FmtArg, FmtArgVec);
106
107/// Formats `format` by substituting placeholders with values from `args`.
108/// Returns the error message as a string on failure (for C FFI ergonomics).
109pub fn fmt_string(format: AzString, args: FmtArgVec) -> String {
110    use strfmt::Format;
111    let format_map = args
112        .iter()
113        .map(|a| (a.key.clone().into_library_owned_string(), a.value.clone()))
114        .collect();
115    match format.as_str().format(&format_map) {
116        Ok(o) => o,
117        Err(e) => format!("{}", e),
118    }
119}