Skip to content

Commit 1b9fa24

Browse files
committed
add write
1 parent cf0cf3d commit 1b9fa24

File tree

3 files changed

+65
-21
lines changed

3 files changed

+65
-21
lines changed

src/hard_coded/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ macro_rules! format_value {
55
($name:ident($out:ident, $v:ident, $Trait:ident): $($tts:tt)*) => {
66
#[allow(clippy::too_many_arguments)]
77
pub(crate) fn $name(
8-
$out: &mut String,
8+
$out: &mut impl std::fmt::Write,
99
$v: impl std::fmt::$Trait,
1010
width: Option<usize>,
1111
precision: Option<usize>,
@@ -15,7 +15,6 @@ macro_rules! format_value {
1515
zero: bool,
1616
) -> crate::Result<(), std::fmt::Error> {
1717
use {crate::Alignment as A, crate::Sign as S};
18-
use std::fmt::Write;
1918

2019
match (width, precision, alignment, sign, hash, zero) {
2120
$($tts)*
@@ -76,7 +75,7 @@ mod upper_hex_debug;
7675

7776
#[allow(clippy::too_many_arguments)]
7877
pub(crate) fn format_value(
79-
out: &mut String,
78+
out: &mut impl Write,
8079
value: &Formattable,
8180
width: Option<usize>,
8281
precision: Option<usize>,

src/lib.rs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Runtime implementation of [`format!`].
22
//!
3-
//! See [`format()`] for details.
3+
//! See [`format`](format()) and [`write`](write()) for details.
44
//!
55
//! # Features
66
//! By default only [`Display`] is supported, the rest of the
@@ -24,7 +24,7 @@ use std::error::Error as StdError;
2424
use std::fmt::Pointer;
2525
#[cfg(feature = "number")]
2626
use std::fmt::{Binary, LowerExp, LowerHex, Octal, UpperExp, UpperHex};
27-
use std::fmt::{Debug, Display, Error as FmtError};
27+
use std::fmt::{Debug, Display, Error as FmtError, Write};
2828
use std::hash::Hash;
2929
use std::num::ParseIntError;
3030

@@ -494,21 +494,23 @@ impl<'a> FormatArgument<'a> {
494494
}
495495
}
496496

497-
/// Runtime version of [`format!`].
497+
/// Runtime version of [`write!`].
498498
///
499-
/// Takes a string and a context, containing [`Formattable`] values, returns a
500-
/// string.
499+
/// Takes a mutable [`Write`] e.g `&mut String`, a format string and a context,
500+
/// containing [`Formattable`] values.
501501
///
502502
/// ```
503-
/// use template::{format, Formattable};
503+
/// use template::{write, Formattable};
504504
///
505-
/// let formatted = format(
505+
/// let mut buf = String::new();
506+
/// write(
507+
/// &mut buf,
506508
/// "{value:+05}", // could be dynamic
507509
/// &[("value", Formattable::display(&12))].into_iter().collect(),
508510
/// )
509511
/// .unwrap();
510512
///
511-
/// assert_eq!(formatted, format!("{:+05}", 12));
513+
/// assert_eq!(buf, format!("{:+05}", 12));
512514
/// ```
513515
///
514516
/// # Errors
@@ -518,16 +520,17 @@ impl<'a> FormatArgument<'a> {
518520
/// failed.
519521
///
520522
/// For more details have a look at [`Error`] and [`FormatArgumentError`].
521-
pub fn format<K: Borrow<str> + Eq + Hash>(
523+
pub fn write<K: Borrow<str> + Eq + Hash>(
524+
out: &mut impl Write,
522525
mut format: &str,
523526
context: &HashMap<K, Formattable>,
524-
) -> Result<String> {
527+
) -> Result {
525528
let format = &mut format;
526-
let mut out = String::with_capacity(format.len());
527529
let idx = &mut 0;
528530
while !format.is_empty() {
529531
if format.starts_with("{{") || format.starts_with("}}") {
530-
out.push_str(&format[..1]);
532+
out.write_str(&format[..1])
533+
.map_err(|e| Error::FmtError(e, *idx))?;
531534
step(2, format, idx);
532535
continue;
533536
}
@@ -548,7 +551,7 @@ pub fn format<K: Borrow<str> + Eq + Hash>(
548551
.get(ident)
549552
.ok_or(Error::MissingValue(ident.to_string(), start))?;
550553
format_value(
551-
&mut out, value, width, precision, alignment, sign, hash, zero, trait_, *idx,
554+
out, value, width, precision, alignment, sign, hash, zero, trait_, *idx,
552555
)?;
553556
ensure!(
554557
format.starts_with('}'),
@@ -561,8 +564,41 @@ pub fn format<K: Borrow<str> + Eq + Hash>(
561564
.chars()
562565
.next()
563566
.expect("should contain a char if not empty");
564-
out.push(next);
567+
out.write_char(next).map_err(|e| Error::FmtError(e, *idx))?;
565568
step(next.len_utf8(), format, idx);
566569
}
570+
Ok(())
571+
}
572+
573+
/// Runtime version of [`format!`].
574+
///
575+
/// Takes a string and a context, containing [`Formattable`] values, returns a
576+
/// string.
577+
///
578+
/// ```
579+
/// use template::{format, Formattable};
580+
///
581+
/// let formatted = format(
582+
/// "{value:+05}", // could be dynamic
583+
/// &[("value", Formattable::display(&12))].into_iter().collect(),
584+
/// )
585+
/// .unwrap();
586+
///
587+
/// assert_eq!(formatted, format!("{:+05}", 12));
588+
/// ```
589+
///
590+
/// # Errors
591+
///
592+
/// It will return an error if the specified format string has invalid syntax,
593+
/// the type doesn't implement the expected trait, or the formatting it self
594+
/// failed.
595+
///
596+
/// For more details have a look at [`Error`] and [`FormatArgumentError`].
597+
pub fn format<K: Borrow<str> + Eq + Hash>(
598+
format: &str,
599+
context: &HashMap<K, Formattable>,
600+
) -> Result<String> {
601+
let mut out = String::with_capacity(format.len());
602+
write(&mut out, format, context)?;
567603
Ok(out)
568604
}

tests/test.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,25 @@ fn test(
120120
t.pass_inline(
121121
&converter.to_token_stream().to_string(),
122122
&quote! {
123-
use template::{{format, Formattable}};
123+
use template::{{format, Formattable, write}};
124124
use std::thread;
125+
use std::fmt::Write;
125126
fn main() {
126127
// This stack overflows without the thread on windows + nightly
127128
thread::spawn(move ||{
128129
let value = &[("ident", Formattable::#converter(&#value))].into_iter().collect();
129-
#(assert!(
130-
format(#format_args, value).unwrap() == format!(#format_args, ident = #value),
130+
#(
131+
assert_eq!(
132+
format(#format_args, value).unwrap(),
133+
format!(#format_args, ident = #value),
131134
"{}", #format_args
132-
);)*
135+
);
136+
let mut buf_rt = String::new();
137+
write(&mut buf_rt, #format_args, value).unwrap();
138+
let mut buf_ct = String::new();
139+
write!(&mut buf_ct, #format_args, ident = #value).unwrap();
140+
assert_eq!(buf_rt, buf_ct, "{}", #format_args);
141+
)*
133142
}).join().unwrap();
134143
}
135144
}

0 commit comments

Comments
 (0)