koto_runtime/
display_context.rs

1use std::fmt;
2
3use koto_memory::Address;
4
5use crate::{KString, KotoVm};
6
7/// A helper for converting Koto values to strings
8#[derive(Default)]
9pub struct DisplayContext<'a> {
10    result: String,
11    vm: Option<&'a KotoVm>,
12    // A contained value might need to be displayed differently,
13    // - Strings should be displayed with quotes when they're inside a container.
14    // - Containers should check the parent list to avoid recursive display operations.
15    parent_containers: Vec<Address>,
16    /// True when the resulting string is to be used in a debug context.
17    debug: bool,
18}
19
20impl<'a> DisplayContext<'a> {
21    /// Makes a display context with the given VM
22    pub fn with_vm(vm: &'a KotoVm) -> Self {
23        Self {
24            result: String::default(),
25            vm: Some(vm),
26            parent_containers: Vec::default(),
27            debug: false,
28        }
29    }
30
31    /// Makes a display context with the given VM and reserved capacity
32    pub fn with_vm_and_capacity(vm: &'a KotoVm, capacity: usize) -> Self {
33        Self {
34            result: String::with_capacity(capacity),
35            vm: Some(vm),
36            parent_containers: Vec::default(),
37            debug: false,
38        }
39    }
40
41    /// Enables the debug flag on the display context
42    pub fn enable_debug(mut self) -> Self {
43        self.debug = true;
44        self
45    }
46
47    /// Returns the resulting string and consumes the context
48    pub fn result(self) -> String {
49        self.result
50    }
51
52    /// Appends to the end of the string
53    pub fn append<'b>(&mut self, s: impl Into<StringBuilderAppend<'b>>) {
54        s.into().append(&mut self.result);
55    }
56
57    /// Returns a reference to the context's VM
58    pub fn vm(&self) -> &Option<&'a KotoVm> {
59        &self.vm
60    }
61
62    /// True if the resulting string will be used in a debug context
63    ///
64    /// This is true when a `debug` expression is being used, or when using the `?` representation
65    /// in a formatted string.
66    pub fn debug_enabled(&self) -> bool {
67        self.debug
68    }
69
70    /// Returns true if the value that's being displayed is in a container
71    pub fn is_contained(&self) -> bool {
72        !self.parent_containers.is_empty()
73    }
74
75    /// Returns true if the given ID is present in the parent container list
76    pub fn is_in_parents(&self, id: Address) -> bool {
77        self.parent_containers.contains(&id)
78    }
79
80    /// Adds the given ID to the parents list
81    ///
82    /// Containers should call this before displaying their contained values.
83    pub fn push_container(&mut self, id: Address) {
84        self.parent_containers.push(id);
85    }
86
87    /// Pops the previously added parent ID
88    ///
89    /// Containers should call this after displaying their contained values.
90    pub fn pop_container(&mut self) {
91        self.parent_containers.pop();
92    }
93}
94
95impl fmt::Write for DisplayContext<'_> {
96    fn write_str(&mut self, s: &str) -> fmt::Result {
97        self.append(s);
98        Ok(())
99    }
100}
101
102/// Types that can be appended to [DisplayContext]
103pub enum StringBuilderAppend<'a> {
104    Char(char),
105    Str(&'a str),
106    String(String),
107    KString(KString),
108    KStringRef(&'a KString),
109}
110
111impl From<char> for StringBuilderAppend<'_> {
112    fn from(value: char) -> Self {
113        StringBuilderAppend::Char(value)
114    }
115}
116
117impl<'a> From<&'a str> for StringBuilderAppend<'a> {
118    fn from(value: &'a str) -> Self {
119        StringBuilderAppend::Str(value)
120    }
121}
122
123impl From<String> for StringBuilderAppend<'_> {
124    fn from(value: String) -> Self {
125        StringBuilderAppend::String(value)
126    }
127}
128
129impl From<KString> for StringBuilderAppend<'_> {
130    fn from(value: KString) -> Self {
131        StringBuilderAppend::KString(value)
132    }
133}
134
135impl<'a> From<&'a KString> for StringBuilderAppend<'a> {
136    fn from(value: &'a KString) -> Self {
137        StringBuilderAppend::KStringRef(value)
138    }
139}
140
141impl StringBuilderAppend<'_> {
142    fn append(self, string: &mut String) {
143        match self {
144            StringBuilderAppend::Char(c) => string.push(c),
145            StringBuilderAppend::Str(s) => string.push_str(s),
146            StringBuilderAppend::String(s) => string.push_str(&s),
147            StringBuilderAppend::KString(s) => string.push_str(&s),
148            StringBuilderAppend::KStringRef(s) => string.push_str(s),
149        }
150    }
151}