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