Skip to main content

logicaffeine_system/
io.rs

1//! I/O Operations for LOGOS Programs
2//!
3//! Provides input/output primitives for LOGOS programs including:
4//!
5//! - [`show`]: Natural formatting output (primitives without quotes, collections with brackets)
6//! - `print`, `println`, `eprintln`: Standard output functions
7//! - [`read_line`]: Read a line from stdin
8//!
9//! The [`Showable`] trait enables custom types to integrate with the `show` verb.
10//!
11//! # Example
12//!
13//! ```no_run
14//! use logicaffeine_system::io::{show, println, read_line};
15//!
16//! // Natural formatting with show
17//! show(&42);           // Prints: 42
18//! show(&"hello");      // Prints: hello (no quotes)
19//! show(&vec![1, 2, 3]); // Prints: [1, 2, 3]
20//!
21//! // Standard output
22//! println("Enter your name:");
23//! let name = read_line();
24//! println(format!("Hello, {}!", name));
25//! ```
26
27use std::fmt::{self, Display};
28
29/// Custom trait for LOGOS Show verb - provides clean, natural output.
30/// Primitives display without quotes, collections display with brackets.
31pub trait Showable {
32    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result;
33}
34
35// Blanket impl for references: &T is Showable if T is Showable
36impl<T: Showable + ?Sized> Showable for &T {
37    #[inline(always)]
38    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        (**self).format_show(f)
40    }
41}
42
43// Primitives: use Display formatting
44impl Showable for i32 {
45    #[inline(always)]
46    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
47        Display::fmt(self, f)
48    }
49}
50
51impl Showable for i64 {
52    #[inline(always)]
53    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
54        Display::fmt(self, f)
55    }
56}
57
58impl Showable for u64 {
59    #[inline(always)]
60    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        Display::fmt(self, f)
62    }
63}
64
65impl Showable for usize {
66    #[inline(always)]
67    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        Display::fmt(self, f)
69    }
70}
71
72impl Showable for f64 {
73    #[inline(always)]
74    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
75        Display::fmt(self, f)
76    }
77}
78
79// Exact rationals show as `n/d` (or a bare integer when whole) — `Display` already
80// reduces, so `Let x: Rational be 7 / 2; Show x` prints `7/2`.
81/// The exact compiled integer (i64 fast path, BigInt spill) prints as the
82/// plain number — indistinguishable from an `i64` `Show`.
83impl Showable for logicaffeine_data::LogosInt {
84    #[inline(always)]
85    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        Display::fmt(self, f)
87    }
88}
89
90impl Showable for logicaffeine_data::LogosRational {
91    #[inline(always)]
92    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        Display::fmt(self, f)
94    }
95}
96
97// Exact base-10 decimals (money) show with their scale faithful — `Display` prints
98// `19.99`/`20.00`, so `Show decimal("19.99")` prints `19.99`, never a lossy float.
99impl Showable for logicaffeine_data::LogosDecimal {
100    #[inline(always)]
101    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        Display::fmt(self, f)
103    }
104}
105
106// Exact complex numbers show as `3+4i` / `i` / `-2i` — `Display` already formats them.
107impl Showable for logicaffeine_data::LogosComplex {
108    #[inline(always)]
109    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        Display::fmt(self, f)
111    }
112}
113
114// ℤ/nℤ elements show as `3 (mod 7)`.
115impl Showable for logicaffeine_data::LogosModular {
116    #[inline(always)]
117    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        Display::fmt(self, f)
119    }
120}
121
122// Physical quantities show as magnitude + unit (`42/127 ft`, `20 °C`) — `Display` formats them.
123impl Showable for logicaffeine_data::LogosQuantity {
124    #[inline(always)]
125    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
126        Display::fmt(self, f)
127    }
128}
129
130impl Showable for logicaffeine_data::LogosMoney {
131    #[inline(always)]
132    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        Display::fmt(self, f)
134    }
135}
136
137impl Showable for logicaffeine_data::LogosUuid {
138    #[inline(always)]
139    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        Display::fmt(self, f)
141    }
142}
143
144impl Showable for bool {
145    #[inline(always)]
146    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        Display::fmt(self, f)
148    }
149}
150
151impl Showable for u8 {
152    #[inline(always)]
153    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
154        Display::fmt(self, f)
155    }
156}
157
158impl Showable for char {
159    #[inline(always)]
160    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        Display::fmt(self, f)
162    }
163}
164
165impl Showable for String {
166    #[inline(always)]
167    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
168        Display::fmt(self, f)
169    }
170}
171
172impl Showable for &str {
173    #[inline(always)]
174    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
175        Display::fmt(self, f)
176    }
177}
178
179// Sequences: bracket notation with recursive formatting
180impl<T: Showable> Showable for Vec<T> {
181    #[inline(always)]
182    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        write!(f, "[")?;
184        for (i, item) in self.iter().enumerate() {
185            if i > 0 {
186                write!(f, ", ")?;
187            }
188            item.format_show(f)?;
189        }
190        write!(f, "]")
191    }
192}
193
194impl<T: Showable> Showable for logicaffeine_data::LogosSeq<T> {
195    #[inline(always)]
196    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
197        let inner = self.borrow();
198        write!(f, "[")?;
199        for (i, item) in inner.iter().enumerate() {
200            if i > 0 {
201                write!(f, ", ")?;
202            }
203            item.format_show(f)?;
204        }
205        write!(f, "]")
206    }
207}
208
209impl<K: Showable + Eq + std::hash::Hash, V: Showable> Showable for logicaffeine_data::LogosMap<K, V> {
210    #[inline(always)]
211    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        let inner = self.borrow();
213        write!(f, "{{")?;
214        for (i, (k, v)) in inner.iter().enumerate() {
215            if i > 0 {
216                write!(f, ", ")?;
217            }
218            k.format_show(f)?;
219            write!(f, ": ")?;
220            v.format_show(f)?;
221        }
222        write!(f, "}}")
223    }
224}
225
226// A Set shows as `{e0, e1, …}` in INSERTION order — matching the
227// tree-walker's Vec-backed set display and the direct-WASM linear set
228// (the LOGOS `Set` alias is an insertion-ordered IndexSet).
229impl<T: Showable, S: std::hash::BuildHasher> Showable for indexmap::IndexSet<T, S> {
230    #[inline(always)]
231    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        write!(f, "{{")?;
233        for (i, v) in self.iter().enumerate() {
234            if i > 0 {
235                write!(f, ", ")?;
236            }
237            v.format_show(f)?;
238        }
239        write!(f, "}}")
240    }
241}
242
243// Slices: same as Vec
244impl<T: Showable> Showable for [T] {
245    #[inline(always)]
246    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
247        write!(f, "[")?;
248        for (i, item) in self.iter().enumerate() {
249            if i > 0 {
250                write!(f, ", ")?;
251            }
252            item.format_show(f)?;
253        }
254        write!(f, "]")
255    }
256}
257
258// Note: &[T] is covered by the blanket `impl<T: Showable + ?Sized> Showable for &T`
259// since `[T]: Showable`.
260
261// Option type: shows "nothing" or the value
262impl<T: Showable> Showable for Option<T> {
263    #[inline(always)]
264    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
265        match self {
266            Some(v) => v.format_show(f),
267            None => write!(f, "nothing"),
268        }
269    }
270}
271
272// CRDT types: show the value
273impl Showable for logicaffeine_data::crdt::GCounter {
274    #[inline(always)]
275    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
276        write!(f, "{}", self.value())
277    }
278}
279
280impl Showable for logicaffeine_data::crdt::PNCounter {
281    #[inline(always)]
282    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
283        write!(f, "{}", self.value())
284    }
285}
286
287// LWWRegister: show the current value
288impl<T: Showable> Showable for logicaffeine_data::crdt::LWWRegister<T> {
289    #[inline(always)]
290    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
291        self.get().format_show(f)
292    }
293}
294
295// MVRegister: show single value or conflict notation
296impl<T: Showable + Clone + PartialEq> Showable for logicaffeine_data::crdt::MVRegister<T> {
297    #[inline(always)]
298    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
299        let values = self.values();
300        if values.len() == 1 {
301            values[0].format_show(f)
302        } else if values.is_empty() {
303            write!(f, "nothing")
304        } else {
305            // Multiple concurrent values - show as conflict
306            write!(f, "conflict[")?;
307            for (i, val) in values.iter().enumerate() {
308                if i > 0 {
309                    write!(f, ", ")?;
310                }
311                val.format_show(f)?;
312            }
313            write!(f, "]")
314        }
315    }
316}
317
318// Dynamic Value type for tuples
319impl Showable for logicaffeine_data::Value {
320    #[inline(always)]
321    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
322        Display::fmt(self, f)
323    }
324}
325
326// Temporal types: Duration with human-readable formatting
327impl Showable for std::time::Duration {
328    #[inline(always)]
329    fn format_show(&self, f: &mut fmt::Formatter) -> fmt::Result {
330        let nanos = self.as_nanos();
331        if nanos >= 3_600_000_000_000 {
332            // Hours
333            write!(f, "{}h", nanos / 3_600_000_000_000)
334        } else if nanos >= 60_000_000_000 {
335            // Minutes
336            write!(f, "{}min", nanos / 60_000_000_000)
337        } else if nanos >= 1_000_000_000 {
338            // Seconds
339            write!(f, "{}s", nanos / 1_000_000_000)
340        } else if nanos >= 1_000_000 {
341            // Milliseconds
342            write!(f, "{}ms", nanos / 1_000_000)
343        } else if nanos >= 1_000 {
344            // Microseconds
345            write!(f, "{}μs", nanos / 1_000)
346        } else {
347            // Nanoseconds
348            write!(f, "{}ns", nanos)
349        }
350    }
351}
352
353/// The Show verb - prints value with natural formatting
354/// Takes a reference to avoid moving the value.
355#[inline(always)]
356pub fn show<T: Showable>(value: &T) {
357    struct Wrapper<'a, T>(&'a T);
358    impl<T: Showable> Display for Wrapper<'_, T> {
359        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360            self.0.format_show(f)
361        }
362    }
363    println!("{}", Wrapper(value));
364}
365
366pub fn read_line() -> String {
367    let mut buffer = String::new();
368    std::io::stdin().read_line(&mut buffer).unwrap_or(0);
369    buffer.trim().to_string()
370}
371
372#[inline(always)]
373pub fn print<T: Display>(x: T) {
374    print!("{}", x);
375}
376
377#[inline(always)]
378pub fn eprintln<T: Display>(x: T) {
379    eprintln!("{}", x);
380}
381
382#[inline(always)]
383pub fn println<T: Display>(x: T) {
384    println!("{}", x);
385}