lua_stdlib/init.rs
1//! Initialization of standard libraries for Lua.
2//!
3//! Opens all standard libraries via `require`-style loading and registers
4//! them into the global table.
5//!
6//! Port of `src/linit.c` (66 lines, 1 function).
7
8use crate::state_stub::{LuaState, LuaStateStubExt as _};
9use lua_types::error::LuaError;
10
11// Matches types.tsv: lua_CFunction → fn(&mut LuaState) -> Result<usize, LuaError>
12type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
13
14// ── Library-name byte-string constants ────────────────────────────────────
15//
16// These replace the C macros from lualib.h and lauxlib.h:
17// LUA_GNAME = "_G" (lauxlib.h)
18// LUA_LOADLIBNAME = "package" (lualib.h)
19// LUA_COLIBNAME = "coroutine" (lualib.h)
20// LUA_TABLIBNAME = "table" (lualib.h)
21// LUA_IOLIBNAME = "io" (lualib.h)
22// LUA_OSLIBNAME = "os" (lualib.h)
23// LUA_STRLIBNAME = "string" (lualib.h)
24// LUA_MATHLIBNAME = "math" (lualib.h)
25// LUA_UTF8LIBNAME = "utf8" (lualib.h)
26// LUA_DBLIBNAME = "debug" (lualib.h)
27//
28// Per PORTING.md §3.1 all Lua string data uses &[u8], not &str.
29
30// {LUA_GNAME, luaopen_base},
31// {LUA_LOADLIBNAME, luaopen_package},
32// {LUA_COLIBNAME, luaopen_coroutine},
33// {LUA_TABLIBNAME, luaopen_table},
34// {LUA_IOLIBNAME, luaopen_io},
35// {LUA_OSLIBNAME, luaopen_os},
36// {LUA_STRLIBNAME, luaopen_string},
37// {LUA_MATHLIBNAME, luaopen_math},
38// {LUA_UTF8LIBNAME, luaopen_utf8},
39// {LUA_DBLIBNAME, luaopen_debug},
40// {NULL, NULL}
41// };
42//
43// PORT NOTE: C sentinel `{NULL, NULL}` dropped — Rust slices carry their
44// own length, so no terminator is needed.
45//
46// PORT NOTE: Per PORTING.md §7, `luaopen_X` → `open` inside the module
47// (e.g. `crate::base::open`, `crate::string_lib::open`). As of Phase A
48// the individual stdlib modules exported inconsistent names:
49// base.rs → `pub fn open` (canonical; matches here)
50// string_lib.rs → `pub fn luaopen_string` (needs rename in Phase B)
51// table_lib.rs → `pub fn open_table` (needs rename in Phase B)
52// math_lib.rs → `pub fn luaopen_math` (needs rename in Phase B)
53// io_lib.rs → `pub fn luaopen_io` (needs rename in Phase B)
54// os_lib.rs → `pub fn open_os` (needs rename in Phase B)
55// utf8_lib, debug_lib, coro_lib, loadlib → not yet ported (Phase B)
56// Phase B should rename every stdlib opener to `pub fn open` and update
57// this table accordingly.
58static LOADED_LIBS: &[(&[u8], LuaCFunction)] = &[
59 (b"_G", crate::base::open),
60 #[cfg(feature = "package")]
61 (b"package", crate::loadlib::luaopen_package),
62 #[cfg(feature = "coroutine")]
63 (b"coroutine", crate::coro_lib::open_coroutine),
64 (b"table", crate::table_lib::open_table),
65 #[cfg(feature = "io")]
66 (b"io", crate::io_lib::luaopen_io),
67 #[cfg(feature = "os")]
68 (b"os", crate::os_lib::open_os),
69 (b"string", crate::string_lib::luaopen_string),
70 (b"math", crate::math_lib::luaopen_math),
71 #[cfg(feature = "utf8")]
72 (b"utf8", crate::utf8_lib::open_utf8),
73 #[cfg(feature = "debug")]
74 (b"debug", crate::debug_lib::open_debug),
75];
76
77// const luaL_Reg *lib;
78// /* "require" functions from 'loadedlibs' and set results to global table */
79// for (lib = loadedlibs; lib->func; lib++) {
80// luaL_requiref(L, lib->name, lib->func, 1);
81// lua_pop(L, 1); /* remove lib */
82// }
83// }
84//
85// PORT NOTE: `LUALIB_API` → `pub` (PORTING.md §4.1 / macros.tsv).
86// `luaL_requiref(L, name, func, 1)` → `state.require_lib(name, func, true)?`
87// The final `1` argument means "set global" — the loaded module value is
88// assigned to the global table under `name` and the value left on the
89// stack is then discarded by `lua_pop(L, 1)`.
90// `lua_pop(L, 1)` → `state.pop_n(1)` (macros.tsv).
91/// Open all standard Lua libraries into `state`, registering each into the
92/// global table.
93///
94/// Corresponds to `luaL_openlibs` in `linit.c`.
95pub fn open_libs(state: &mut LuaState) -> Result<(), LuaError> {
96 // Whether this version ships `utf8` (a 5.3 addition) is the #234 capability
97 // matrix — the reference-backed single source — not a second inline version
98 // check. The `#[cfg(feature = "utf8")]`-gated registration entries in
99 // LOADED_LIBS still control compile-time availability.
100 let has_utf8 = state
101 .global()
102 .lua_version
103 .supports(lua_types::Feature::Utf8Lib);
104 for &(name, func) in LOADED_LIBS {
105 if name == b"utf8".as_slice() && !has_utf8 {
106 continue;
107 }
108 state.require_lib(name, func, true)?;
109 state.pop_n(1);
110 }
111 // `bit32` is present on 5.2 and 5.3 and removed in 5.4; the version dimension
112 // comes from the #234 matrix, the compile-time dimension from the feature
113 // gate (registration = `cfg(feature) && version.supports(Bit32Lib)`).
114 #[cfg(feature = "bit32")]
115 if state
116 .global()
117 .lua_version
118 .supports(lua_types::Feature::Bit32Lib)
119 {
120 state.require_lib(b"bit32", crate::bit32_lib::open_bit32, true)?;
121 state.pop_n(1);
122 }
123 Ok(())
124}
125
126// ──────────────────────────────────────────────────────────────────────────
127// PORT STATUS
128// source: src/linit.c (66 lines, 1 function)
129// target_crate: lua-stdlib
130// confidence: high
131// todos: 1
132// port_notes: 3
133// unsafe_blocks: 0
134// notes: Trivial file. Cross-crate refs (state.require_lib,
135// state.pop_n, crate::*::open) resolve in Phase B.
136// Phase B must also reconcile inconsistent open-function
137// names in the existing stdlib modules (see PORT NOTEs).
138// ──────────────────────────────────────────────────────────────────────────