#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "std")]
mod alloc_impls;
#[cfg(feature = "std")]
pub use alloc_impls::*;
#[cfg(feature = "std")]
mod compiled;
#[cfg(feature = "std")]
pub use compiled::ParsedFmt;
mod parse;
pub use parse::{FromStr, ParseSegment};
use core::cell::Cell;
use core::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum FormatKeyError {
Fmt(fmt::Error),
UnknownKey,
}
#[cfg(feature = "std")]
impl std::error::Error for FormatKeyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FormatKeyError::Fmt(f) => Some(f),
FormatKeyError::UnknownKey => None,
}
}
}
impl fmt::Display for FormatKeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatKeyError::Fmt(_) => f.write_str("There was an error writing to the formatter"),
FormatKeyError::UnknownKey => f.write_str("The requested key is unknown"),
}
}
}
impl From<fmt::Error> for FormatKeyError {
fn from(value: fmt::Error) -> Self {
FormatKeyError::Fmt(value)
}
}
pub trait FormatKey {
fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError>;
}
pub trait ToFormatParser<'a> {
type Parser: Iterator<Item = ParseSegment<'a>>;
fn to_parser(&'a self) -> Self::Parser;
fn unparsed(iter: Self::Parser) -> &'a str;
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum FormatError<'a> {
Key(&'a str),
Parse(&'a str),
}
#[cfg(feature = "std")]
impl std::error::Error for FormatError<'_> {}
impl fmt::Display for FormatError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatError::Key(key) => write!(f, "The requested key {key:?} is unknown"),
FormatError::Parse(rest) => write!(f, "Failed to parse {rest:?}"),
}
}
}
pub struct FormatArgs<'fs, 'fk, FS: ?Sized, FK: ?Sized> {
format_segments: &'fs FS,
format_keys: &'fk FK,
error: Cell<Option<FormatError<'fs>>>,
}
impl<'fs, 'fk, FS: ?Sized, FK: ?Sized> FormatArgs<'fs, 'fk, FS, FK> {
pub fn new(format_specified: &'fs FS, format_keys: &'fk FK) -> Self {
FormatArgs {
format_segments: format_specified,
format_keys,
error: Cell::new(None),
}
}
pub fn status(&self) -> Result<(), FormatError<'fs>> {
match self.error.take() {
Some(err) => Err(err),
None => Ok(()),
}
}
}
impl<'fs, 'fk, FS, FK> fmt::Display for FormatArgs<'fs, 'fk, FS, FK>
where
FS: ?Sized + ToFormatParser<'fs>,
FK: ?Sized + FormatKey,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut segments = self.format_segments.to_parser();
for segment in &mut segments {
match segment {
ParseSegment::Literal(s) => f.write_str(s)?,
ParseSegment::Key(key) => match self.format_keys.fmt(key, f) {
Ok(_) => {}
Err(FormatKeyError::Fmt(e)) => return Err(e),
Err(FormatKeyError::UnknownKey) => {
self.error.set(Some(FormatError::Key(key)));
return Err(fmt::Error);
}
},
}
}
let remaining = FS::unparsed(segments);
if !remaining.is_empty() {
self.error.set(Some(FormatError::Parse(remaining)));
Err(fmt::Error)
} else {
Ok(())
}
}
}
impl<'fs, 'fk, FS, FK> fmt::Debug for FormatArgs<'fs, 'fk, FS, FK>
where
FS: ?Sized + ToFormatParser<'fs>,
FK: ?Sized + FormatKey,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[cfg(test)]
mod tests {
use core::fmt::{self, Write};
use crate::{FormatArgs, FormatError, FormatKey, FormatKeyError};
struct WriteShim<'a> {
w: &'a mut [u8],
n: usize,
}
impl fmt::Write for WriteShim<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let remaining = self.w.len() - self.n;
if let Some(prefix) = s.as_bytes().get(..remaining) {
self.w[self.n..].copy_from_slice(prefix);
self.n = self.w.len();
Err(fmt::Error)
} else {
let n = self.n + s.len();
self.w[self.n..n].copy_from_slice(s.as_bytes());
self.n = n;
Ok(())
}
}
}
fn format<'a, F: FormatKey>(
s: &'a str,
fmt: &'a F,
f: impl FnOnce(&[u8]),
) -> Result<(), FormatError<'a>> {
let mut bytes = WriteShim {
w: &mut [0; 1024],
n: 0,
};
let fmt = FormatArgs::new(s, fmt);
let _ = write!(bytes, "{}", fmt);
if let Some(err) = fmt.error.take() {
return Err(err);
}
f(&bytes.w[..bytes.n]);
Ok(())
}
struct Message;
impl FormatKey for Message {
fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
match key {
"recipient" => f.write_str("World").map_err(FormatKeyError::Fmt),
"time_descriptor" => f.write_str("morning").map_err(FormatKeyError::Fmt),
_ => Err(FormatKeyError::UnknownKey),
}
}
}
#[test]
fn happy_path() {
let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptor}.";
let expected = "Hello, World. Hope you are having a nice morning.";
format(format_str, &Message, |output| {
assert_eq!(output, expected.as_bytes())
})
.unwrap();
}
#[test]
fn missing_key() {
let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptr}.";
assert_eq!(
format(format_str, &Message, |_| {}),
Err(FormatError::Key("time_descriptr"))
);
}
#[test]
fn failed_parsing() {
let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptor.";
assert_eq!(
format(format_str, &Message, |_| {}),
Err(FormatError::Parse("time_descriptor."))
);
}
#[test]
fn escape_brackets() {
let format_str = "You can make custom formatting terms using {{foo}!";
let expected = "You can make custom formatting terms using {foo}!";
format(format_str, &Message, |output| {
assert_eq!(output, expected.as_bytes())
})
.unwrap();
}
}