serpent-serializer 0.1.0

Serialize Lua values to round-trippable Lua source with cycle and shared-reference handling
Documentation
//! Serialization options.
//!
//! [`Options`] mirrors the serpent option table. The three entry points build
//! their own defaults: [`Options::dump`], [`Options::line`], and
//! [`Options::block`]. [`Options::serialize`] takes the options as given with no
//! defaults, matching the raw serpent entry point.

use crate::value::{Ident, Key, Value};
use std::collections::HashSet;

/// A custom key sort. Receives the key list to reorder in place and the source
/// table's key/value pairs, so the comparator can rank by key or by value.
///
/// serpent calls the comparator as `alphanumsort(keys, table, padding)`. This
/// signature mirrors the first two arguments. Reorder `keys` in place. Look up a
/// key's value in `entries` when the order depends on values.
pub type SortFn = Box<dyn Fn(&mut Vec<Key>, &[(Key, Value)])>;

/// A custom table formatter. Receives `(tag, head, body, tail, level)` and
/// returns the table text. Mirrors serpent's `custom` option.
pub type CustomFn = Box<dyn Fn(&str, &str, &str, &str, usize) -> String>;

/// Options controlling serialization.
///
/// Field names and defaults follow serpent. Booleans use Lua truthiness unless
/// a field notes an exact comparison. Set fields directly, or start from
/// [`Options::dump`], [`Options::line`], or [`Options::block`].
#[derive(Default)]
pub struct Options {
    /// Variable name. When set, output is wrapped in a `do ... return name end`
    /// block with a self-reference section. `dump` sets `_`.
    pub name: Option<String>,
    /// Indentation unit. When set, output is multi-line. `block` sets two spaces.
    pub indent: Option<String>,
    /// Remove spaces around separators. `dump` sets true.
    pub compact: bool,
    /// Force sparse array encoding, skipping nil holes. `dump` sets true.
    pub sparse: bool,
    /// Raise an error on non-serializable values instead of emitting a fallback.
    pub fatal: bool,
    /// Maximum number of array-part elements to emit.
    pub maxnum: Option<usize>,
    /// Maximum table nesting depth to expand. Deeper tables emit `{}`.
    pub maxlevel: Option<usize>,
    /// Maximum cumulative length of emitted element strings.
    pub maxlength: Option<i64>,
    /// Replace functions with an empty-body stub instead of bytecode.
    pub nocode: bool,
    /// Disable the special `1/0`, `-1/0`, `0/0` forms for inf and nan.
    pub nohuge: bool,
    /// Append `--[[...]]` comments up to this depth. The gate is `level < n`, so
    /// a table at level `l` gets a comment when `l < n`. `Some(0)` disables
    /// comments, matching serpent's numeric depth 0. `Some(usize::MAX)` comments
    /// every level, standing in for serpent's `math.huge`. `line` and `block`
    /// set `Some(usize::MAX)`.
    pub comment: Option<usize>,
    /// Sort non-array keys. `line` and `block` set the default sort on.
    pub sortkeys: bool,
    /// Custom key sort. Overrides the default `alphanumsort` when set.
    pub sortfn: Option<SortFn>,
    /// Force a `.` decimal separator. A no-op in this implementation because
    /// Rust float formatting is locale-independent.
    pub fixradix: bool,
    /// Use `__tostring` on tables. Only an explicit `Some(false)` disables it;
    /// `None` and `Some(true)` enable it, matching serpent's `~= false` gate.
    pub metatostring: Option<bool>,
    /// `printf`-style number format. Defaults to `%.17g`.
    pub numformat: Option<String>,
    /// Values to skip, matched by identity or scalar value.
    pub valignore: Option<HashSet<Ident>>,
    /// Scalar values to skip.
    pub valignore_scalar: Option<Vec<Value>>,
    /// Allowlist of keys to serialize. Keys not in the list are dropped. Keyed
    /// by `Key`, so numeric and boolean keys work, not only strings.
    pub keyallow: Option<Vec<Key>>,
    /// Denylist of keys to skip. Keyed by `Key`, so numeric and boolean keys
    /// work, not only strings.
    pub keyignore: Option<Vec<Key>>,
    /// Value type names to skip, such as `function` or `table`.
    pub valtypeignore: Option<HashSet<String>>,
    /// Custom table formatter.
    pub custom: Option<CustomFn>,
}

impl Options {
    /// Options for full serialization: `name = "_"`, `compact`, `sparse`.
    #[must_use]
    pub fn dump() -> Self {
        Options {
            name: Some("_".to_string()),
            compact: true,
            sparse: true,
            ..Options::default()
        }
    }

    /// Options for single-line pretty printing: `sortkeys` and `comment` on.
    #[must_use]
    pub fn line() -> Self {
        Options {
            sortkeys: true,
            comment: Some(usize::MAX),
            ..Options::default()
        }
    }

    /// Options for multi-line pretty printing: two-space `indent`, `sortkeys`,
    /// and `comment` on.
    #[must_use]
    pub fn block() -> Self {
        Options {
            indent: Some("  ".to_string()),
            sortkeys: true,
            comment: Some(usize::MAX),
            ..Options::default()
        }
    }

    /// Options for the raw `serialize` entry point: all defaults, nothing preset.
    #[must_use]
    pub fn serialize() -> Self {
        Options::default()
    }
}