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