Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

upon/
fmt.rs

1//! Types for value formatters.
2//!
3//! Value formatters allow you to change the way a [`Value`] is formatted in the
4//! rendered template. They can be configured on the engine using
5//! [`set_default_formatter`][crate::Engine::set_default_formatter] or
6//! [`add_formatter`][crate::Engine::add_formatter].
7//!
8//! This module defines a [`Formatter`] type that is similar to
9//! [`std::fmt::Formatter`] so it should be a familiar API. A mutable reference
10//! to this struct is passed to formatter functions and writing to it will
11//! update the underlying buffer, be it a [`String`] or an arbitrary
12//! [`std::io::Write`] buffer.
13//!
14//! All formatter functions must have the following signature.
15//!
16//! ```text
17//! use upon::{Value, fmt};
18//! Fn(&mut fmt::Formatter<'_>, &Value) -> fmt::Result;
19//! ```
20//!
21//! Since [`Error`] implements `From<String>` and `From<&str>` it is possible
22//! to return custom messages from formatter functions. You can also easily
23//! propagate the standard library [`std::fmt::Error`].
24//!
25//! # Examples
26//!
27//! ### Escape ASCII
28//!
29//! Consider a use case where you want to escape all non-ascii characters in
30//! strings. We could define a value formatter for that using the standard
31//! library function [`escape_ascii`][slice::escape_ascii].
32//!
33//! ```
34//! use std::fmt::Write;
35//! use upon::{fmt, Engine, Value};
36//!
37//! fn escape_ascii(f: &mut fmt::Formatter<'_>, value: &Value) -> fmt::Result {
38//!     match value {
39//!         Value::String(s) => write!(f, "{}", s.as_bytes().escape_ascii())?,
40//!         v => fmt::default(f, v)?, // fallback to default formatter
41//!     };
42//!     Ok(())
43//! }
44//!
45//! let mut engine = Engine::new();
46//! engine.add_formatter("escape_ascii", escape_ascii);
47//! ```
48//!
49//! We could then use this this formatter in templates like this.
50//!
51//! ```text
52//! {{ user.name | escape_ascii }}
53//! ```
54//!
55//! ### Error on [`Value::None`]
56//!
57//! The [`default`] value formatter formats [`Value::None`] as an empty string.
58//! This example demonstrates how you can configure a default formatter to error
59//! instead.
60//!
61//! ```
62//! use std::fmt::Write;
63//! use upon::{fmt, Engine, Value};
64//!
65//! fn error_on_none(f: &mut fmt::Formatter<'_>, value: &Value) -> fmt::Result {
66//!     match value {
67//!         Value::None => Err(fmt::Error::from("unable to format None")),
68//!         v => fmt::default(f, v), // fallback to default formatter
69//!     }
70//! }
71//!
72//! let mut engine = Engine::new();
73//! engine.set_default_formatter(&error_on_none);
74//! ```
75
76use std::fmt;
77use std::fmt::Write;
78use std::io;
79
80use crate::Value;
81
82/// A formatter function or closure.
83pub(crate) type DynFormatter = dyn Fn(&mut Formatter<'_>, &Value) -> Result + Sync + Send + 'static;
84
85/// A [`std::fmt::Write`] façade.
86pub struct Formatter<'a> {
87    buf: &'a mut (dyn fmt::Write + 'a),
88}
89
90/// The result type returned from a formatter function.
91pub type Result = std::result::Result<(), Error>;
92
93/// The error type returned from a formatter function.
94#[derive(Debug, Clone)]
95pub struct Error(Option<String>);
96
97pub(crate) struct Writer<W> {
98    writer: W,
99    err: Option<io::Error>,
100}
101
102impl<'a> Formatter<'a> {
103    pub(crate) fn with_string(buf: &'a mut String) -> Self {
104        Self { buf }
105    }
106
107    pub(crate) fn with_writer<W>(buf: &'a mut Writer<W>) -> Self
108    where
109        W: io::Write,
110    {
111        Self { buf }
112    }
113}
114
115impl fmt::Write for Formatter<'_> {
116    #[inline]
117    fn write_str(&mut self, s: &str) -> fmt::Result {
118        fmt::Write::write_str(self.buf, s)
119    }
120
121    #[inline]
122    fn write_char(&mut self, c: char) -> fmt::Result {
123        fmt::Write::write_char(self.buf, c)
124    }
125
126    #[inline]
127    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
128        fmt::Write::write_fmt(self.buf, args)
129    }
130}
131
132impl Error {
133    pub(crate) fn message(self) -> Option<String> {
134        self.0
135    }
136}
137
138impl std::error::Error for Error {}
139
140impl std::fmt::Display for Error {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        match &self.0 {
143            Some(msg) => write!(f, "{msg}"),
144            None => write!(f, "format error"),
145        }
146    }
147}
148
149impl From<&str> for Error {
150    fn from(msg: &str) -> Self {
151        Self(Some(msg.to_owned()))
152    }
153}
154
155impl From<String> for Error {
156    fn from(msg: String) -> Self {
157        Self(Some(msg))
158    }
159}
160
161impl From<fmt::Error> for Error {
162    fn from(_: fmt::Error) -> Self {
163        Self(None)
164    }
165}
166
167impl<W> Writer<W>
168where
169    W: io::Write,
170{
171    pub fn new(writer: W) -> Self {
172        Self { writer, err: None }
173    }
174
175    pub fn take_err(&mut self) -> Option<io::Error> {
176        self.err.take()
177    }
178}
179
180impl<W> fmt::Write for Writer<W>
181where
182    W: io::Write,
183{
184    #[inline]
185    fn write_str(&mut self, s: &str) -> fmt::Result {
186        self.writer.write_all(s.as_bytes()).map_err(|e| {
187            self.err = Some(e);
188            fmt::Error
189        })
190    }
191
192    #[inline]
193    fn write_char(&mut self, c: char) -> fmt::Result {
194        self.writer
195            .write_all(c.encode_utf8(&mut [0; 4]).as_bytes())
196            .map_err(|e| {
197                self.err = Some(e);
198                fmt::Error
199            })
200    }
201}
202
203/// The default value formatter.
204///
205/// Values are formatted as follows:
206/// - [`Value::None`]: empty string
207/// - [`Value::Bool`]: `true` or `false`
208/// - [`Value::Integer`]: the integer formatted using [`Display`][std::fmt::Display]
209/// - [`Value::Float`]: the float formatted using [`Display`][std::fmt::Display]
210/// - [`Value::String`]: the string, unescaped
211///
212/// Errors if the value is a [`Value::List`] or [`Value::Map`].
213#[inline]
214pub fn default(f: &mut Formatter<'_>, value: &Value) -> Result {
215    match value {
216        Value::None => {}
217        Value::Bool(b) => write!(f, "{b}")?,
218        Value::Integer(n) => write!(f, "{n}")?,
219        Value::Float(n) => write!(f, "{n}")?,
220        Value::String(s) => write!(f, "{s}")?,
221        value => {
222            return Err(Error::from(format!(
223                "expression evaluated to unformattable type {}",
224                value.human()
225            )));
226        }
227    }
228    Ok(())
229}