serde_luaq 0.2.1

A Lua %q serialization file format
Documentation
# `serde_luaq`   [![Latest Version]][crates.io] [![Docs version]][docs.rs]

[Latest Version]: https://img.shields.io/crates/v/serde_luaq.svg
[crates.io]: https://crates.io/crates/serde_luaq
[Docs version]: https://img.shields.io/docsrs/serde_luaq.svg
[docs.rs]: https://docs.rs/serde_luaq/

> [!NOTE]
> This library is still a work in progress, and there are no API stability guarantees.

`serde_luaq` is a library for deserialising (and eventually, serialising) simple, JSON-equivalent
data structures from Lua 5.4 source code, _without requiring Lua itself_ (unlike [`mlua`][mlua]).

The goal is to be able to read state from software (mostly games) which is serialised using
[Lua `%q` formatting][format] (and similar techniques) _without_ requiring arbitrary code execution.

This library consists of four parts:

- A [`LuaValue`][luavalue] `enum`, which describes Lua 5.4's basic data types (`nil`, boolean,
  string, number, table).

- A [`peg`][peg]-based parser for parsing a `&[u8]` (containing Lua) into a `LuaValue`.

- A [Serde][serde]-based `Deserialize` implementation for converting a `LuaValue` into your own
  data types.

- _Optional_ lossy converter to and from `serde_json`'s `Value` type.

## Goal

For example, you could have a Lua script like this:

```lua
a = 1
b = {1, 2, 3}
c = {
    ["foo"] = "bar",
}
```

And define some a schema using Serde traits:

```rust
#[derive(Deserialize, PartialEq, Debug)]
struct ComplexType {
    foo: String,
}

#[derive(Deserialize, PartialEq, Debug)]
struct Test {
    a: u32,
    b: Vec<u32>,
    c: ComplexType,
}
```

Then deserialise it with:

```rust
use serde_luaq::{from_slice, LuaFormat};

let parsed: Test = serde_luaq::from_slice(
  input,
  LuaFormat::Script,
  /* maximum table depth */ 16,
).unwrap();

assert_eq!(parsed, Test {
  a: true,
  b: vec![1, 2, 3],
  c: ComplexType { foo: "bar".to_string() },
});
```

## Parser features

- [x] Input formats
  - [x] Bare Lua value expression, similar to JSON (`{["hello"] = "world"}`)
  - [x] Lua return statement (`return {["hello"] = "world"}`)
  - [x] Script with identifier assignments _only_ (`hello = "world"`)
- [ ] Serde (partial)
  - [x] Deserialising
  - [ ] Serialising
- [x] _Lossy_ `serde_json` interoperability
  - [x] `LuaValue` -> `serde_json::Value`
  - [x] `serde_json::Value` -> `LuaValue`

## Lua language features

This library aims to implement a _subset_ of Lua 5.4 that is equivalent to the subset of JavaScript
that a JSON parser would implement:

- [x] `nil`
- [x] Booleans (`true`, `false`)
- [x] [Numbers][lua3.1]
  - [x] [Integers][lua3.1] `i64` only
    - [x] Decimal integers (`123`)
      - [x] Coercion to float for decimal numbers `< i64::MIN` or `> i64::MAX` ([Lua 5.4][lua8])
    - [x] Hexadecimal integers (`0xFF`)
      - [x] Wrapping large hexadecimal numbers to `i64`
  - [x] [Floats][lua3.1] `f64` only
    - [x] Decimal floats with decimal point and optional exponent (`3.14`, `0.314e1`)
    - [x] Decimal floats with mandatory exponent (`3e14`)
    - [x] Hexadecimal floating points (`0x.ABCDEFp+24`) (*not supported on WASM before v0.2.1*)
    - [x] Positive and negative infinity (`1e9999`, `-1e9999`)
    - [x] NaN (`(0/0)`)
- [x] [Strings][lua3.1]
  - [x] Strings in single quotes (`'`)
  - [x] Strings in double quotes (`"`)
  - [x] Strings in long brackets (`[[string]]`, `[==[string]==]`) (_up to 5 `=` deep_)
  - [x] Arbitrary 8-bit binary data inside strings (like `[u8]`)
  - [x] Escapes in quoted strings:
    - [x] C-like backslash-escapes (`abfnrtv\"'`)
    - [x] Escaped line breaks (`\\\n`, `\\\r`, `\\\r\n`, `\\\n\r`)
    - [x] `\z` whitespace span escapes (`str\z    ing` == `string`)
    - [x] Decimal escape sequences (`\1`, `\01`, `\001`)
    - [x] Hexadecimal escape sequences (`\x01`)
    - [x] UTF-8 escape sequences (`\u{1F4A9}`)
      - [x] Sequences allowed by RFC 2279 but not RFC 3629 (`\u{D800}`, `\u{7FFFFFFF}`)
- [x] [Tables][lua3.4.9]
  - [x] Key-values / expression keys (`{["foo"] = "bar"}`, `{[1234]="bar"}`)
  - [x] Name-values / identifier keys (`{foo = "bar"}`)
    - [x] Identifier validation (Lua 5.4-style)
  - [x] Values / implicit keys (`{"bar"}`)
  - [x] Mixed key types
  - [x] Recursion depth limits

This library is not designed to replace Lua, nor execute arbitrary Lua code, so these Lua features
are _intentionally unsupported_:

- Arithmetic operators (`+`, `-`, `*`, `/`...)
- Bitwise operators (`<<`, `>>`, `&`, `|`, `~`...)
- Blocks and control structures (`if`, `break`, `do`, `end`, `for`, `goto`, `repeat`, `until`, `while`...)
- Comments
- Function calls
- Function definitions
- Length operator (`#`)
- Locale-specific behaviour (`3,14159`)
- Logical operators (`and`, `or`, `not`)
- Newline character normalisation in strings (`\r\n` => `\n` on UNIX, `\n` => `\r\n` on Windows)
- Parentheses, except for `(0/0)` (NaN)
- Pointers (light userdata)
- Referencing other variables (`a = 10; b = a`)
- Relational operators (`==`, `~=`, `<`, `>`...)
- String concatenation (`"hello" .. " world"`)
- Threads and coroutines
- Updating other variables (`a = {}; a.b = 'foo'`)
- Userdata
- Vararg assignments and destructuring (`a, b = 1, 2`)
- Variable attributes and visibility modifiers (`local <const> a = 10`)

If you want to use these language features or otherwise need to run arbitrary Lua code, look at
something like [`mlua`][mlua], which links to `liblua`, and also provides `serde` bindings.

## Known users of Lua serialisation

- [Programming in Lua][pil12.1.1] has a function that dumps a Lua table into a file as a script.

- [SaveData][]: Love2D library, emits a `return` statement which is loaded by evaluating the string.

- [Balatro][] (`engine/string_packer.lua`) is a modified version of `SaveData` that also compresses
  with `deflate` for save games and settings (`.jkr` files).

- World of Warcraft: addon state, written to `WTF/{Account,SavedVariables}/**/*.lua` as scripts that
  set variables.

## License

This project is dual-licensed under the terms of the Apache-2.0 and MIT licenses.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
this project by you, as defined in the Apache-2.0 license, shall be dual licensed under the terms of
the Apache-2.0 and MIT licenses, without any additional terms or conditions. 

`serde_luaq` does not include any copy of Lua itself. To ensure maximal compatibility with Lua's
syntax, some of its test cases are derived from [Lua's test suite][luatest].

[Balatro]: https://www.playbalatro.com/
[format]: https://www.lua.org/manual/5.4/manual.html#pdf-string.format
[lua3.1]: https://www.lua.org/manual/5.4/manual.html#3.1
[lua3.4.9]: https://www.lua.org/manual/5.4/manual.html#3.4.9
[lua8]: https://www.lua.org/manual/5.4/manual.html#8
[luatest]: https://github.com/lua/lua/tree/master/testes
[luavalue]: https://docs.rs/serde_luaq/latest/serde_luaq/enum.LuaValue.html
[mlua]: https://github.com/mlua-rs/mlua
[peg]: https://docs.rs/peg/latest/peg/
[pil12.1.1]: https://www.lua.org/pil/12.1.1.html
[SaveData]: https://github.com/BroccoliRaab/SaveData
[serde]: https://serde.rs/