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