rune/runtime/
format.rs

1//! Types for dealing with formatting specifications.
2
3use core::fmt;
4use core::iter;
5use core::mem::take;
6use core::num::NonZeroUsize;
7use core::str;
8
9#[cfg(feature = "musli")]
10use musli::{Decode, Encode};
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14use crate as rune;
15use crate::alloc::clone::TryClone;
16use crate::alloc::fmt::TryWrite;
17use crate::alloc::{self, String};
18use crate::runtime::{Formatter, Inline, ProtocolCaller, Repr, Value, VmError, VmErrorKind};
19use crate::{Any, TypeHash};
20
21/// Error raised when trying to parse a type string and it fails.
22#[derive(Debug, Clone, Copy)]
23#[non_exhaustive]
24pub struct TypeFromStrError;
25
26impl fmt::Display for TypeFromStrError {
27    #[inline]
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        write!(f, "Bad type string")
30    }
31}
32
33/// Error raised when trying to parse an alignment string and it fails.
34#[derive(Debug, Clone, Copy)]
35pub struct AlignmentFromStrError;
36
37impl fmt::Display for AlignmentFromStrError {
38    #[inline]
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "Bad alignment string")
41    }
42}
43
44/// A format specification, wrapping an inner value.
45#[derive(Any, Debug, Clone, TryClone)]
46#[rune(item = ::std::fmt)]
47pub struct Format {
48    /// The value being formatted.
49    pub(crate) value: Value,
50    /// The specification.
51    #[try_clone(copy)]
52    pub(crate) spec: FormatSpec,
53}
54
55/// A format specification.
56#[derive(Debug, Clone, Copy, TryClone)]
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[cfg_attr(feature = "musli", derive(Decode, Encode))]
59#[try_clone(copy)]
60#[non_exhaustive]
61pub struct FormatSpec {
62    /// Formatting flags.
63    pub(crate) flags: Flags,
64    /// The fill character.
65    pub(crate) fill: char,
66    /// The alignment specification.
67    pub(crate) align: Alignment,
68    /// Formatting width.
69    pub(crate) width: Option<NonZeroUsize>,
70    /// Formatting precision.
71    pub(crate) precision: Option<NonZeroUsize>,
72    /// The type specification.
73    pub(crate) format_type: Type,
74}
75
76impl FormatSpec {
77    /// Construct a new format specification.
78    pub fn new(
79        flags: Flags,
80        fill: char,
81        align: Alignment,
82        width: Option<NonZeroUsize>,
83        precision: Option<NonZeroUsize>,
84        format_type: Type,
85    ) -> Self {
86        Self {
87            flags,
88            fill,
89            align,
90            width,
91            precision,
92            format_type,
93        }
94    }
95
96    /// get traits out of a floating point number.
97    fn float_traits(&self, n: f64) -> (f64, Alignment, char, Option<char>) {
98        if self.flags.test(Flag::SignAwareZeroPad) {
99            if n.is_sign_negative() {
100                (-n, Alignment::Right, '0', Some('-'))
101            } else {
102                (n, Alignment::Right, '0', None)
103            }
104        } else if self.flags.test(Flag::SignPlus) && n.is_sign_positive() {
105            (n, self.align, self.fill, Some('+'))
106        } else {
107            (n, self.align, self.fill, None)
108        }
109    }
110
111    /// get traits out of an integer.
112    fn int_traits(&self, n: i64) -> (i64, Alignment, char, Option<char>) {
113        if self.flags.test(Flag::SignAwareZeroPad) {
114            if n < 0 {
115                (-n, Alignment::Right, '0', Some('-'))
116            } else {
117                (n, Alignment::Right, '0', None)
118            }
119        } else if self.flags.test(Flag::SignPlus) && n >= 0 {
120            (n, self.align, self.fill, Some('+'))
121        } else {
122            (n, self.align, self.fill, None)
123        }
124    }
125
126    /// Format the given number.
127    fn format_number(&self, buf: &mut String, n: i64) -> alloc::Result<()> {
128        let mut buffer = itoa::Buffer::new();
129        buf.try_push_str(buffer.format(n))?;
130        Ok(())
131    }
132
133    /// Format the given float.
134    fn format_float(&self, buf: &mut String, n: f64) -> alloc::Result<()> {
135        if let Some(precision) = self.precision {
136            write!(buf, "{:.*}", precision.get(), n)?;
137        } else {
138            let mut buffer = ryu::Buffer::new();
139            buf.try_push_str(buffer.format(n))?;
140        }
141
142        Ok(())
143    }
144
145    /// Format fill.
146    fn format_fill(
147        &self,
148        f: &mut Formatter,
149        align: Alignment,
150        fill: char,
151        sign: Option<char>,
152    ) -> alloc::Result<()> {
153        let (f, buf) = f.parts_mut();
154
155        if let Some(sign) = sign {
156            f.try_write_char(sign)?;
157        }
158
159        let mut w = self.width.map(|n| n.get()).unwrap_or_default();
160
161        if w == 0 {
162            f.try_write_str(buf)?;
163            return Ok(());
164        }
165
166        w = w
167            .saturating_sub(buf.chars().count())
168            .saturating_sub(sign.map(|_| 1).unwrap_or_default());
169
170        if w == 0 {
171            f.try_write_str(buf)?;
172            return Ok(());
173        }
174
175        let mut filler = iter::repeat_n(fill, w);
176
177        match align {
178            Alignment::Left => {
179                f.try_write_str(buf)?;
180
181                for c in filler {
182                    f.try_write_char(c)?;
183                }
184            }
185            Alignment::Center => {
186                for c in (&mut filler).take(w / 2) {
187                    f.try_write_char(c)?;
188                }
189
190                f.try_write_str(buf)?;
191
192                for c in filler {
193                    f.try_write_char(c)?;
194                }
195            }
196            Alignment::Right => {
197                for c in filler {
198                    f.try_write_char(c)?;
199                }
200
201                f.try_write_str(buf)?;
202            }
203        }
204
205        Ok(())
206    }
207
208    fn format_display(
209        &self,
210        value: &Value,
211        f: &mut Formatter,
212        caller: &mut dyn ProtocolCaller,
213    ) -> Result<(), VmError> {
214        'fallback: {
215            match value.as_ref() {
216                Repr::Inline(value) => match value {
217                    Inline::Char(c) => {
218                        f.buf_mut().try_push(*c)?;
219                        self.format_fill(f, self.align, self.fill, None)?;
220                    }
221                    Inline::Signed(n) => {
222                        let (n, align, fill, sign) = self.int_traits(*n);
223                        self.format_number(f.buf_mut(), n)?;
224                        self.format_fill(f, align, fill, sign)?;
225                    }
226                    Inline::Float(n) => {
227                        let (n, align, fill, sign) = self.float_traits(*n);
228                        self.format_float(f.buf_mut(), n)?;
229                        self.format_fill(f, align, fill, sign)?;
230                    }
231                    _ => {
232                        break 'fallback;
233                    }
234                },
235                Repr::Dynamic(..) => {
236                    break 'fallback;
237                }
238                Repr::Any(value) => match value.type_hash() {
239                    String::HASH => {
240                        let s = value.borrow_ref::<String>()?;
241                        f.buf_mut().try_push_str(&s)?;
242                        self.format_fill(f, self.align, self.fill, None)?;
243                    }
244                    _ => {
245                        break 'fallback;
246                    }
247                },
248            }
249
250            return Ok(());
251        }
252
253        value.display_fmt_with(f, caller)
254    }
255
256    fn format_debug(
257        &self,
258        value: &Value,
259        f: &mut Formatter,
260        caller: &mut dyn ProtocolCaller,
261    ) -> Result<(), VmError> {
262        'fallback: {
263            match value.as_ref() {
264                Repr::Inline(value) => match value {
265                    Inline::Signed(n) => {
266                        let (n, align, fill, sign) = self.int_traits(*n);
267                        self.format_number(f.buf_mut(), n)?;
268                        self.format_fill(f, align, fill, sign)?;
269                    }
270                    Inline::Float(n) => {
271                        let (n, align, fill, sign) = self.float_traits(*n);
272                        self.format_float(f.buf_mut(), n)?;
273                        self.format_fill(f, align, fill, sign)?;
274                    }
275                    _ => {
276                        break 'fallback;
277                    }
278                },
279                Repr::Dynamic(..) => {
280                    break 'fallback;
281                }
282                Repr::Any(value) => match value.type_hash() {
283                    String::HASH => {
284                        let s = value.borrow_ref::<String>()?;
285                        write!(f, "{s:?}")?;
286                    }
287                    _ => {
288                        break 'fallback;
289                    }
290                },
291            }
292
293            return Ok(());
294        };
295
296        value.debug_fmt_with(f, caller)
297    }
298
299    fn format_upper_hex(&self, value: &Value, f: &mut Formatter) -> Result<(), VmError> {
300        match value.as_inline() {
301            Some(Inline::Signed(n)) => {
302                let (n, align, fill, sign) = self.int_traits(*n);
303                write!(f.buf_mut(), "{n:X}")?;
304                self.format_fill(f, align, fill, sign)?;
305            }
306            _ => {
307                return Err(VmError::new(VmErrorKind::IllegalFormat));
308            }
309        }
310
311        Ok(())
312    }
313
314    fn format_lower_hex(&self, value: &Value, f: &mut Formatter) -> Result<(), VmError> {
315        match value.as_inline() {
316            Some(Inline::Signed(n)) => {
317                let (n, align, fill, sign) = self.int_traits(*n);
318                write!(f.buf_mut(), "{n:x}")?;
319                self.format_fill(f, align, fill, sign)?;
320            }
321            _ => {
322                return Err(VmError::new(VmErrorKind::IllegalFormat));
323            }
324        }
325
326        Ok(())
327    }
328
329    fn format_binary(&self, value: &Value, f: &mut Formatter) -> Result<(), VmError> {
330        match value.as_inline() {
331            Some(Inline::Signed(n)) => {
332                let (n, align, fill, sign) = self.int_traits(*n);
333                write!(f.buf_mut(), "{n:b}")?;
334                self.format_fill(f, align, fill, sign)?;
335            }
336            _ => {
337                return Err(VmError::new(VmErrorKind::IllegalFormat));
338            }
339        }
340
341        Ok(())
342    }
343
344    fn format_pointer(&self, value: &Value, f: &mut Formatter) -> Result<(), VmError> {
345        match value.as_inline() {
346            Some(Inline::Signed(n)) => {
347                let (n, align, fill, sign) = self.int_traits(*n);
348                write!(f.buf_mut(), "{:p}", n as *const ())?;
349                self.format_fill(f, align, fill, sign)?;
350            }
351            _ => {
352                return Err(VmError::new(VmErrorKind::IllegalFormat));
353            }
354        }
355
356        Ok(())
357    }
358
359    /// Format the given value to the out buffer `out`, using `buf` for
360    /// intermediate work if necessary.
361    pub(crate) fn format(
362        &self,
363        value: &Value,
364        f: &mut Formatter,
365        caller: &mut dyn ProtocolCaller,
366    ) -> Result<(), VmError> {
367        f.buf_mut().clear();
368
369        match self.format_type {
370            Type::Display => self.format_display(value, f, caller)?,
371            Type::Debug => self.format_debug(value, f, caller)?,
372            Type::UpperHex => self.format_upper_hex(value, f)?,
373            Type::LowerHex => self.format_lower_hex(value, f)?,
374            Type::Binary => self.format_binary(value, f)?,
375            Type::Pointer => self.format_pointer(value, f)?,
376        }
377
378        Ok(())
379    }
380}
381
382impl fmt::Display for FormatSpec {
383    #[inline]
384    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
385        write!(
386            f,
387            "format(fill = {fill:?}, align = {align}, flags = {flags:?}, width = {width}, precision = {precision}, format_type = {format_type})",
388            fill = self.fill,
389            align = self.align,
390            flags = self.flags,
391            width = OptionDebug(self.width.as_ref()),
392            precision = OptionDebug(self.precision.as_ref()),
393            format_type = self.format_type
394        )
395    }
396}
397
398struct OptionDebug<'a, T>(Option<&'a T>);
399
400impl<T> fmt::Display for OptionDebug<'_, T>
401where
402    T: fmt::Display,
403{
404    #[inline]
405    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406        match self.0 {
407            Some(value) => write!(f, "{value}"),
408            None => write!(f, "?"),
409        }
410    }
411}
412
413/// The type of formatting requested.
414#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
415#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
416#[cfg_attr(feature = "musli", derive(Decode, Encode))]
417#[non_exhaustive]
418pub enum Type {
419    /// Display type (default).
420    #[default]
421    Display,
422    /// Debug type.
423    Debug,
424    /// Upper hex type.
425    UpperHex,
426    /// Upper hex type.
427    LowerHex,
428    /// Binary formatting type.
429    Binary,
430    /// Pointer formatting type.
431    Pointer,
432}
433
434impl str::FromStr for Type {
435    type Err = TypeFromStrError;
436
437    #[inline]
438    fn from_str(s: &str) -> Result<Self, Self::Err> {
439        match s {
440            "display" => Ok(Self::Display),
441            "debug" => Ok(Self::Debug),
442            "upper_hex" => Ok(Self::UpperHex),
443            "lower_hex" => Ok(Self::LowerHex),
444            "binary" => Ok(Self::Binary),
445            "pointer" => Ok(Self::Pointer),
446            _ => Err(TypeFromStrError),
447        }
448    }
449}
450
451impl fmt::Display for Type {
452    #[inline]
453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
454        match self {
455            Self::Display => {
456                write!(f, "display")?;
457            }
458            Self::Debug => {
459                write!(f, "debug")?;
460            }
461            Self::UpperHex => {
462                write!(f, "upper_hex")?;
463            }
464            Self::LowerHex => {
465                write!(f, "lower_hex")?;
466            }
467            Self::Binary => {
468                write!(f, "binary")?;
469            }
470            Self::Pointer => {
471                write!(f, "pointer")?;
472            }
473        }
474
475        Ok(())
476    }
477}
478
479/// The alignment requested.
480#[derive(Debug, Clone, Copy, TryClone, PartialEq, Eq)]
481#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
482#[cfg_attr(feature = "musli", derive(Decode, Encode))]
483#[try_clone(copy)]
484#[non_exhaustive]
485pub enum Alignment {
486    /// Left alignment.
487    Left,
488    /// Center alignment.
489    Center,
490    /// Right alignment.
491    Right,
492}
493
494impl Default for Alignment {
495    fn default() -> Self {
496        Self::Left
497    }
498}
499
500impl str::FromStr for Alignment {
501    type Err = AlignmentFromStrError;
502
503    #[inline]
504    fn from_str(s: &str) -> Result<Self, Self::Err> {
505        match s {
506            "left" => Ok(Self::Left),
507            "center" => Ok(Self::Center),
508            "right" => Ok(Self::Right),
509            _ => Err(AlignmentFromStrError),
510        }
511    }
512}
513
514impl fmt::Display for Alignment {
515    #[inline]
516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517        match self {
518            Self::Left => {
519                write!(f, "left")?;
520            }
521            Self::Center => {
522                write!(f, "center")?;
523            }
524            Self::Right => {
525                write!(f, "right")?;
526            }
527        }
528
529        Ok(())
530    }
531}
532
533/// A single flag for format spec.
534#[derive(Clone, Copy)]
535#[repr(u32)]
536#[non_exhaustive]
537pub enum Flag {
538    /// Plus sign `+`.
539    SignPlus,
540    /// Minus sign `-`.
541    SignMinus,
542    /// Alternate specifier `#`.
543    Alternate,
544    /// Sign-aware zero pad `0`.
545    SignAwareZeroPad,
546}
547
548/// Format specification flags.
549#[derive(Clone, Copy, TryClone, Default, PartialEq, Eq)]
550#[repr(transparent)]
551#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
552#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(transparent))]
553#[try_clone(copy)]
554pub struct Flags(u32);
555
556impl Flags {
557    /// Check if the set of flags is empty.
558    #[inline]
559    pub fn is_empty(self) -> bool {
560        self.0 == 0
561    }
562
563    /// Get the flags as a number. This representation is not guaranteed to be
564    /// stable.
565    #[inline]
566    pub fn into_u32(self) -> u32 {
567        self.0
568    }
569
570    /// Set the given flag.
571    #[inline]
572    pub fn set(&mut self, flag: Flag) {
573        self.0 |= &(1 << flag as u32);
574    }
575
576    /// Test the given flag.
577    #[inline]
578    pub fn test(&self, flag: Flag) -> bool {
579        (self.0 & (1 << flag as u32)) != 0
580    }
581}
582
583impl From<u32> for Flags {
584    fn from(flags: u32) -> Self {
585        Self(flags)
586    }
587}
588
589impl fmt::Debug for Flags {
590    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591        macro_rules! fmt_flag {
592            ($flag:ident, $o:ident, $spec:literal) => {
593                if self.test(Flag::$flag) {
594                    if !take(&mut $o) {
595                        write!(f, ", ")?;
596                    }
597
598                    write!(f, $spec)?;
599                }
600            };
601        }
602
603        let mut o = true;
604        write!(f, "Flags{{")?;
605        fmt_flag!(SignPlus, o, "+");
606        fmt_flag!(SignMinus, o, "-");
607        fmt_flag!(Alternate, o, "#");
608        fmt_flag!(SignAwareZeroPad, o, "0");
609        write!(f, "}}")?;
610        Ok(())
611    }
612}