Skip to main content

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}