rune_alloc/fmt/mod.rs
1//! Built-in formatting utilities.
2
3mod impls;
4
5use core::fmt::{self, Arguments};
6
7use crate::borrow::TryToOwned;
8use crate::error::Error;
9use crate::string::String;
10
11/// Fallible write formatting implementation.
12pub trait TryWrite {
13 /// Writes a string slice into this writer, returning whether the write
14 /// succeeded.
15 ///
16 /// This method can only succeed if the entire string slice was successfully
17 /// written, and this method will not return until all data has been
18 /// written or an error occurs.
19 ///
20 /// # Errors
21 ///
22 /// This function will return an instance of [`Error`] on error.
23 ///
24 /// # Examples
25 ///
26 /// ```
27 /// use rune::alloc::fmt::TryWrite;
28 /// use rune::alloc::{String, Error};
29 ///
30 /// fn writer<W: TryWrite>(f: &mut W, s: &str) -> Result<(), Error> {
31 /// f.try_write_str(s)
32 /// }
33 ///
34 /// let mut buf = String::new();
35 /// writer(&mut buf, "hola")?;
36 /// assert_eq!(&buf, "hola");
37 /// # Ok::<_, rune::alloc::Error>(())
38 /// ```
39 fn try_write_str(&mut self, s: &str) -> Result<(), Error>;
40
41 /// Writes a [`char`] into this writer, returning whether the write succeeded.
42 ///
43 /// A single [`char`] may be encoded as more than one byte.
44 /// This method can only succeed if the entire byte sequence was successfully
45 /// written, and this method will not return until all data has been
46 /// written or an error occurs.
47 ///
48 /// # Errors
49 ///
50 /// This function will return an instance of [`Error`] on error.
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use rune::alloc::fmt::TryWrite;
56 /// use rune::alloc::{String, Error};
57 ///
58 /// fn writer<W: TryWrite>(f: &mut W, c: char) -> Result<(), Error> {
59 /// f.try_write_char(c)
60 /// }
61 ///
62 /// let mut buf = String::new();
63 /// writer(&mut buf, 'a')?;
64 /// writer(&mut buf, 'b')?;
65 /// assert_eq!(&buf, "ab");
66 /// # Ok::<_, rune::alloc::Error>(())
67 /// ```
68 #[inline]
69 fn try_write_char(&mut self, c: char) -> Result<(), Error> {
70 self.try_write_str(c.encode_utf8(&mut [0; 4]))
71 }
72
73 #[inline]
74 #[doc(hidden)]
75 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<(), Error>
76 where
77 Self: Sized,
78 {
79 struct Writer<'a> {
80 target: &'a mut dyn TryWrite,
81 error: Option<Error>,
82 }
83
84 impl fmt::Write for Writer<'_> {
85 #[inline]
86 fn write_str(&mut self, s: &str) -> fmt::Result {
87 if let Err(error) = (*self.target).try_write_str(s) {
88 self.error = Some(error);
89 }
90
91 Ok(())
92 }
93
94 #[inline]
95 fn write_char(&mut self, c: char) -> fmt::Result {
96 if let Err(error) = (*self.target).try_write_char(c) {
97 self.error = Some(error);
98 }
99
100 Ok(())
101 }
102 }
103
104 let mut writer = Writer {
105 target: self,
106 error: None,
107 };
108
109 if let Err(fmt::Error) = fmt::write(&mut writer, args) {
110 return Err(Error::FormatError);
111 }
112
113 if let Some(error) = writer.error {
114 Err(error)
115 } else {
116 Ok(())
117 }
118 }
119}
120
121/// The `format` function takes an [`Arguments`] struct and returns the
122/// resulting formatted string.
123///
124/// The [`Arguments`] instance can be created with the [`format_args!`] macro.
125///
126/// # Examples
127///
128/// Basic usage:
129///
130/// ```
131/// use rune::alloc::fmt;
132///
133/// let s = fmt::try_format(format_args!("Hello, {}!", "world"))?;
134/// assert_eq!(s, "Hello, world!");
135/// # Ok::<_, rune::alloc::Error>(())
136/// ```
137///
138/// Please note that using [`try_format!`] might be preferable. Example:
139///
140/// ```
141/// use rune::alloc::try_format;
142///
143/// let s = try_format!("Hello, {}!", "world");
144/// assert_eq!(s, "Hello, world!");
145/// # Ok::<_, rune::alloc::Error>(())
146/// ```
147///
148/// [`format_args!`]: core::format_args
149/// [`try_format!`]: try_format!
150#[inline]
151pub fn try_format(args: Arguments<'_>) -> Result<String, Error> {
152 #[cfg(rune_nightly)]
153 fn estimated_capacity(args: &Arguments<'_>) -> usize {
154 args.estimated_capacity()
155 }
156
157 #[cfg(not(rune_nightly))]
158 fn estimated_capacity(_: &Arguments<'_>) -> usize {
159 0
160 }
161
162 fn format_inner(args: Arguments<'_>) -> Result<String, Error> {
163 let capacity = estimated_capacity(&args);
164 let mut output = String::try_with_capacity(capacity)?;
165 output.write_fmt(args)?;
166 Ok(output)
167 }
168
169 match args.as_str() {
170 Some(string) => string.try_to_owned(),
171 None => format_inner(args),
172 }
173}