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_core::{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), musli(crate = musli_core))]
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), musli(crate = musli_core))]
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(Default, Debug, Clone, Copy, TryClone, PartialEq, Eq)]
481#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
482#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core))]
483#[try_clone(copy)]
484#[non_exhaustive]
485pub enum Alignment {
486    /// Left alignment.
487    #[default]
488    Left,
489    /// Center alignment.
490    Center,
491    /// Right alignment.
492    Right,
493}
494
495impl str::FromStr for Alignment {
496    type Err = AlignmentFromStrError;
497
498    #[inline]
499    fn from_str(s: &str) -> Result<Self, Self::Err> {
500        match s {
501            "left" => Ok(Self::Left),
502            "center" => Ok(Self::Center),
503            "right" => Ok(Self::Right),
504            _ => Err(AlignmentFromStrError),
505        }
506    }
507}
508
509impl fmt::Display for Alignment {
510    #[inline]
511    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
512        match self {
513            Self::Left => {
514                write!(f, "left")?;
515            }
516            Self::Center => {
517                write!(f, "center")?;
518            }
519            Self::Right => {
520                write!(f, "right")?;
521            }
522        }
523
524        Ok(())
525    }
526}
527
528/// A single flag for format spec.
529#[derive(Clone, Copy)]
530#[repr(u32)]
531#[non_exhaustive]
532pub enum Flag {
533    /// Plus sign `+`.
534    SignPlus,
535    /// Minus sign `-`.
536    SignMinus,
537    /// Alternate specifier `#`.
538    Alternate,
539    /// Sign-aware zero pad `0`.
540    SignAwareZeroPad,
541}
542
543/// Format specification flags.
544#[derive(Clone, Copy, TryClone, Default, PartialEq, Eq)]
545#[repr(transparent)]
546#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
547#[cfg_attr(feature = "musli", derive(Decode, Encode), musli(crate = musli_core, transparent))]
548#[try_clone(copy)]
549pub struct Flags(u32);
550
551impl Flags {
552    /// Check if the set of flags is empty.
553    #[inline]
554    pub fn is_empty(self) -> bool {
555        self.0 == 0
556    }
557
558    /// Get the flags as a number. This representation is not guaranteed to be
559    /// stable.
560    #[inline]
561    pub fn into_u32(self) -> u32 {
562        self.0
563    }
564
565    /// Set the given flag.
566    #[inline]
567    pub fn set(&mut self, flag: Flag) {
568        self.0 |= &(1 << flag as u32);
569    }
570
571    /// Test the given flag.
572    #[inline]
573    pub fn test(&self, flag: Flag) -> bool {
574        (self.0 & (1 << flag as u32)) != 0
575    }
576}
577
578impl From<u32> for Flags {
579    fn from(flags: u32) -> Self {
580        Self(flags)
581    }
582}
583
584impl fmt::Debug for Flags {
585    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586        macro_rules! fmt_flag {
587            ($flag:ident, $o:ident, $spec:literal) => {
588                if self.test(Flag::$flag) {
589                    if !take(&mut $o) {
590                        write!(f, ", ")?;
591                    }
592
593                    write!(f, $spec)?;
594                }
595            };
596        }
597
598        let mut o = true;
599        write!(f, "Flags{{")?;
600        fmt_flag!(SignPlus, o, "+");
601        fmt_flag!(SignMinus, o, "-");
602        fmt_flag!(Alternate, o, "#");
603        fmt_flag!(SignAwareZeroPad, o, "0");
604        write!(f, "}}")?;
605        Ok(())
606    }
607}