#![warn(clippy::all, missing_docs, nonstandard_style, future_incompatible)]
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
pub trait Format {
const PLACEHOLDERS: (&'static str, &'static str) = ("{{", "}}");
fn format(&self, template: impl Into<String>) -> String
where
Self: Serialize,
{
let mut result = template.into();
let data_map: HashMap<String, Value> =
serde_json::from_value(serde_json::to_value(self).unwrap_or_default())
.unwrap_or_default();
let (left, right) = Self::PLACEHOLDERS;
for (key, value) in data_map.iter() {
let placeholder = format!("{left}{key}{right}");
result = result.replace(
&placeholder,
&value
.as_str()
.map(ToOwned::to_owned)
.unwrap_or_else(|| value.to_string()),
);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_placeholders() {
#[derive(Serialize)]
struct Foo {
s: String,
n: u32,
b: bool,
}
impl Format for Foo {}
let foo = Foo {
s: "hey".into(),
n: 1,
b: true,
};
assert_eq!(foo.format("s={{s}} n={{n}} b={{b}}"), "s=hey n=1 b=true");
}
#[test]
fn custom_placeholders() {
#[derive(Serialize)]
struct Foo {
s: String,
n: u32,
b: bool,
}
impl Format for Foo {
const PLACEHOLDERS: (&'static str, &'static str) = ("${", "}");
}
let foo = Foo {
s: "hey".into(),
n: 1,
b: true,
};
assert_eq!(foo.format("s=${s} n=${n} b=${b}"), "s=hey n=1 b=true");
}
}