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            Self::Bool(v) => format!("{v}").display_str(f),
38            Self::Uchar(v) => v.display_str(f),
39            Self::Schar(v) => v.display_str(f),
40            Self::Ushort(v) => v.display_str(f),
41            Self::Sshort(v) => v.display_str(f),
42            Self::Uint(v) => v.display_str(f),
43            Self::Sint(v) => v.display_str(f),
44            Self::Ulong(v) => v.display_str(f),
45            Self::Slong(v) => v.display_str(f),
46            Self::Isize(v) => v.display_str(f),
47            Self::Usize(v) => v.display_str(f),
48            Self::Float(v) => v.display_str(f),
49            Self::Double(v) => v.display_str(f),
50            Self::Str(v) => v.as_str().display_str(f),
51            Self::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            Self::Bool(v) => v.fmt(f),
70            Self::Uchar(v) => v.fmt(f),
71            Self::Schar(v) => v.fmt(f),
72            Self::Ushort(v) => v.fmt(f),
73            Self::Sshort(v) => v.fmt(f),
74            Self::Uint(v) => v.fmt(f),
75            Self::Sint(v) => v.fmt(f),
76            Self::Ulong(v) => v.fmt(f),
77            Self::Slong(v) => v.fmt(f),
78            Self::Isize(v) => v.fmt(f),
79            Self::Usize(v) => v.fmt(f),
80            Self::Float(v) => v.fmt(f),
81            Self::Double(v) => v.fmt(f),
82            Self::Str(v) => v.as_str().fmt(f),
83            Self::StrVec(sv) => {
84                use std::fmt::Debug;
85                let vec: Vec<&str> = sv.as_ref().iter().map(AzString::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).
109// FFI-exported formatter: owned AzString/FmtArgVec args are the api.json signature.
110#[allow(clippy::needless_pass_by_value)]
111#[must_use] pub fn fmt_string(format: AzString, args: FmtArgVec) -> String {
112    use strfmt::Format;
113    let format_map = args
114        .iter()
115        .map(|a| (a.key.clone().into_library_owned_string(), a.value.clone()))
116        .collect();
117    match format.as_str().format(&format_map) {
118        Ok(o) => o,
119        Err(e) => format!("{e}"),
120    }
121}