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

            
7
use std::fmt;
8

            
9
use 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)]
15
pub 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

            
33
impl 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

            
66
impl 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)]
95
pub struct FmtArg {
96
    pub key: AzString,
97
    pub value: FmtValue,
98
}
99

            
100
azul_css::impl_option!(FmtArg, OptionFmtArg, copy = false, [Debug, Clone, PartialEq, PartialOrd]);
101
azul_css::impl_vec!(FmtArg, FmtArgVec, FmtArgVecDestructor, FmtArgVecDestructorType, FmtArgVecSlice, OptionFmtArg);
102
azul_css::impl_vec_clone!(FmtArg, FmtArgVec, FmtArgVecDestructor);
103
azul_css::impl_vec_debug!(FmtArg, FmtArgVec);
104
azul_css::impl_vec_partialeq!(FmtArg, FmtArgVec);
105
azul_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
pub 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
}