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 struct Writer<'a, T>
77 where
78 T: ?Sized,
79 {
80 target: &'a mut T,
81 error: Option<Error>,
82 }
83
84 impl<T> fmt::Write for Writer<'_, T>
85 where
86 T: ?Sized + TryWrite,
87 {
88 #[inline]
89 fn write_str(&mut self, s: &str) -> fmt::Result {
90 if let Err(error) = (*self.target).try_write_str(s) {
91 self.error = Some(error);
92 }
93
94 Ok(())
95 }
96
97 #[inline]
98 fn write_char(&mut self, c: char) -> fmt::Result {
99 if let Err(error) = (*self.target).try_write_char(c) {
100 self.error = Some(error);
101 }
102
103 Ok(())
104 }
105 }
106
107 let mut writer = Writer {
108 target: self,
109 error: None,
110 };
111
112 if let Err(fmt::Error) = fmt::write(&mut writer, args) {
113 return Err(Error::FormatError);
114 }
115
116 if let Some(error) = writer.error {
117 Err(error)
118 } else {
119 Ok(())
120 }
121 }
122}
123
124/// The `format` function takes an [`Arguments`] struct and returns the
125/// resulting formatted string.
126///
127/// The [`Arguments`] instance can be created with the [`format_args!`] macro.
128///
129/// # Examples
130///
131/// Basic usage:
132///
133/// ```
134/// use rune::alloc::fmt;
135///
136/// let s = fmt::try_format(format_args!("Hello, {}!", "world"))?;
137/// assert_eq!(s, "Hello, world!");
138/// # Ok::<_, rune::alloc::Error>(())
139/// ```
140///
141/// Please note that using [`try_format!`] might be preferable. Example:
142///
143/// ```
144/// use rune::alloc::try_format;
145///
146/// let s = try_format!("Hello, {}!", "world");
147/// assert_eq!(s, "Hello, world!");
148/// # Ok::<_, rune::alloc::Error>(())
149/// ```
150///
151/// [`format_args!`]: core::format_args
152/// [`try_format!`]: try_format!
153#[inline]
154pub fn try_format(args: Arguments<'_>) -> Result<String, Error> {
155 #[cfg(rune_nightly)]
156 fn estimated_capacity(args: &Arguments<'_>) -> usize {
157 args.estimated_capacity()
158 }
159
160 #[cfg(not(rune_nightly))]
161 fn estimated_capacity(_: &Arguments<'_>) -> usize {
162 0
163 }
164
165 fn format_inner(args: Arguments<'_>) -> Result<String, Error> {
166 let capacity = estimated_capacity(&args);
167 let mut output = String::try_with_capacity(capacity)?;
168 output.write_fmt(args)?;
169 Ok(output)
170 }
171
172 match args.as_str() {
173 Some(string) => string.try_to_owned(),
174 None => format_inner(args),
175 }
176}