lua_types/filehandle.rs
1//! Minimal file-handle abstraction shared between `lua-vm` (hook type) and
2//! `lua-stdlib` (io library).
3//!
4//! `std::fs` is banned in `lua-stdlib` by PORTING.md ยง1. The concrete
5//! implementation (backed by `std::fs::File`) lives in `lua-cli` and is
6//! installed on [`lua_vm::state::GlobalState`] via the `FileOpenHook` /
7//! `FileRemoveHook` / `FileRenameHook` function pointers. Those hooks return
8//! `std::io::Result` (not `LuaError`): only `std::io::Error` carries
9//! `raw_os_error()`, and `io.open`/`os.remove`/`os.rename` must report the real
10//! errno as their third return value the way C's `luaL_fileresult` does โ a
11//! `LuaError` return would drop it (#301). This trait is the shared seam that
12//! lets `lua-stdlib` program against file handles without importing `std::fs`.
13//!
14//! On the `wasm32-unknown-unknown` host boundary there is no `io::Error` to pass
15//! by value, so the `open_file` host import encodes the outcome in its `i32`
16//! return: `>= 0` is a live handle id, `-1` is failure with no errno available
17//! (mapped to a `raw_os_error`-less error โ a 2-value `(nil, msg)` result), and
18//! `<= -2` encodes `errno = -id` (mapped via `io::Error::from_raw_os_error`).
19//! See `lua-wasm`'s `imported_file_open` and the JS host's `openFile`.
20//!
21//! ## Trait design
22//! The trait mirrors the subset of `LuaFileOps` (defined in `lua-stdlib`) that
23//! is required to run the built-in io library at the level needed for
24//! `attrib.lua`-class tests: sequential write, byte-by-byte read, flush, and
25//! seek. `LuaFileOps` in `lua-stdlib` extends this trait so that a single
26//! concrete type (the `FsFile` in `lua-cli`) satisfies both.
27
28use std::io::{self, SeekFrom};
29
30/// Capabilities required by the io library from an OS file handle.
31///
32/// Designed to be object-safe (`Box<dyn LuaFileHandle>`). Implementations
33/// backed by `std::fs::File` live in `lua-cli`; implementations for the
34/// standard streams live in `lua-stdlib/src/io_lib.rs`.
35pub trait LuaFileHandle: Send {
36 /// Read one byte from the handle; return it as `i32`, or `-1` on EOF/error.
37 fn read_byte(&mut self) -> i32;
38
39 /// Push back a previously-read byte so the next `read_byte` returns it.
40 fn unread_byte(&mut self, byte: i32);
41
42 /// Write a byte slice; return the number of bytes actually written.
43 fn write_bytes(&mut self, data: &[u8]) -> io::Result<usize>;
44
45 /// Flush any write buffers to the underlying OS handle.
46 fn flush(&mut self) -> io::Result<()>;
47
48 /// Seek within the file.
49 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64>;
50
51 /// Return the current file position without moving it.
52 fn tell(&mut self) -> io::Result<u64>;
53
54 /// Clear the error/EOF flag on the handle.
55 fn clear_error(&mut self);
56
57 /// Return `true` if the handle has a pending error.
58 fn has_error(&self) -> bool;
59
60 /// Return the last pending OS error as `(errno, message)` when available.
61 ///
62 /// Some real Lua programs probe platform behavior through specific errno
63 /// values. LuaRocks' macOS directory detection, for example, expects
64 /// reading an opened directory to report `EISDIR` rather than look like EOF.
65 fn last_error_info(&self) -> Option<(i32, String)> {
66 None
67 }
68
69 /// Control write buffering. Mode values mirror `file:setvbuf` option order:
70 /// 0 = no buffering, 1 = full buffering, 2 = line buffering.
71 fn set_buf_mode(&mut self, _mode: i32, _size: usize) -> io::Result<()> {
72 Ok(())
73 }
74}