rune/modules/io.rs
1//! I/O functions.
2
3#[cfg(feature = "std")]
4use std::io::{self, Write as _};
5
6use crate as rune;
7#[cfg(feature = "std")]
8use crate::alloc;
9#[cfg(feature = "std")]
10use crate::alloc::fmt::TryWrite;
11use crate::compile;
12use crate::macros::{quote, FormatArgs, MacroContext, TokenStream};
13#[cfg(feature = "std")]
14use crate::runtime::{Address, Formatter, Memory, Output, VmError};
15use crate::{docstring, ContextError, Module};
16
17/// I/O functions.
18#[rune::module(::std::io)]
19pub fn module(
20 #[cfg_attr(not(feature = "std"), allow(unused))] stdio: bool,
21) -> Result<Module, ContextError> {
22 let mut m = Module::from_meta(self::module__meta)?.with_unique("std::io");
23
24 m.item_mut().docs(docstring! {
25 /// The std::io module contains a number of common things
26 /// you’ll need when doing input and output.
27 /// The most core parts of this module are the [print()], [println()],
28 /// and [dbg()] functions which are used to hook up printing for a Rune project.
29 ///
30 /// With complete names:
31 /// * `::std::io::print`
32 /// * `::std::io::println`
33 /// * `::std::io::dbg`
34 ///
35 /// Their definitions can be omitted from the built-in standard library, and
36 /// can then easily be defined by third party modules allowing for printing
37 /// to be hooked up to whatever system you want.
38 })?;
39
40 #[cfg(feature = "std")]
41 m.ty::<io::Error>()?;
42 #[cfg(feature = "std")]
43 m.function_meta(io_error_display_fmt)?;
44 #[cfg(feature = "std")]
45 m.function_meta(io_error_debug_fmt)?;
46
47 #[cfg(feature = "std")]
48 if stdio {
49 m.function_meta(print_impl)?;
50 m.function_meta(println_impl)?;
51
52 m.raw_function("dbg", dbg_impl).build()?.docs(docstring! {
53 /// Debug to output.
54 ///
55 /// This is the actual output hook, and if you install rune modules without
56 /// `I/O` enabled this will not be defined. It is then up to someone else to
57 /// provide an implementation.
58 ///
59 /// # Examples
60 ///
61 /// ```rune
62 /// let number = 10;
63 /// let number = number * 4;
64 ///
65 /// let who = "World";
66 /// let string = format!("Hello {who}");
67 ///
68 /// dbg(number, string);
69 /// ```
70 })?;
71 }
72
73 // These are unconditionally included, but using them might cause a
74 // compilation error unless `::std::io::*` functions are provided somehow.
75 m.macro_meta(dbg_macro)?;
76 m.macro_meta(print_macro)?;
77 m.macro_meta(println_macro)?;
78 Ok(m)
79}
80
81#[rune::function(instance, protocol = DISPLAY_FMT)]
82#[cfg(feature = "std")]
83fn io_error_display_fmt(error: &io::Error, f: &mut Formatter) -> alloc::Result<()> {
84 write!(f, "{error}")
85}
86
87#[rune::function(instance, protocol = DEBUG_FMT)]
88#[cfg(feature = "std")]
89fn io_error_debug_fmt(error: &io::Error, f: &mut Formatter) -> alloc::Result<()> {
90 write!(f, "{error:?}")
91}
92
93#[cfg(feature = "std")]
94fn dbg_impl(
95 memory: &mut dyn Memory,
96 addr: Address,
97 args: usize,
98 out: Output,
99) -> Result<(), VmError> {
100 let stdout = io::stdout();
101 let mut stdout = stdout.lock();
102
103 for value in memory.slice_at(addr, args)? {
104 writeln!(stdout, "{value:?}").map_err(VmError::panic)?;
105 }
106
107 memory.store(out, ())?;
108 Ok(())
109}
110
111/// Debug print the given argument.
112///
113/// Everything in rune can be "debug printed" in one way or another. This is
114/// provided as a cheap an dirty way to introspect values.
115///
116/// See also the [`dbg!`] macro.
117///
118/// # Examples
119///
120/// ```rune
121/// let number = 10;
122/// let number = number * 4;
123///
124/// let who = "World";
125/// let string = format!("Hello {}", who);
126///
127/// dbg!(number, string);
128/// ```
129#[rune::macro_(path = dbg)]
130pub(crate) fn dbg_macro(
131 cx: &mut MacroContext<'_, '_, '_>,
132 stream: &TokenStream,
133) -> compile::Result<TokenStream> {
134 Ok(quote!(::std::io::dbg(#stream)).into_token_stream(cx)?)
135}
136
137/// Prints to output.
138///
139/// Output printing is performed by calling the [`print()`] function, this is
140/// just a convenience wrapper around it which allows for formatting.
141///
142/// # Examples
143///
144/// ```rune
145/// let who = "World";
146/// print!("Hello {}!", who);
147/// ```
148#[rune::macro_(path = print)]
149pub(crate) fn print_macro(
150 cx: &mut MacroContext<'_, '_, '_>,
151 stream: &TokenStream,
152) -> compile::Result<TokenStream> {
153 let args = FormatArgs::parse(cx, stream)?;
154 let expanded = args.expand(cx)?;
155 Ok(quote!(::std::io::print(#expanded)).into_token_stream(cx)?)
156}
157
158/// Prints to output.
159///
160/// This is the actual output hook, and if you install rune modules without
161/// `I/O` enabled this will not be defined. It is then up to someone else to
162/// provide an implementation.
163///
164/// See also the [`print!`] macro.
165///
166/// # Examples
167///
168/// ```rune
169/// print("Hi!");
170/// ```
171#[rune::function(path = print)]
172#[cfg(feature = "std")]
173fn print_impl(m: &str) -> Result<(), VmError> {
174 let stdout = io::stdout();
175 let mut stdout = stdout.lock();
176 write!(stdout, "{m}").map_err(VmError::panic)?;
177 Ok(())
178}
179
180/// Prints to output, with a newline.
181///
182/// Output printing is performed by calling the [`println()`] function, this is
183/// just a convenience wrapper around it which allows for formatting.
184///
185/// # Examples
186///
187/// ```rune
188/// let who = "World";
189/// println!("Hello {}!", who);
190/// ```
191#[rune::macro_(path = println)]
192pub(crate) fn println_macro(
193 cx: &mut MacroContext<'_, '_, '_>,
194 stream: &TokenStream,
195) -> compile::Result<TokenStream> {
196 let args = FormatArgs::parse(cx, stream)?;
197 let expanded = args.expand(cx)?;
198 Ok(quote!(::std::io::println(#expanded)).into_token_stream(cx)?)
199}
200
201/// Prints to output, with a newline.
202///
203/// This is the actual output hook, and if you install rune modules without
204/// `I/O` enabled this will not be defined. It is then up to someone else to
205/// provide an implementation.
206///
207/// # Examples
208///
209/// ```rune
210/// println("Hi!");
211/// ```
212#[rune::function(path = println)]
213#[cfg(feature = "std")]
214fn println_impl(message: &str) -> Result<(), VmError> {
215 let stdout = io::stdout();
216 let mut stdout = stdout.lock();
217 writeln!(stdout, "{message}").map_err(VmError::panic)?;
218 Ok(())
219}