#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
mod helpers;
mod impls;
mod utils;
use core::{slice::from_raw_parts, str::from_utf8_unchecked};
pub mod derive {
pub use tfmt_macros::uDebug;
}
#[doc(hidden)]
pub use utils::{uDisplayFloat, uDisplayHex, UnstableDoAsFormatter};
#[allow(non_camel_case_types)]
pub trait uWrite {
type Error;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error>;
fn write_char(&mut self, c: char) -> Result<(), Self::Error> {
let mut buf: [u8; 4] = [0; 4];
self.write_str(c.encode_utf8(&mut buf))
}
}
#[macro_export]
#[cfg(not(feature = "std"))]
macro_rules! uformat {
($cap:expr, $($tt:tt)*) => {{
let mut s = heapless::String::<$cap>::new();
#[allow(unreachable_code)]
match tfmt::uwrite!(&mut s, $($tt)*) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}};
}
#[macro_export]
#[cfg(feature = "std")]
macro_rules! uformat {
($cap:expr, $($tt:tt)*) => {{
let mut s = String::new();
#[allow(unreachable_code)]
match tfmt::uwrite!(&mut s, $($tt)*) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}};
}
#[doc(hidden)]
#[macro_export]
macro_rules! udisplay_as_udebug {
($type: ty) => {
impl uDebug for $type {
#[inline(always)]
fn fmt<W>(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
<$type as uDisplay>::fmt(self, f)
}
}
};
}
#[cfg(not(doctest))] pub use tfmt_macros::uwrite;
pub use tfmt_macros::uwriteln;
#[allow(non_camel_case_types)]
pub struct Formatter<'w, W>
where
W: uWrite + ?Sized,
{
writer: &'w mut W,
indentation: usize,
pretty: bool,
}
impl<'w, W> Formatter<'w, W>
where
W: uWrite + ?Sized,
{
pub fn new(writer: &'w mut W) -> Self {
Self {
writer,
indentation: 0,
pretty: false,
}
}
pub fn write_char(&mut self, c: char) -> Result<(), W::Error> {
let mut buf = [0_u8; 4];
let s = c.encode_utf8(&mut buf);
self.writer.write_str(s)
}
pub fn write_str(&mut self, s: &str) -> Result<(), W::Error> {
self.writer.write_str(s)
}
pub fn pretty(
&mut self,
f: impl FnOnce(&mut Self) -> Result<(), W::Error>,
) -> Result<(), W::Error> {
let pretty = self.pretty;
self.pretty = true;
f(self)?;
self.pretty = pretty;
Ok(())
}
fn indent(&mut self) -> Result<(), W::Error> {
for _ in 0..self.indentation {
self.write_str(" ")?;
}
Ok(())
}
pub fn write_padded(
&mut self,
s: &str,
pad_char: char,
padding: Padding,
) -> Result<(), W::Error> {
let mut buf = [0_u8; 4];
let pad_c = pad_char.encode_utf8(&mut buf);
match padding {
Padding::LeftAligned(pad_length) => {
self.writer.write_str(s)?;
for _ in s.len()..pad_length {
self.writer.write_str(pad_c)?;
}
Ok(())
}
Padding::Usual(pad_length) | Padding::RightAligned(pad_length) => {
for _ in s.len()..pad_length {
self.writer.write_str(pad_c)?;
}
self.writer.write_str(s)
}
Padding::CenterAligned(pad_length) => {
let padding = pad_length - s.len();
let half = padding / 2;
for _ in 0..half {
self.writer.write_str(pad_c)?;
}
self.writer.write_str(s)?;
for _ in half..padding {
self.writer.write_str(pad_c)?;
}
Ok(())
}
}
}
}
#[allow(non_camel_case_types)]
pub trait uDisplay {
fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized;
}
#[allow(non_camel_case_types)]
pub trait uDebug {
fn fmt<W>(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized;
}
#[derive(PartialEq, Clone, Copy)]
pub enum Padding {
Usual(usize),
LeftAligned(usize),
RightAligned(usize),
CenterAligned(usize),
}
#[allow(non_camel_case_types)]
pub trait uDisplayPadded {
fn fmt_padded<W>(
&self,
_: &mut Formatter<'_, W>,
padding: Padding,
pad_char: char,
) -> Result<(), W::Error>
where
W: uWrite + ?Sized;
}
#[allow(non_camel_case_types)]
pub trait uDisplayFormatted {
fn fmt_formatted<W>(
&self,
_: &mut Formatter<'_, W>,
prefix: bool,
cmd: char,
padding: Padding,
pad_char: char,
behind: usize,
) -> Result<(), W::Error>
where
W: uWrite + ?Sized;
}
pub struct Convert<const CAP: usize> {
buf: [u8; CAP],
idx: usize,
}
impl<const CAP: usize> Convert<CAP> {
pub fn new(pad_char: u8) -> Convert<CAP> {
Convert { buf: [pad_char; CAP], idx: CAP }
}
pub fn as_str(&self) -> &str {
unsafe {
let p_buf = self.buf.as_ptr().cast::<u8>();
let length = CAP - self.idx;
let slice = from_raw_parts(p_buf.add(self.idx), length);
from_utf8_unchecked(slice)
}
}
pub fn write_u8(&mut self, c: u8) -> Result<(), ()> {
if self.idx > 0 {
let p_buf = self.buf.as_mut_ptr().cast::<u8>();
self.idx -= 1;
unsafe { p_buf.add(self.idx).write_volatile(c) };
Ok(())
} else {
Err(())
}
}
pub fn write_str(&mut self, s: &str) -> Result<(), ()> {
for c in s.bytes().rev() {
self.write_u8(c)?;
}
Ok(())
}
}