serde_format/
lib.rs

1//! # serde-format
2//!
3//! A tiny trait to format a serializable struct using custom placeholders.
4//!
5//! ## Goals
6//!
7//! - Be as lightweight as possible
8//! - Have no dependencies other than [serde] and [serde_json]
9//!
10//! ## Non-goals
11//!
12//! - Prioritize performance
13//! - Support any syntax beyond variable substitution
14//!
15//! ## Usage
16//!
17//! ```
18//! use serde::Serialize;
19//! use serde_format::Format;
20//!
21//! #[derive(Serialize)]
22//! struct Foo {
23//!     name: String
24//! }
25//!
26//! impl Format for Foo {}
27//!
28//! let foo = Foo { name: "Bar".into() };
29//! assert_eq!(foo.format("Hey, {{name}}!"), "Hey, Bar!");
30//! ```
31
32#![warn(clippy::all, missing_docs, nonstandard_style, future_incompatible)]
33
34use serde::Serialize;
35use serde_json::Value;
36use std::collections::HashMap;
37
38/// A simple formatter with customizable placeholders
39pub trait Format {
40    /// Left and right placeholders for variables
41    const PLACEHOLDERS: (&'static str, &'static str) = ("{{", "}}");
42
43    /// Formats the struct using the template
44    fn format(&self, template: impl Into<String>) -> String
45    where
46        Self: Serialize,
47    {
48        let mut result = template.into();
49        let data_map: HashMap<String, Value> =
50            serde_json::from_value(serde_json::to_value(self).unwrap_or_default())
51                .unwrap_or_default();
52        let (left, right) = Self::PLACEHOLDERS;
53        for (key, value) in data_map.iter() {
54            let placeholder = format!("{left}{key}{right}");
55            result = result.replace(
56                &placeholder,
57                &value
58                    .as_str()
59                    .map(ToOwned::to_owned)
60                    .unwrap_or_else(|| value.to_string()),
61            );
62        }
63        result
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn default_placeholders() {
73        #[derive(Serialize)]
74        struct Foo {
75            s: String,
76            n: u32,
77            b: bool,
78        }
79
80        impl Format for Foo {}
81
82        let foo = Foo {
83            s: "hey".into(),
84            n: 1,
85            b: true,
86        };
87
88        assert_eq!(foo.format("s={{s}} n={{n}} b={{b}}"), "s=hey n=1 b=true");
89    }
90
91    #[test]
92    fn custom_placeholders() {
93        #[derive(Serialize)]
94        struct Foo {
95            s: String,
96            n: u32,
97            b: bool,
98        }
99
100        impl Format for Foo {
101            const PLACEHOLDERS: (&'static str, &'static str) = ("${", "}");
102        }
103
104        let foo = Foo {
105            s: "hey".into(),
106            n: 1,
107            b: true,
108        };
109
110        assert_eq!(foo.format("s=${s} n=${n} b=${b}"), "s=hey n=1 b=true");
111    }
112}