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