diff --git a/docs/src/modules/List.md b/docs/src/modules/List.md new file mode 100644 index 0000000..0f5c2a6 --- /dev/null +++ b/docs/src/modules/List.md @@ -0,0 +1,451 @@ +--- +desc: + "A Python-style list class providing common operations to create, modify, and + query sequences of values." +--- + +# `List` + +A Python-style list class providing common operations to create, modify, and +query sequences of values. + +## Usage + +```lua +lsist = require "mods.List" + +ls = List({ "a" }):append("b") +print(ls:contains("b")) --> true +print(ls:index("b")) --> 2 +``` + +## Functions + +**Predicates**: + +| Function | Description | +| ------------- | ----------------------------------------------- | +| [`all`](#all) | Return true if all values match the predicate. | +| [`any`](#any) | Return true if any value matches the predicate. | + +**Mutation**: + +| Function | Description | +| --------------------- | -------------------------------------------------------------------- | +| [`append`](#append) | Append a value to the end of the list. | +| [`clear`](#clear) | Remove all elements from the list. | +| [`extend`](#extend) | Extend the list with another list. | +| [`extract`](#extract) | Extract values matching the predicate and remove them from the list. | +| [`insert`](#insert) | Insert a value at the given position. | +| [`insert`](#insert) | Append a value to the end of the list. | +| [`pop`](#pop) | Remove and return the last element. | +| [`pop`](#pop) | Remove and return the element at the given position. | +| [`prepend`](#prepend) | Insert a value at the start of the list. | +| [`remove`](#remove) | Remove the first matching value. | +| [`sort`](#sort) | Sort the list in place. | + +**Copying**: + +| Function | Description | +| --------------- | ---------------------------------- | +| [`copy`](#copy) | Return a shallow copy of the list. | + +**Query**: + +| Function | Description | +| ----------------------- | ----------------------------------------------------------- | +| [`contains`](#contains) | Return true if the list contains the value. | +| [`count`](#count) | Count how many times a value appears. | +| [`index`](#index) | Return the index of the first matching value. | +| [`index_if`](#index-if) | Return the index of the first value matching the predicate. | +| [`len`](#len) | Return the number of elements in the list. | + +**Access**: + +| Function | Description | +| ----------------- | ------------------------------------------- | +| [`first`](#first) | Return the first element or `nil` if empty. | +| [`last`](#last) | Return the last element or `nil` if empty. | + +**Transform**: + +| Function | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------- | +| [`difference`](#difference) | Return a new list with values not in the given list. | +| [`drop`](#drop) | Return a new list without the first n elements. | +| [`filter`](#filter) | Return a new list with values matching the predicate. | +| [`flatten`](#flatten) | Flatten one level of nested lists. | +| [`foreach`](#foreach) | Apply a function to each element (for side effects). Returns nil. | +| [`group_by`](#group-by) | Group list values by a computed key. | +| [`intersection`](#intersection) | Return values that are also present in the given list. Order is preserved from the original list. | +| [`invert`](#invert) | Invert values to indices in a new table. | +| [`join`](#join) | Join list values into a string. | +| [`map`](#map) | Return a new list by mapping each value. | +| [`reduce`](#reduce) | Reduce the list to a single value using an accumulator. | +| [`reverse`](#reverse) | Return a new list with items reversed. | +| [`setify`](#setify) | Convert the list to a set. | +| [`slice`](#slice) | Return a new list containing items from i to j (inclusive). | +| [`take`](#take) | Return the first n elements as a new list. | +| [`uniq`](#uniq) | Return a new list with duplicates removed (first occurrence kept). | +| [`zip`](#zip) | Zip two lists into a list of 2-element tables. | + +### Predicates + +Boolean checks for list-wide conditions. + +#### `all` + +Return true if all values match the predicate. + +```lua +is_even = function(v) return v % 2 == 0 end +ok = List({ 2, 4 }):all(is_even) --> true +``` + +> [!NOTE] Empty lists return `true`. + +#### `any` + +Return true if any value matches the predicate. + +```lua +has_len_2 = function(v) return #v == 2 end +ok = List({ "a", "bb" }):any(has_len_2) --> true +``` + +### Mutation + +In-place operations that modify the current list. + +#### `append` + +Append a value to the end of the list. + +```lua +ls = List({ "a" }):append("b") --> { "a", "b" } +``` + +#### `clear` + +Remove all elements from the list. + +```lua +ls = List({ "a", "b" }):clear() --> { } +``` + +#### `extend` + +Extend the list with another list. + +```lua +ls = List({ "a" }):extend({ "b", "c" }) --> { "a", "b", "c" } +``` + +#### `extract` + +Extract values matching the predicate and remove them from the list. + +```lua +ls = List({ "a", "bb", "c" }) +is_len_1 = function(v) return #v == 1 end +ex = ls:extract(is_len_1) --> ex = { "a", "c" }, ls = { "bb" } +``` + +#### `insert` + +Insert a value at the given position. + +```lua +ls = List({ "a", "c" }):insert(2, "b") --> { "a", "b", "c" } +``` + +#### `insert` + +Append a value to the end of the list. + +```lua +ls = List({ "a", "b" }):insert("b") --> { "a", "b", "c" } +``` + +#### `pop` + +Remove and return the last element. + +```lua +ls = List({ "a", "b" }) +v = ls:pop() --> v == "b"; ls is { "a" } +``` + +#### `pop` + +Remove and return the element at the given position. + +```lua +ls = List({ "a", "b", "c" }) +v = ls:pop(2) --> v == "b"; ls is { "a", "c" } +``` + +#### `prepend` + +Insert a value at the start of the list. + +```lua +ls = List({ "b", "c" }) +ls:prepend("a") --> { "a", "b", "c" } +``` + +#### `remove` + +Remove the first matching value. + +```lua +ls = List({ "a", "b", "b" }) +ls:remove("b") --> { "a", "b" } +``` + +#### `sort` + +Sort the list in place. + +```lua +ls = List({ 3, 1, 2 }) +ls:sort() --> { 1, 2, 3 } +``` + +### Copying + +Operations that return copied list data. + +#### `copy` + +Return a shallow copy of the list. + +```lua +c = List({ "a", "b" }):copy() --> { "a", "b" } +``` + +### Query + +Read-only queries for membership, counts, and indices. + +#### `contains` + +Return true if the list contains the value. + +```lua +ok = List({ "a", "b" }):contains("b") --> true +``` + +#### `count` + +Count how many times a value appears. + +```lua +n = List({ "a", "b", "b" }):count("b") --> 2 +``` + +#### `index` + +Return the index of the first matching value. + +```lua +i = List({ "a", "b", "c", "b" }):index("b") --> 2 +``` + +#### `index_if` + +Return the index of the first value matching the predicate. + +```lua +gt_1 = function(x) return x > 1 end +i = List({ 1, 2, 3 }):index_if(gt_1) --> 2 +``` + +#### `len` + +Return the number of elements in the list. + +```lua +n = List({ "a", "b", "c" }):len() --> 3 +``` + +> [!NOTE] Uses Lua's `#` operator, so length is reliable for contiguous +> array-like lists. + +### Access + +Direct element access helpers. + +#### `first` + +Return the first element or `nil` if empty. + +```lua +v = List({ "a", "b" }):first() --> "a" +``` + +#### `last` + +Return the last element or `nil` if empty. + +```lua +v = List({ "a", "b" }):last() --> "b" +``` + +### Transform + +Non-mutating transformations and derived-list operations. + +#### `difference` + +Return a new list with values not in the given list. + +```lua +d = List({ "a", "b", "c" }):difference({ "b" }) --> { "a", "c" } +``` + +#### `drop` + +Return a new list without the first n elements. + +```lua +t = List({ "a", "b", "c" }):drop(1) --> { "b", "c" } +``` + +#### `filter` + +Return a new list with values matching the predicate. + +```lua +is_len_1 = function(v) return #v == 1 end +f = List({ "a", "bb", "c" }):filter(is_len_1) --> { "a", "c" } +``` + +#### `flatten` + +Flatten one level of nested lists. + +```lua +f = List({ { "a", "b" }, { "c" } }):flatten() --> { "a", "b", "c" } +``` + +#### `foreach` + +Apply a function to each element (for side effects). Returns nil. + +```lua +List({ "a", "b" }):foreach(print) +--> prints -> a +--> prints -> b +``` + +#### `group_by` + +Group list values by a computed key. + +```lua +words = { "aa", "b", "ccc", "dd" } +g = List(words):group_by(string.len) --> { {"b"}, { "aa", "dd" }, { "ccc" } } +``` + +#### `intersection` + +Return values that are also present in the given list. Order is preserved from +the original list. + +```lua +i = List({ "a", "b", "a", "c" }):intersection({ "a", "c" }) +--> { "a", "a", "c" } +``` + +#### `invert` + +Invert values to indices in a new table. + +```lua +t = List({ "a", "b", "c" }):invert() --> { a = 1, b = 2, c = 3 } +``` + +#### `join` + +Join list values into a string. + +```lua +s = List({ "a", "b", "c" }):join(",") --> "a,b,c" +``` + +#### `map` + +Return a new list by mapping each value. + +```lua +to_upper = function(v) return v:upper() end +m = List({ "a", "b" }):map(to_upper) --> { "A", "B" } +``` + +#### `reduce` + +Reduce the list to a single value using an accumulator. + +```lua +add = function(acc, v) return acc + v end +sum = List({ 1, 2, 3 }):reduce(add, 0) --> 6 +sum = List({ 1, 2, 3 }):reduce(add, 10) --> 16 +``` + +> [!NOTE] +> +> If init is `nil`, the first element is used as the initial value. Empty lists +> return init (or `nil` if init is `nil`). + +#### `reverse` + +Return a new list with items reversed. + +```lua +r = List({ "a", "b", "c" }):reverse() --> { "c", "b", "a" } +``` + +#### `setify` + +Convert the list to a set. + +```lua +s = List({ "a", "b", "a" }):setify() --> { a = true, b = true } +``` + +#### `slice` + +Return a new list containing items from i to j (inclusive). + +```lua +t = List({ "a", "b", "c", "d" }):slice(2, 3) --> { "b", "c" } +``` + +> [!NOTE] Supports negative indices (-1 is last element). + +#### `take` + +Return the first n elements as a new list. + +```lua +t = List({ "a", "b", "c" }):take(2) --> { "a", "b" } +``` + +#### `uniq` + +Return a new list with duplicates removed (first occurrence kept). + +```lua +u = List({ "a", "b", "a", "c" }):uniq() --> { "a", "b", "c" } +``` + +#### `zip` + +Zip two lists into a list of 2-element tables. + +```lua +z = List({ "a", "b" }):zip({ 1, 2 }) --> { {"a",1}, {"b",2} } +``` + +> [!NOTE] Length is the minimum of both lists. diff --git a/docs/src/modules/Set.md b/docs/src/modules/Set.md new file mode 100644 index 0000000..d7fc972 --- /dev/null +++ b/docs/src/modules/Set.md @@ -0,0 +1,259 @@ +--- +desc: + "A Python-style set class providing common operations to create, modify, and + query collections of unique values." +--- + +# `Set` + +A Python-style set class providing common operations to create, modify, and +query collections of unique values. + +## Usage + +```lua +Set = require "mods.Set" + +s = Set({ "a" }) +print(s:contains("a")) --> true +``` + +## Functions + +**Mutation**: + +| Function | Description | +| ------------------------------------------------------------- | ----------------------------------------------------------- | +| [`add`](#add) | Add an element to the set. | +| [`clear`](#clear) | Remove all elements from the set. | +| [`difference_update`](#difference-update) | Remove elements found in another set (in place). | +| [`discard`](#discard) | Remove an element if present, do nothing otherwise. | +| [`intersection_update`](#intersection-update) | Keep only elements common to both sets (in place). | +| [`pop`](#pop) | Remove and return an arbitrary element. | +| [`symmetric_difference_update`](#symmetric-difference-update) | Update the set with elements not shared by both (in place). | +| [`update`](#update) | Add all elements from another set (in place). | + +**Copying**: + +| Function | Description | +| ----------------------------------------------- | ----------------------------------------------- | +| [`copy`](#copy) | Return a shallow copy of the set. | +| [`difference`](#difference) | Return elements in this set but not in another. | +| [`intersection`](#intersection) | Return elements common to both sets. | +| [`symmetric_difference`](#symmetric-difference) | Return elements not shared by both sets. | +| [`union`](#union) | Return a new set with all elements from both. | + +**Predicates**: + +| Function | Description | +| --------------------------- | ---------------------------------------------------------------- | +| [`isdisjoint`](#isdisjoint) | Return true if sets have no elements in common. | +| [`isempty`](#isempty) | Return true if the set has no elements. | +| [`issubset`](#issubset) | Return true if all elements of this set are also in another set. | +| [`issuperset`](#issuperset) | Return true if this set contains all elements of another set. | + +**Query**: + +| Function | Description | +| ----------------------- | ----------------------------------------- | +| [`contains`](#contains) | Return true if the set contains `v`. | +| [`len`](#len) | Return the number of elements in the set. | + +**Transform**: + +| Function | Description | +| ------------------- | --------------------------------------- | +| [`map`](#map) | Return a new set by mapping each value. | +| [`values`](#values) | Return a list of all values in the set. | + +### Mutation + +In-place operations that mutate the current set. + +#### `add` + +Add an element to the set. + +```lua +s = Set({ "a" }):add("b") --> s contains "a", "b" +``` + +#### `clear` + +Remove all elements from the set. + +```lua +s = Set({ "a", "b" }):clear() --> s is empty +``` + +#### `difference_update` + +Remove elements found in another set (in place). + +```lua +s = Set({ "a", "b" }):difference_update(Set({ "b" })) --> s contains "a" +``` + +#### `discard` + +Remove an element if present, do nothing otherwise. + +```lua +s = Set({ "a", "b" }):discard("b") --> s contains "a" +``` + +#### `intersection_update` + +Keep only elements common to both sets (in place). + +```lua +s = Set({ "a", "b" }):intersection_update(Set({ "b", "c" })) +--> s contains "b" +``` + +#### `pop` + +Remove and return an arbitrary element. + +```lua +v = Set({ "a", "b" }):pop() --> v is either "a" or "b" +``` + +#### `symmetric_difference_update` + +Update the set with elements not shared by both (in place). + +```lua +s = Set({ "a", "b" }):symmetric_difference_update(Set({ "b", "c" })) +--> s contains "a", "c" +``` + +#### `update` + +Add all elements from another set (in place). + +```lua +s = Set({ "a" }):update(Set({ "b" })) --> s contains "a", "b" +``` + +### Copying + +Non-mutating set operations that return new set instances. + +#### `copy` + +Return a shallow copy of the set. + +```lua +c = Set({ "a" }):copy() --> c is a new set with "a" +``` + +#### `difference` + +Return elements in this set but not in another. + +```lua +d = Set({ "a", "b" }):difference(Set({ "b" })) --> d contains "a" +``` + +#### `intersection` + +Return elements common to both sets. + +```lua +i = Set({ "a", "b" }):intersection(Set({ "b", "c" })) --> i contains "b" +``` + +#### `symmetric_difference` + +Return elements not shared by both sets. + +```lua +d = Set({ "a", "b" }):symmetric_difference(Set({ "b", "c" })) +--> d contains "a", "c" +``` + +#### `union` + +Return a new set with all elements from both. + +```lua +s = Set({ "a" }):union(Set({ "b" })) --> s contains "a", "b" +``` + +### Predicates + +Boolean checks about set relationships and emptiness. + +#### `isdisjoint` + +Return true if sets have no elements in common. + +```lua +ok = Set({ "a" }):isdisjoint(Set({ "b" })) --> true +``` + +#### `isempty` + +Return true if the set has no elements. + +```lua +empty = Set({}):isempty() --> true +``` + +#### `issubset` + +Return true if all elements of this set are also in another set. + +```lua +ok = Set({ "a" }):issubset(Set({ "a", "b" })) --> true +``` + +#### `issuperset` + +Return true if this set contains all elements of another set. + +```lua +ok = Set({ "a", "b" }):issuperset(Set({ "a" })) --> true +``` + +### Query + +Read-only queries for membership and size. + +#### `contains` + +Return true if the set contains `v`. + +```lua +ok = Set({ "a", "b" }):contains("a") --> true +ok = Set({ "a", "b" }):contains("z") --> false +``` + +#### `len` + +Return the number of elements in the set. + +```lua +n = Set({ "a", "b" }):len() --> 2 +``` + +### Transform + +Value-to-value transformations and projection helpers. + +#### `map` + +Return a new set by mapping each value. + +```lua +s = Set({ 1, 2 }):map(function(v) return v * 10 end) --> s contains 10, 20 +``` + +#### `values` + +Return a list of all values in the set. + +```lua +values = Set({ "a", "b" }):values() --> { "a", "b" } +``` diff --git a/docs/src/modules/is.md b/docs/src/modules/is.md index da395f7..2496421 100644 --- a/docs/src/modules/is.md +++ b/docs/src/modules/is.md @@ -1,211 +1,212 @@ --- -description: - Type, truthiness, callable, and filesystem path predicates for Lua values. +desc: "Type predicates for Lua values and filesystem path kinds." --- # `is` -Type predicates for Lua values and filesystem paths. - -## Import - -```lua -local mods = require("mods") -local is = mods.is -``` +Type predicates for Lua values and filesystem path kinds. ## Usage ```lua -local ok +is = require "mods.is" -ok = is.number(3.14) --> true +ok = is.number(3.14) --> true ok = is("hello", "string") --> true -ok = is.Table({}) --> true +ok = is.table({}) --> true ``` -> [!NOTE] +> [!NOTE] Function names exist in both lowercase and capitalized forms, and `is` +> is also callable as `is(v, tp)`. > -> - Function names exist in both lowercase and capitalized forms (for example, -> `is.table` or `is.Table`). -> - `is` is callable as `is(v, tp)` where `v` is the value and `tp` is any -> supported type name. +> ```lua +> is.table({}) --> true +> is.Table({}) --> true +> is("hello", "string") --> true +> is("hello", "String") --> true +> ``` -## Quick Reference +## Functions **Type Checks**: -| Function | Description | -| ------------------------------ | -------------------------------------- | -| [`boolean(v)`](#fn-booleanv) | Returns `true` when `v` is a boolean. | -| [`Function(v)`](#fn-functionv) | Returns `true` when `v` is a function. | -| [`Nil(v)`](#fn-nilv) | Returns `true` when `v` is `nil`. | -| [`number(v)`](#fn-numberv) | Returns `true` when `v` is a number. | -| [`string(v)`](#fn-stringv) | Returns `true` when `v` is a string. | -| [`table(v)`](#fn-tablev) | Returns `true` when `v` is a table. | -| [`thread(v)`](#fn-threadv) | Returns `true` when `v` is a thread. | -| [`userdata(v)`](#fn-userdatav) | Returns `true` when `v` is userdata. | +| Function | Description | +| ----------------------- | -------------------------------------- | +| [`boolean`](#boolean) | Returns `true` when `v` is a boolean. | +| [`Function`](#function) | Returns `true` when `v` is a function. | +| [`Nil`](#nil) | Returns `true` when `v` is `nil`. | +| [`number`](#number) | Returns `true` when `v` is a number. | +| [`string`](#string) | Returns `true` when `v` is a string. | +| [`table`](#table) | Returns `true` when `v` is a table. | +| [`thread`](#thread) | Returns `true` when `v` is a thread. | +| [`userdata`](#userdata) | Returns `true` when `v` is userdata. | **Value Checks**: -| Function | Description | -| ------------------------------ | ------------------------------------------- | -| [`False(v)`](#fn-falsev) | Returns `true` when `v` is exactly `false`. | -| [`True(v)`](#fn-truev) | Returns `true` when `v` is exactly `true`. | -| [`falsy(v)`](#fn-falsyv) | Returns `true` when `v` is falsy. | -| [`callable(v)`](#fn-callablev) | Returns `true` when `v` is callable. | -| [`integer(v)`](#fn-integerv) | Returns `true` when `v` is an integer. | -| [`truthy(v)`](#fn-truthyv) | Returns `true` when `v` is truthy. | +| Function | Description | +| ----------------------- | ------------------------------------------- | +| [`False`](#false) | Returns `true` when `v` is exactly `false`. | +| [`True`](#true) | Returns `true` when `v` is exactly `true`. | +| [`falsy`](#falsy) | Returns `true` when `v` is falsy. | +| [`callable`](#callable) | Returns `true` when `v` is callable. | +| [`integer`](#integer) | Returns `true` when `v` is an integer. | +| [`truthy`](#truthy) | Returns `true` when `v` is truthy. | **Path Checks**: -| Function | Description | -| -------------------------- | ------------------------------------------------------- | -| [`block(v)`](#fn-blockv) | Returns `true` when `v` is a block device path. | -| [`char(v)`](#fn-charv) | Returns `true` when `v` is a char device path. | -| [`device(v)`](#fn-devicev) | Returns `true` when `v` is a block or char device path. | -| [`dir(v)`](#fn-dirv) | Returns `true` when `v` is a directory path. | -| [`fifo(v)`](#fn-fifov) | Returns `true` when `v` is a FIFO path. | -| [`file(v)`](#fn-filev) | Returns `true` when `v` is a file path. | -| [`link(v)`](#fn-linkv) | Returns `true` when `v` is a symlink path. | -| [`socket(v)`](#fn-socketv) | Returns `true` when `v` is a socket path. | - -## Functions +| Function | Description | +| ------------------- | ------------------------------------------------------- | +| [`block`](#block) | Returns `true` when `v` is a block device path. | +| [`char`](#char) | Returns `true` when `v` is a char device path. | +| [`device`](#device) | Returns `true` when `v` is a block or char device path. | +| [`dir`](#dir) | Returns `true` when `v` is a directory path. | +| [`fifo`](#fifo) | Returns `true` when `v` is a FIFO path. | +| [`file`](#file) | Returns `true` when `v` is a file path. | +| [`link`](#link) | Returns `true` when `v` is a symlink path. | +| [`socket`](#socket) | Returns `true` when `v` is a socket path. | ### Type Checks -#### `boolean(v)` {#fn-booleanv} +Core Lua type checks (`type(v)` family). + +#### `boolean` Returns `true` when `v` is a boolean. ```lua -is.boolean(true) --> true +is.boolean(true) ``` -#### `Function(v)` {#fn-functionv} +#### `Function` Returns `true` when `v` is a function. ```lua -is.Function(function() end) --> true +is.Function(function() end) ``` -#### `Nil(v)` {#fn-nilv} +#### `Nil` Returns `true` when `v` is `nil`. ```lua -is.Nil(nil) --> true +is.Nil(nil) ``` -#### `number(v)` {#fn-numberv} +#### `number` Returns `true` when `v` is a number. ```lua -is.number(3.14) --> true +is.number(3.14) ``` -#### `string(v)` {#fn-stringv} +#### `string` Returns `true` when `v` is a string. ```lua -is.string("hello") --> true +is.string("hello") ``` -#### `table(v)` {#fn-tablev} +#### `table` Returns `true` when `v` is a table. ```lua -is.table({}) --> true +is.table({}) ``` -#### `thread(v)` {#fn-threadv} +#### `thread` Returns `true` when `v` is a thread. ```lua -is.thread(coroutine.create(function() end)) --> true +is.thread(coroutine.create(function() end)) ``` -#### `userdata(v)` {#fn-userdatav} +#### `userdata` Returns `true` when `v` is userdata. ```lua -is.userdata(io.stdout) --> true +is.userdata(io.stdout) ``` ### Value Checks -#### `False(v)` {#fn-falsev} +Truthiness, exact-value, and callable checks. + +#### `False` Returns `true` when `v` is exactly `false`. ```lua -is.False(false) --> true +is.False(false) ``` -#### `True(v)` {#fn-truev} +#### `True` Returns `true` when `v` is exactly `true`. ```lua -is.True(true) --> true +is.True(true) ``` -#### `falsy(v)` {#fn-falsyv} +#### `falsy` Returns `true` when `v` is falsy. ```lua -is.falsy(false) --> true +is.falsy(false) ``` -#### `callable(v)` {#fn-callablev} +#### `callable` Returns `true` when `v` is callable. ```lua -is.callable(function() end) --> true +is.callable(function() end) ``` -#### `integer(v)` {#fn-integerv} +#### `integer` Returns `true` when `v` is an integer. ```lua -is.integer(42) --> true +is.integer(42) ``` -#### `truthy(v)` {#fn-truthyv} +#### `truthy` Returns `true` when `v` is truthy. ```lua -is.truthy("non-empty") --> true +is.truthy("non-empty") ``` ### Path Checks +Filesystem path kind checks. + > [!IMPORTANT] > > Path checks require **LuaFileSystem** -> ([`lfs`](https://github.com/lunarmodules/luafilesystem)). -> -> These functions raise an error if `lfs` is not installed. +> ([`lfs`](https://github.com/lunarmodules/luafilesystem)) and raise an error it +> is not installed. -#### `block(v)` {#fn-blockv} +#### `block` Returns `true` when `v` is a block device path. +Raises an error if [`lfs`](https://github.com/lunarmodules/luafilesystem) is not +installed. + ```lua is.block("/dev/sda") ``` -#### `char(v)` {#fn-charv} +#### `char` Returns `true` when `v` is a char device path. @@ -213,7 +214,7 @@ Returns `true` when `v` is a char device path. is.char("/dev/null") ``` -#### `device(v)` {#fn-devicev} +#### `device` Returns `true` when `v` is a block or char device path. @@ -221,7 +222,7 @@ Returns `true` when `v` is a block or char device path. is.device("/dev/null") ``` -#### `dir(v)` {#fn-dirv} +#### `dir` Returns `true` when `v` is a directory path. @@ -229,7 +230,7 @@ Returns `true` when `v` is a directory path. is.dir("/tmp") ``` -#### `fifo(v)` {#fn-fifov} +#### `fifo` Returns `true` when `v` is a FIFO path. @@ -237,7 +238,7 @@ Returns `true` when `v` is a FIFO path. is.fifo("/path/to/fifo") ``` -#### `file(v)` {#fn-filev} +#### `file` Returns `true` when `v` is a file path. @@ -245,7 +246,7 @@ Returns `true` when `v` is a file path. is.file("README.md") ``` -#### `link(v)` {#fn-linkv} +#### `link` Returns `true` when `v` is a symlink path. @@ -253,7 +254,7 @@ Returns `true` when `v` is a symlink path. is.link("/path/to/link") ``` -#### `socket(v)` {#fn-socketv} +#### `socket` Returns `true` when `v` is a socket path. diff --git a/docs/src/modules/keyword.md b/docs/src/modules/keyword.md index f25e937..ec35aff 100644 --- a/docs/src/modules/keyword.md +++ b/docs/src/modules/keyword.md @@ -1,92 +1,70 @@ --- -description: Lua keyword helpers for reserved-word checks. +desc: "Lua keyword helpers." --- -# `keyword` +# `keyword` Lua keyword helpers. -## Import +## Usage ```lua -local kw = require("mods.keyword") -``` - -## Dependencies - -- [`mods.List`] is used by `kwlist()`. -- [`mods.Set`] is used by `kwset()`. +kw = require "mods.keyword" -> [!NOTE] -> -> These dependencies are lazy-loaded internally 💤, so requiring `mods.keyword` -> does not immediately load them. - -## Quick Reference - -| Function | Description | -| ------------------------------------------------------ | -------------------------------------------------- | -| [`iskeyword(s)`](#fn-iskeywords) | Return `true` when `s` is a reserved Lua keyword. | -| [`isidentifier(s)`](#fn-isidentifiers) | Return `true` for valid non-keyword identifiers. | -| [`kwlist()`](#fn-kwlist) | Return Lua keywords as a [`mods.List`] of strings. | -| [`kwset()`](#fn-kwset) | Return Lua keywords as a [`mods.Set`] of strings. | -| [`normalize_identifier(s)`](#fn-normalize_identifiers) | Normalize input to a safe identifier. | +kw.iskeyword("local")) --> true +kw.isidentifier("hello_world") --> true +``` ## Functions -### `iskeyword(s)` {#fn-iskeywords} +| Function | Description | +| ----------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`iskeyword`](#iskeyword) | Return `true` when `s` is a reserved Lua keyword. | +| [`isidentifier`](#isidentifier) | Return `true` when `s` is a valid non-keyword Lua identifier. | +| [`kwlist`](#kwlist) | Return Lua keywords as a [`mods.List`](https://luamod.github.io/mods/modules/list) of strings. | +| [`kwset`](#kwset) | Return Lua keywords as a [`mods.Set`](https://luamod.github.io/mods/modules/set) of strings. | +| [`normalize_identifier`](#normalize-identifier) | Normalize an input into a safe Lua identifier. | -Return `true` when `s` is a reserved Lua keyword. +### `iskeyword` -> [!NOTE] -> -> `goto` is treated as a keyword on Lua 5.2+ and not on Lua 5.1/LuaJIT. +Return `true` when `s` is a reserved Lua keyword. ```lua -print(kw.iskeyword("function")) --> true -print(kw.iskeyword("hello")) --> false +kw.iskeyword("function") --> true +kw.iskeyword("hello") --> false ``` -### `kwlist()` {#fn-kwlist} +### `isidentifier` -Return Lua keywords as a [`mods.List`] of strings. +Return `true` when `s` is a valid non-keyword Lua identifier. ```lua -local keywords = kw.kwlist() -print(keywords[1]) --> "and" -print(keywords[#keywords]) --> "while" +kw.isidentifier("hello_world") --> true +kw.isidentifier("local") --> false ``` -### `kwset()` {#fn-kwset} +### `kwlist` -Return Lua keywords as a [`mods.Set`] of strings. +Return Lua keywords as a +[`mods.List`](https://luamod.github.io/mods/modules/list) of strings. ```lua -local keywords = kw.kwset() -print(keywords["and"]) --> true -print(keywords["hello"]) --> nil +kw.kwlist():contains("and") --> true ``` -### `isidentifier(s)` {#fn-isidentifiers} +### `kwset` -Return `true` when `s` is a valid non-keyword Lua identifier. +Return Lua keywords as a [`mods.Set`](https://luamod.github.io/mods/modules/set) +of strings. ```lua -print(kw.isidentifier("hello_world")) --> true -print(kw.isidentifier("local")) --> false +kw.kwlset():contains("and") --> true ``` -### `normalize_identifier(s)` {#fn-normalize_identifiers} +### `normalize_identifier` -Normalize input to a safe Lua identifier. +Normalize an input into a safe Lua identifier. ```lua -print(kw.normalize_identifier(" 2 bad-name ")) --> "_2_bad_name" -print(kw.normalize_identifier("local")) --> "local_" -print(kw.normalize_identifier("end")) --> "end_" -print(kw.normalize_identifier(" ")) --> "_" -print(kw.normalize_identifier(false)) --> "false_" +kw.normalize_identifier(" 2 bad-name ") --> "_2_bad_name" ``` - -[`mods.List`]: /modules/list -[`mods.Set`]: /modules/set diff --git a/docs/src/modules/mods.md b/docs/src/modules/mods.md new file mode 100644 index 0000000..b4241df --- /dev/null +++ b/docs/src/modules/mods.md @@ -0,0 +1 @@ +# `mods` diff --git a/docs/src/modules/operator.md b/docs/src/modules/operator.md index d7e4d3b..fd10c46 100644 --- a/docs/src/modules/operator.md +++ b/docs/src/modules/operator.md @@ -1,513 +1,260 @@ --- -description: - Operator helpers exposed as functions for arithmetic, comparison, logic, and - indexing. +desc: "Operator helpers as functions." --- # `operator` Operator helpers as functions. -## Quick Reference +## Usage + +```lua +operator = require "mods.operator" + +print(operator.add(1, 2)) -->> 3 +``` + +## Functions **Arithmetic**: -| Function | Description | -| --------------------------- | --------------------------------------------- | -| [`add(a, b)`](#fn-adda-b) | Returns `a + b`. | -| [`sub(a, b)`](#fn-suba-b) | Returns `a - b`. | -| [`mul(a, b)`](#fn-mula-b) | Returns `a * b`. | -| [`div(a, b)`](#fn-diva-b) | Returns `a / b`. | -| [`idiv(a, b)`](#fn-idiva-b) | Returns integer division `math.floor(a / b)`. | -| [`mod(a, b)`](#fn-moda-b) | Returns `a % b`. | -| [`pow(a, b)`](#fn-powa-b) | Returns `a ^ b`. | -| [`unm(a)`](#fn-unma) | Returns `-a`. | +| Function | Description | +| --------------- | ------------------------------------------------------------------ | +| [`add`](#add) | Add two numbers. | +| [`sub`](#sub) | Subtract `b` from `a`. | +| [`mul`](#mul) | Multiply two numbers. | +| [`div`](#div) | Divide `a` by `b` using Lua's floating-point division. | +| [`idiv`](#idiv) | Divide `a` by `b` and return the integer quotient (`//` behavior). | +| [`mod`](#mod) | Return the modulo remainder of `a` divided by `b`. | +| [`pow`](#pow) | Raise `a` to the power of `b`. | +| [`unm`](#unm) | Negate a number. | **Comparison**: -| Function | Description | -| ------------------------- | ----------------- | -| [`eq(a, b)`](#fn-eqa-b) | Returns `a == b`. | -| [`neq(a, b)`](#fn-neqa-b) | Returns `a ~= b`. | -| [`lt(a, b)`](#fn-lta-b) | Returns `a < b`. | -| [`le(a, b)`](#fn-lea-b) | Returns `a <= b`. | -| [`gt(a, b)`](#fn-gta-b) | Returns `a > b`. | -| [`ge(a, b)`](#fn-gea-b) | Returns `a >= b`. | +| Function | Description | +| ------------- | -------------------------------------------------- | +| [`eq`](#eq) | Check whether two values are equal. | +| [`neq`](#neq) | Check whether two values are not equal. | +| [`lt`](#lt) | Check whether `a` is strictly less than `b`. | +| [`le`](#le) | Check whether `a` is less than or equal to `b`. | +| [`gt`](#gt) | Check whether `a` is strictly greater than `b`. | +| [`ge`](#ge) | Check whether `a` is greater than or equal to `b`. | **Logical**: -| Function | Description | -| --------------------------- | ------------------ | -| [`land(a, b)`](#fn-landa-b) | Returns `a and b`. | -| [`lor(a, b)`](#fn-lora-b) | Returns `a or b`. | -| [`lnot(a)`](#fn-lnota) | Returns `not a`. | +| Function | Description | +| --------------- | ---------------------------------------------------- | +| [`land`](#land) | Evaluate `a and b` with Lua short-circuit semantics. | +| [`lor`](#lor) | Evaluate `a or b` with Lua short-circuit semantics. | +| [`lnot`](#lnot) | Return the boolean negation of `a`. | **String & Length**: -| Function | Description | -| ------------------------------- | ----------------- | -| [`concat(a, b)`](#fn-concata-b) | Returns `a .. b`. | -| [`len(a)`](#fn-lena) | Returns `#a`. | +| Function | Description | +| ------------------- | ---------------------------------------------------------------- | +| [`concat`](#concat) | Concatenate two strings. | +| [`len`](#len) | Return the length of a string or table using Lua's `#` operator. | **Tables & Calls**: -| Function | Description | -| ---------------------------------------- | -------------------------------- | -| [`index(t, k)`](#fn-indext-k) | Returns `t[k]`. | -| [`setindex(t, k, v)`](#fn-setindext-k-v) | Sets `t[k] = v` and returns `v`. | -| [`call(f, ...)`](#fn-callf-varargs) | Calls `f(...)`. | - -## Functions +| Function | Description | +| ----------------------- | -------------------------------------------------------------- | +| [`index`](#index) | Return the value at key/index `k` in table `t`. | +| [`setindex`](#setindex) | Set `t[k] = v` and return the assigned value. | +| [`call`](#call) | Call a function with variadic arguments and return its result. | ### Arithmetic -#### `add(a, b)` {#fn-adda-b} - -Returns `a + b`. +Numeric arithmetic operators as functions. -::: code-group +#### `add` -```lua [example.lua] -add(1, 2) -- 3 -``` +Add two numbers. -```lua [signature.lua] ----@param a number ----@param b number ----@return number sum ----@nodiscard -function add(a, b) end +```lua +add(1, 2) --> 3 ``` -::: +#### `sub` -#### `sub(a, b)` {#fn-suba-b} +Subtract `b` from `a`. -Returns `a - b`. - -::: code-group - -```lua [example.lua] -sub(5, 3) -- 2 +```lua +sub(5, 3) --> 2 ``` -```lua [signature.lua] ----@param a number ----@param b number ----@return number difference ----@nodiscard -function sub(a, b) end -``` +#### `mul` -::: +Multiply two numbers. -#### `mul(a, b)` {#fn-mula-b} - -Returns `a * b`. - -::: code-group - -```lua [example.lua] -mul(3, 4) -- 12 +```lua +mul(3, 4) --> 12 ``` -```lua [signature.lua] ----@param a number ----@param b number ----@return number product ----@nodiscard -function mul(a, b) end -``` - -::: - -#### `div(a, b)` {#fn-diva-b} +#### `div` -Returns `a / b`. +Divide `a` by `b` using Lua's floating-point division. -::: code-group - -```lua [example.lua] -div(10, 4) -- 2.5 -``` - -```lua [signature.lua] ----@param a number ----@param b number ----@return number quotient ----@nodiscard -function div(a, b) end +```lua +div(10, 4) --> 2.5 ``` -::: - -#### `idiv(a, b)` {#fn-idiva-b} - -Returns integer division `math.floor(a / b)`. +#### `idiv` -::: code-group +Divide `a` by `b` and return the integer quotient (`//` behavior). -```lua [example.lua] -idiv(5, 2) -- 2 +```lua +idiv(5, 2) --> 2 ``` -```lua [signature.lua] ----@param a number ----@param b number ----@return integer quotient ----@nodiscard -function idiv(a, b) end -``` - -::: - -#### `mod(a, b)` {#fn-moda-b} +#### `mod` -Returns `a % b`. - -::: code-group - -```lua [example.lua] -mod(5, 2) -- 1 -``` +Return the modulo remainder of `a` divided by `b`. -```lua [signature.lua] ----@param a number ----@param b number ----@return number remainder ----@nodiscard -function mod(a, b) end +```lua +mod(5, 2) --> 1 ``` -::: +#### `pow` -#### `pow(a, b)` {#fn-powa-b} +Raise `a` to the power of `b`. -Returns `a ^ b`. - -::: code-group - -```lua [example.lua] -pow(2, 4) -- 16 +```lua +pow(2, 4) --> 16 ``` -```lua [signature.lua] ----@param a number ----@param b number ----@return number power ----@nodiscard -function pow(a, b) end -``` - -::: - -#### `unm(a)` {#fn-unma} +#### `unm` -Returns `-a`. +Negate a number. -::: code-group - -```lua [example.lua] -unm(3) -- -3 -``` - -```lua [signature.lua] ----@param a number ----@return number negated ----@nodiscard -function unm(a) end +```lua +unm(3) --> -3 ``` -::: - ### Comparison -#### `eq(a, b)` {#fn-eqa-b} - -Returns `a == b`. +Equality and ordering comparison operators. -::: code-group +#### `eq` -```lua [example.lua] -eq(1, 1) -- true -``` +Check whether two values are equal. -```lua [signature.lua] ----@param a any ----@param b any ----@return boolean isEqual ----@nodiscard -function eq(a, b) end +```lua +eq(1, 1) --> true ``` -::: +#### `neq` -#### `neq(a, b)` {#fn-neqa-b} +Check whether two values are not equal. -Returns `a ~= b`. - -::: code-group - -```lua [example.lua] -neq(1, 2) -- true +```lua +neq(1, 2) --> true ``` -```lua [signature.lua] ----@param a any ----@param b any ----@return boolean isNotEqual ----@nodiscard -function neq(a, b) end -``` - -::: - -#### `lt(a, b)` {#fn-lta-b} +#### `lt` -Returns `a < b`. +Check whether `a` is strictly less than `b`. -::: code-group - -```lua [example.lua] -lt(1, 2) -- true -``` - -```lua [signature.lua] ----@param a number ----@param b number ----@return boolean isLess ----@nodiscard -function lt(a, b) end +```lua +lt(1, 2) --> true ``` -::: - -#### `le(a, b)` {#fn-lea-b} - -Returns `a <= b`. - -::: code-group +#### `le` -```lua [example.lua] -lte(2, 2) -- true -``` +Check whether `a` is less than or equal to `b`. -```lua [signature.lua] ----@param a number ----@param b number ----@return boolean isLessOrEqual ----@nodiscard -function le(a, b) end +```lua +lte(2, 2) --> true ``` -::: - -#### `gt(a, b)` {#fn-gta-b} - -Returns `a > b`. +#### `gt` -::: code-group +Check whether `a` is strictly greater than `b`. -```lua [example.lua] -gt(3, 2) -- true +```lua +gt(3, 2) --> true ``` -```lua [signature.lua] ----@param a number ----@param b number ----@return boolean isGreater ----@nodiscard -function gt(a, b) end -``` - -::: - -#### `ge(a, b)` {#fn-gea-b} +#### `ge` -Returns `a >= b`. +Check whether `a` is greater than or equal to `b`. -::: code-group - -```lua [example.lua] -gte(2, 2) -- true -``` - -```lua [signature.lua] ----@param a number ----@param b number ----@return boolean isGreaterOrEqual ----@nodiscard -function ge(a, b) end +```lua +gte(2, 2) --> true ``` -::: - ### Logical -#### `land(a, b)` {#fn-landa-b} - -Returns `a and b`. +Boolean logic operators with Lua truthiness semantics. -::: code-group +#### `land` -```lua [example.lua] -land(true, false) -- false -``` +Evaluate `a and b` with Lua short-circuit semantics. -```lua [signature.lua] ----@generic T1,T2 ----@param a T1 ----@param b T2 ----@return T1|T2 andValue ----@nodiscard -function land(a, b) end +```lua +land(true, false) --> false ``` -::: +#### `lor` -#### `lor(a, b)` {#fn-lora-b} +Evaluate `a or b` with Lua short-circuit semantics. -Returns `a or b`. - -::: code-group - -```lua [example.lua] -lor(false, true) -- true +```lua +lor(false, true) --> true ``` -```lua [signature.lua] ----@generic T1,T2 ----@param a T1 ----@param b T2 ----@return T1|T2 orValue ----@nodiscard -function lor(a, b) end -``` - -::: - -#### `lnot(a)` {#fn-lnota} +#### `lnot` -Returns `not a`. +Return the boolean negation of `a`. -::: code-group - -```lua [example.lua] -lnot(true) -- false -``` - -```lua [signature.lua] ----@param a any ----@return boolean isNot ----@nodiscard -function lnot(a) end +```lua +lnot(true) --> false ``` -::: - ### String & Length -#### `concat(a, b)` {#fn-concata-b} - -Returns `a .. b`. +String concatenation and length operators. -::: code-group +#### `concat` -```lua [example.lua] -concat("a", "b") -- "ab" -``` +Concatenate two strings. -```lua [signature.lua] ----@param a string ----@param b string ----@return string concatenated ----@nodiscard -function concat(a, b) end +```lua +concat("a", "b") --> "ab" ``` -::: +#### `len` -#### `len(a)` {#fn-lena} +Return the length of a string or table using Lua's `#` operator. -Returns `#a`. - -::: code-group - -```lua [example.lua] -len("abc") -- 3 +```lua +len("abc") --> 3 ``` -```lua [signature.lua] ----@param a string|table ----@return integer length ----@nodiscard -function len(a) end -``` - -::: - ### Tables & Calls -#### `index(t, k)` {#fn-indext-k} +Table indexing helpers and function invocation. -Returns `t[k]`. +#### `index` -::: code-group +Return the value at key/index `k` in table `t`. -```lua [example.lua] -index({ a = 1 }, "a") -- 1 +```lua +index({ a = 1 }, "a") --> 1 ``` -```lua [signature.lua] ----@generic T ----@param t table ----@param k T ----@return T value ----@nodiscard -function index(t, k) end -``` - -::: - -#### `setindex(t, k, v)` {#fn-setindext-k-v} +#### `setindex` -Sets `t[k] = v` and returns `v`. - -::: code-group - -```lua [example.lua] -setindex({}, "a", 1) -- 1 -``` +Set `t[k] = v` and return the assigned value. -```lua [signature.lua] ----@generic T ----@param t table ----@param k any ----@param v T ----@return T value ----@nodiscard -function setindex(t, k, v) end +```lua +setindex({}, "a", 1) --> 1 ``` -::: +#### `call` -#### `call(f, ...)` {#fn-callf-varargs} +Call a function with variadic arguments and return its result. -Calls `f(...)`. - -::: code-group - -```lua [example.lua] -call(math.max, 1, 2) -- 2 +```lua +call(math.max, 1, 2) --> 2 ``` - -```lua [signature.lua] ----@generic T,T2 ----@param f fun(...:T):T2 ----@param ... T ----@return T2 result ----@nodiscard -function call(f, ...) end -``` - -::: diff --git a/docs/src/modules/repr.md b/docs/src/modules/repr.md index 309f4b9..3e59146 100644 --- a/docs/src/modules/repr.md +++ b/docs/src/modules/repr.md @@ -1,49 +1,27 @@ --- -editLinkTarget: types/repr.lua -description: Fast, readable string rendering for Lua values and nested tables. +desc: "Render any Lua value as a readable string." --- -# `repr` +# `repr` Render any Lua value as a readable string. -## Import +## Usage ```lua -local mods = require("mods.repr") -``` +repr = require "mods.repr" -## Usage +print(repr("Hello world!")) --> "Hello world!" -```lua -local out = repr({ - user = { name = "Ada", role = "Engineer" }, - count = 3, - msg = 'He said "hi"', -}) --- result: --- { --- count = 3, --- msg = 'He said "hi"', --- user = { --- name = "Ada", --- role = "Engineer" --- } --- } +print(repr({ user = { name = "Ada", tags = { "lua", "docs" } } })) +--> { +-- user = { +-- name = "Ada", +-- tags = { +-- [1] = "lua", +-- [2] = "docs" +-- } +-- } +-- } -out = repr({ - user = { - name = "Ada", - meta = { role = "Engineer" }, - }, -}) --- result: --- { --- user = { --- meta = { --- role = "Engineer" --- }, --- name = "Ada" --- } --- } ``` diff --git a/docs/src/modules/str.md b/docs/src/modules/str.md index 3675394..69af7be 100644 --- a/docs/src/modules/str.md +++ b/docs/src/modules/str.md @@ -1,459 +1,507 @@ --- -description: - Python-like string utilities for formatting, predicates, splitting, and - casing. +desc: "String utility helpers modeled after Python's `str`." --- # `str` String utility helpers modeled after Python's `str`. -## Quick Reference - -**Quick Reference: Formatting**: - -| Function | Description | -| --------------------------------------------------------------------- | --------------------------------------------------------------------- | -| [`capitalize(s)`](#fn-capitalizes) | Return copy with first character capitalized and the rest lowercased. | -| [`center(s, width, fillchar)`](#fn-centers-width-fillchar) | Center string within width, padded with fill characters. | -| [`count(s, sub, start, stop)`](#fn-counts-sub-start-stop) | Count non-overlapping occurrences of a substring. | -| [`endswith(s, suffix, start, stop)`](#fn-endswiths-suffix-start-stop) | Return true if string ends with suffix. | -| [`expandtabs(s, tabsize)`](#fn-expandtabss-tabsize) | Expand tabs to spaces using given tabsize. | -| [`find(s, sub, start, stop)`](#fn-finds-sub-start-stop) | Return lowest index of substring or nil if not found. | -| [`format_map(s, mapping)`](#fn-format_maps-mapping) | Format string with mapping (key-based) replacement. | - -**Quick Reference: Predicates**: - -| Function | Description | -| -------------------------------------- | -------------------------------------------------------------------------------------------- | -| [`isalnum(s)`](#fn-isalnums) | Return true if all characters are alphanumeric and string is non-empty. | -| [`isalpha(s)`](#fn-isalphas) | Return true if all characters are alphabetic and string is non-empty. | -| [`isascii(s)`](#fn-isasciis) | Return true if all characters are ASCII and string is non-empty. | -| [`isdecimal(s)`](#fn-isdecimals) | Return true if all characters are decimal characters and string is non-empty. | -| [`isdigit(s)`](#fn-isdigits) | Return true if all characters are digits and string is non-empty. | -| [`isidentifier(s)`](#fn-isidentifiers) | Return true if string is a valid identifier and not a reserved keyword. | -| [`islower(s)`](#fn-islowers) | Return true if all cased characters are lowercase and there is at least one cased character. | -| [`isnumeric(s)`](#fn-isnumerics) | Return true if all characters are numeric and string is non-empty. | -| [`isprintable(s)`](#fn-isprintables) | Return true if all characters are printable and string is non-empty. | -| [`isspace(s)`](#fn-isspaces) | Return true if all characters are whitespace and string is non-empty. | -| [`istitle(s)`](#fn-istitles) | Return true if string is titlecased. | -| [`isupper(s)`](#fn-isuppers) | Return true if all cased characters are uppercase and there is at least one cased character. | - -**Quick Reference: Layout**: - -| Function | Description | -| -------------------------------------------------------- | ------------------------------------------------------------- | -| [`join(sep, ls)`](#fn-joinsep-ls) | Join an iterable of strings using this string as separator. | -| [`ljust(s, width, fillchar)`](#fn-ljusts-width-fillchar) | Left-justify string in a field of given width. | -| [`lower(s)`](#fn-lowers) | Return lowercased copy. | -| [`lstrip(s, chars)`](#fn-lstrips-chars) | Remove leading characters (default: whitespace). | -| [`rstrip(s, chars)`](#fn-rstrips-chars) | Remove trailing characters (default: whitespace). | -| [`strip(s, chars)`](#fn-strips-chars) | Remove leading and trailing characters (default: whitespace). | - -**Quick Reference: Split & Replace**: - -| Function | Description | -| ----------------------------------------------------------- | ------------------------------------------------------------------------- | -| [`partition(s, sep)`](#fn-partitions-sep) | Partition string into head, sep, tail from left. | -| [`removeprefix(s, prefix)`](#fn-removeprefixs-prefix) | Remove prefix if present. | -| [`removesuffix(s, suffix)`](#fn-removesuffixs-suffix) | Remove suffix if present. | -| [`replace(s, old, new, count)`](#fn-replaces-old-new-count) | Return a copy of the string with all occurrences of a substring replaced. | -| [`rfind(s, sub, start, stop)`](#fn-rfinds-sub-start-stop) | Return highest index of substring or nil if not found. | -| [`rindex(s, sub, start, stop)`](#fn-rindexs-sub-start-stop) | Like rfind but raises on failure (placeholder). | -| [`rjust(s, width, fillchar)`](#fn-rjusts-width-fillchar) | Right-justify string in a field of given width. | -| [`rpartition(s, sep)`](#fn-rpartitions-sep) | Partition string into head, sep, tail from right. | -| [`rsplit(s, sep, maxsplit)`](#fn-rsplits-sep-maxsplit) | Split from the right by separator, up to maxsplit. | -| [`split(s, sep, maxsplit)`](#fn-splits-sep-maxsplit) | Split by separator (or whitespace) up to maxsplit. | -| [`splitlines(s, keepends)`](#fn-splitliness-keepends) | Split on line boundaries. | - -**Quick Reference: Casing & Transform**: - -| Function | Description | -| ------------------------------------------------------------------------- | --------------------------------------------------------- | -| [`swapcase(s)`](#fn-swapcases) | Return a copy with case of alphabetic characters swapped. | -| [`startswith(s, prefix, start, stop)`](#fn-startswiths-prefix-start-stop) | Return true if string starts with prefix. | -| [`title(s)`](#fn-titles) | Return titlecased copy. | -| [`translate(s, table_map)`](#fn-translates-table_map) | Translate characters using a mapping table. | -| [`upper(s)`](#fn-uppers) | Return uppercased copy. | -| [`zfill(s, width)`](#fn-zfills-width) | Pad numeric string on the left with zeros. | +## Usage + +```lua +str = require "mods.str" + +print(str.capitalize("hello world")) --> "Hello world" +``` ## Functions +**Formatting**: + +| Function | Description | +| --------------------------- | ----------------------------------------------------------------------------------------------- | +| [`capitalize`](#capitalize) | Return copy with first character capitalized and the rest lowercased. | +| [`center`](#center) | Center string within width, padded with fill characters. | +| [`count`](#count) | Count non-overlapping occurrences of a substring. | +| [`endswith`](#endswith) | Return true if string ends with suffix. If suffix is a list, return true if any suffix matches. | +| [`expandtabs`](#expandtabs) | Expand tabs to spaces using given tabsize. | +| [`find`](#find) | Return lowest index of substring or nil if not found. | +| [`format_map`](#format-map) | Format string with mapping (key-based) replacement. | + +**Predicates**: + +| Function | Description | +| ------------------------------- | -------------------------------------------------------------------------------------------- | +| [`isalnum`](#isalnum) | Return true if all characters are alphanumeric and string is non-empty. | +| [`isalpha`](#isalpha) | Return true if all characters are alphabetic and string is non-empty. | +| [`isascii`](#isascii) | Return true if all characters are ASCII and string is non-empty. | +| [`isdecimal`](#isdecimal) | Return true if all characters are decimal characters and string is non-empty. | +| [`isdigit`](#isdigit) | Return true if all characters are digits and string is non-empty. | +| [`isidentifier`](#isidentifier) | Return true if string is a valid identifier and not a reserved keyword. | +| [`islower`](#islower) | Return true if all cased characters are lowercase and there is at least one cased character. | +| [`isnumeric`](#isnumeric) | Return true if all characters are numeric and string is non-empty. | +| [`isprintable`](#isprintable) | Return true if all characters are printable and string is non-empty. | +| [`isspace`](#isspace) | Return true if all characters are whitespace and string is non-empty. | +| [`istitle`](#istitle) | Return true if string is titlecased. | +| [`isupper`](#isupper) | Return true if all cased characters are uppercase and there is at least one cased character. | + +**Layout**: + +| Function | Description | +| ------------------- | ------------------------------------------------------------- | +| [`join`](#join) | Join an iterable of strings using this string as separator. | +| [`ljust`](#ljust) | Left-justify string in a field of given width. | +| [`lower`](#lower) | Return lowercased copy. | +| [`lstrip`](#lstrip) | Remove leading characters (default: whitespace). | +| [`rstrip`](#rstrip) | Remove trailing characters (default: whitespace). | +| [`strip`](#strip) | Remove leading and trailing characters (default: whitespace). | + +**Split & Replace**: + +| Function | Description | +| ------------------------------- | ------------------------------------------------------------------------- | +| [`partition`](#partition) | Partition string into head, sep, tail from left. | +| [`removeprefix`](#removeprefix) | Remove prefix if present. | +| [`removesuffix`](#removesuffix) | Remove suffix if present. | +| [`replace`](#replace) | Return a copy of the string with all occurrences of a substring replaced. | +| [`rfind`](#rfind) | Return highest index of substring or nil if not found. | +| [`rindex`](#rindex) | Like rfind but raises on failure (placeholder). | +| [`rjust`](#rjust) | Right-justify string in a field of given width. | +| [`rpartition`](#rpartition) | Partition string into head, sep, tail from right. | +| [`rsplit`](#rsplit) | Split from the right by separator, up to maxsplit. | +| [`split`](#split) | Split by separator (or whitespace) up to maxsplit. | +| [`splitlines`](#splitlines) | Split on line boundaries. | + +**Casing & Transform**: + +| Function | Description | +| --------------------------- | ------------------------------------------------------------------------------------------------- | +| [`swapcase`](#swapcase) | Return a copy with case of alphabetic characters swapped. | +| [`startswith`](#startswith) | Return true if string starts with prefix. If prefix is a list, return true if any prefix matches. | +| [`title`](#title) | Return titlecased copy. | +| [`translate`](#translate) | Translate characters using a mapping table. | +| [`upper`](#upper) | Return uppercased copy. | +| [`zfill`](#zfill) | Pad numeric string on the left with zeros. | + ### Formatting -#### `capitalize(s)` {#fn-capitalizes} +#### `capitalize` Return copy with first character capitalized and the rest lowercased. ```lua -local s = capitalize("hello WORLD") --> "Hello world" +s = capitalize("hello WORLD") +--result: "Hello world" ``` -#### `center(s, width, fillchar)` {#fn-centers-width-fillchar} +#### `center` Center string within width, padded with fill characters. ```lua -local s = center("hi", 6, "-") --> "--hi--" +s = center("hi", 6, "-") +--result: "--hi--" ``` -#### `count(s, sub, start, stop)` {#fn-counts-sub-start-stop} +#### `count` Count non-overlapping occurrences of a substring. ```lua -local n = count("aaaa", "aa") --> 2 -n = count("aaaa", "a", 2, -1) --> 2 -n = count("abcd", "") --> 5 +n = count("aaaa", "aa") +--result: 2 + +n = count("aaaa", "a", 2, -1) +--result: 2 + +n = count("abcd", "") +--result: 5 ``` -#### `endswith(s, suffix, start, stop)` {#fn-endswiths-suffix-start-stop} +#### `endswith` Return true if string ends with suffix. If suffix is a list, return true if any suffix matches. ```lua -local ok = endswith("hello.lua", ".lua") --> true +ok = endswith("hello.lua", ".lua") +--result: true ``` -#### `expandtabs(s, tabsize)` {#fn-expandtabss-tabsize} +#### `expandtabs` Expand tabs to spaces using given tabsize. ```lua -local s = expandtabs("a\tb", 4) --> "a b" +s = expandtabs("a\tb", 4) +--result: "a b" ``` -#### `find(s, sub, start, stop)` {#fn-finds-sub-start-stop} +#### `find` Return lowest index of substring or nil if not found. ```lua -local i = find("hello", "ll") --> 3 +i = find("hello", "ll") +--result: 3 ``` -#### `format_map(s, mapping)` {#fn-format_maps-mapping} +#### `format_map` Format string with mapping (key-based) replacement. ```lua -local s = format_map("hi {name}", { name = "bob" }) --> "hi bob" +s = format_map("hi {name}", { name = "bob" }) +--result: "hi bob" ``` ### Predicates -#### `isalnum(s)` {#fn-isalnums} +#### `isalnum` Return true if all characters are alphanumeric and string is non-empty. ```lua -local ok = isalnum("abc123") --> true +ok = isalnum("abc123") +--result: true ``` -> [!NOTE] -> -> Lua letters are ASCII by default, so non-ASCII letters are not alphanumeric. +> [!NOTE] Lua letters are ASCII by default, so non-ASCII letters are not +> alphanumeric. > > ```lua > isalnum("á1")` --> `false` > ``` -#### `isalpha(s)` {#fn-isalphas} +#### `isalpha` Return true if all characters are alphabetic and string is non-empty. ```lua -local ok = isalpha("abc") --> true +ok = isalpha("abc") +--result: true ``` -> [!NOTE] -> -> Lua letters are ASCII by default, so non-ASCII letters are not alphabetic. +> [!NOTE] Lua letters are ASCII by default, so non-ASCII letters are not +> alphabetic. > > ```lua > isalpha("á")` --> `false` > ``` -#### `isascii(s)` {#fn-isasciis} +#### `isascii` Return true if all characters are ASCII and string is non-empty. ```lua -local ok = isascii("hello") --> true +ok = isascii("hello") +--result: true ``` -> [!NOTE] -> -> The empty string returns `true`. +> [!NOTE] The empty string returns `true`. -#### `isdecimal(s)` {#fn-isdecimals} +#### `isdecimal` Return true if all characters are decimal characters and string is non-empty. ```lua -local ok = isdecimal("123") --> true +ok = isdecimal("123") +--result: true ``` -#### `isdigit(s)` {#fn-isdigits} +#### `isdigit` Return true if all characters are digits and string is non-empty. ```lua -local ok = isdigit("123") --> true +ok = isdigit("123") +--result: true ``` -#### `isidentifier(s)` {#fn-isidentifiers} +#### `isidentifier` Return true if string is a valid identifier and not a reserved keyword. ```lua -local ok = isidentifier("foo_bar") --> true -ok = isidentifier("2var") --> false -ok = isidentifier("end") --> false (keyword) +ok = isidentifier("foo_bar") +--result: true + +ok = isidentifier("2var") +--result: false + +ok = isidentifier("end") +--result: false (keyword) ``` -#### `islower(s)` {#fn-islowers} +#### `islower` Return true if all cased characters are lowercase and there is at least one cased character. ```lua -local ok = islower("hello") --> true +ok = islower("hello") +--result: true ``` -#### `isnumeric(s)` {#fn-isnumerics} +#### `isnumeric` Return true if all characters are numeric and string is non-empty. ```lua -local ok = isnumeric("123") --> true +ok = isnumeric("123") +--result: true ``` -#### `isprintable(s)` {#fn-isprintables} +#### `isprintable` Return true if all characters are printable and string is non-empty. ```lua -local ok = isprintable("abc!") --> true +ok = isprintable("abc!") +--result: true ``` -> [!NOTE] -> -> The empty string returns `true`. +> [!NOTE] The empty string returns `true`. -#### `isspace(s)` {#fn-isspaces} +#### `isspace` Return true if all characters are whitespace and string is non-empty. ```lua -local ok = isspace(" \t") --> true +ok = isspace(" \t") +--result: true ``` -#### `istitle(s)` {#fn-istitles} +#### `istitle` Return true if string is titlecased. ```lua -local ok = istitle("Hello World") --> true +ok = istitle("Hello World") +--result: true ``` -#### `isupper(s)` {#fn-isuppers} +#### `isupper` Return true if all cased characters are uppercase and there is at least one cased character. ```lua -local ok = isupper("HELLO") --> true +ok = isupper("HELLO") +--result: true ``` ### Layout -#### `join(sep, ls)` {#fn-joinsep-ls} +#### `join` Join an iterable of strings using this string as separator. ```lua -local s = join(",", { "a", "b", "c" }) --> "a,b,c" +s = join(",", { "a", "b", "c" }) +--result: "a,b,c" ``` -#### `ljust(s, width, fillchar)` {#fn-ljusts-width-fillchar} +#### `ljust` Left-justify string in a field of given width. ```lua -local s = ljust("hi", 5, ".") --> "hi..." +s = ljust("hi", 5, ".") +--result: "hi..." ``` -#### `lower(s)` {#fn-lowers} +#### `lower` Return lowercased copy. ```lua -local s = lower("HeLLo") --> "hello" +s = lower("HeLLo") +--result: "hello" ``` -#### `lstrip(s, chars)` {#fn-lstrips-chars} +#### `lstrip` Remove leading characters (default: whitespace). ```lua -local s = lstrip(" hello") --> "hello" +s = lstrip(" hello") +--result: "hello" ``` -#### `rstrip(s, chars)` {#fn-rstrips-chars} +#### `rstrip` Remove trailing characters (default: whitespace). ```lua -local s = rstrip("hello ") --> "hello" +s = rstrip("hello ") +--result: "hello" ``` -#### `strip(s, chars)` {#fn-strips-chars} +#### `strip` Remove leading and trailing characters (default: whitespace). ```lua -local s = strip(" hello ") --> "hello" +s = strip(" hello ") +--result: "hello" ``` ### Split & Replace -#### `partition(s, sep)` {#fn-partitions-sep} +#### `partition` Partition string into head, sep, tail from left. ```lua -local a, b, c = partition("a-b-c", "-") --> "a", "-", "b-c" +a, b, c = partition("a-b-c", "-") +--result: "a", "-", "b-c" ``` -#### `removeprefix(s, prefix)` {#fn-removeprefixs-prefix} +#### `removeprefix` Remove prefix if present. ```lua -local s = removeprefix("foobar", "foo") --> "bar" +s = removeprefix("foobar", "foo") +--result: "bar" ``` -#### `removesuffix(s, suffix)` {#fn-removesuffixs-suffix} +#### `removesuffix` Remove suffix if present. ```lua -local s = removesuffix("foobar", "bar") --> "foo" +s = removesuffix("foobar", "bar") +--result: "foo" ``` -#### `replace(s, old, new, count)` {#fn-replaces-old-new-count} +#### `replace` Return a copy of the string with all occurrences of a substring replaced. ```lua -local s = replace("a-b-c", "-", "_", 1) --> "a_b-c" +s = replace("a-b-c", "-", "_", 1) +--result: "a_b-c" ``` -#### `rfind(s, sub, start, stop)` {#fn-rfinds-sub-start-stop} +#### `rfind` Return highest index of substring or nil if not found. ```lua -local i = rfind("ababa", "ba") --> 4 +i = rfind("ababa", "ba") +--result: 4 ``` -#### `rindex(s, sub, start, stop)` {#fn-rindexs-sub-start-stop} +#### `rindex` Like rfind but raises on failure (placeholder). ```lua -local i = rindex("ababa", "ba") --> 4 +i = rindex("ababa", "ba") +--result: 4 ``` -#### `rjust(s, width, fillchar)` {#fn-rjusts-width-fillchar} +#### `rjust` Right-justify string in a field of given width. ```lua -local s = rjust("hi", 5, ".") --> "...hi" +s = rjust("hi", 5, ".") +--result: "...hi" ``` -#### `rpartition(s, sep)` {#fn-rpartitions-sep} +#### `rpartition` Partition string into head, sep, tail from right. ```lua -local a, b, c = rpartition("a-b-c", "-") --> "a-b", "-", "c" +a, b, c = rpartition("a-b-c", "-") +--result: "a-b", "-", "c" ``` -#### `rsplit(s, sep, maxsplit)` {#fn-rsplits-sep-maxsplit} +#### `rsplit` Split from the right by separator, up to maxsplit. ```lua -local parts = rsplit("a,b,c", ",", 1) --> { "a,b", "c" } +parts = rsplit("a,b,c", ",", 1) +--result: { "a,b", "c" } ``` -#### `split(s, sep, maxsplit)` {#fn-splits-sep-maxsplit} +#### `split` Split by separator (or whitespace) up to maxsplit. ```lua -local parts = split("a,b,c", ",") --> { "a", "b", "c" } +parts = split("a,b,c", ",") +--result: { "a", "b", "c" } ``` -#### `splitlines(s, keepends)` {#fn-splitliness-keepends} +#### `splitlines` Split on line boundaries. ```lua -local lines = splitlines("a\nb\r\nc") --> { "a", "b", "c" } +lines = splitlines("a\nb\r\nc") +--result: { "a", "b", "c" } ``` ### Casing & Transform -#### `swapcase(s)` {#fn-swapcases} +#### `swapcase` Return a copy with case of alphabetic characters swapped. ```lua -local s = swapcase("AbC") --> "aBc" +s = swapcase("AbC") +--result: "aBc" ``` -#### `startswith(s, prefix, start, stop)` {#fn-startswiths-prefix-start-stop} +#### `startswith` Return true if string starts with prefix. If prefix is a list, return true if any prefix matches. ```lua -local ok = startswith("hello.lua", "he") --> true +ok = startswith("hello.lua", "he") +--result: true ``` -#### `title(s)` {#fn-titles} +#### `title` Return titlecased copy. ```lua -local s = title("hello world") --> "Hello World" +s = title("hello world") +--result: "Hello World" ``` -#### `translate(s, table_map)` {#fn-translates-table_map} +#### `translate` Translate characters using a mapping table. ```lua -local map = { [string.byte("a")] = "b", ["c"] = false } -local s = translate("abc", map) --> "bb" +map = { [string.byte("a")] = "b", ["c"] = false } +s = translate("abc", map) +--result: "bb" ``` -#### `upper(s)` {#fn-uppers} +#### `upper` Return uppercased copy. ```lua -local s = upper("Hello") --> "HELLO" +s = upper("Hello") +--result: "HELLO" ``` -#### `zfill(s, width)` {#fn-zfills-width} +#### `zfill` Pad numeric string on the left with zeros. ```lua -local s = zfill("42", 5) --> "00042" +s = zfill("42", 5) +--result: "00042" ``` diff --git a/docs/src/modules/stringcase.md b/docs/src/modules/stringcase.md index 920890d..aabbfe5 100644 --- a/docs/src/modules/stringcase.md +++ b/docs/src/modules/stringcase.md @@ -1,209 +1,198 @@ --- -description: - Convert strings across naming conventions like snake_case, camelCase, and - kebab-case. +desc: "String case conversion helpers." --- # `stringcase` String case conversion helpers. -## Import - -```lua -local sc = require("mods.stringcase") -``` - ## Usage ```lua -local s = "FooBar baz" +sc = require "mods.stringcase" -sc.snake(s) --> "foo_bar_baz" -sc.camel(s) --> "fooBarBaz" -sc.kebab(s) --> "foo-bar-baz" -sc.title(s) --> "Foo Bar Baz" +print(stringcase.snake("FooBar")) --> "foo_bar" ``` -## Quick Reference - -**Quick Reference: Basic**: - -| Function | Description | -| ------------------------ | -------------------------------- | -| [`lower(s)`](#fn-lowers) | Convert string to all lowercase. | -| [`upper(s)`](#fn-uppers) | Convert string to all uppercase. | - -**Quick Reference: Word Case**: - -| Function | Description | -| ------------------------------------- | --------------------------------------------------------------------- | -| [`snake(s)`](#fn-snakes) | Convert string to snake_case. | -| [`camel(s)`](#fn-camels) | Convert string to camelCase. | -| [`replace(s, sep)`](#fn-replaces-sep) | Normalize to snake_case, then replace underscores with a separator. | -| [`acronym(s)`](#fn-acronyms) | Get acronym of words in string (first letters only). | -| [`title(s)`](#fn-titles) | Convert string to Title Case (first letter of each word capitalized). | -| [`constant(s)`](#fn-constants) | Convert string to CONSTANT_CASE (uppercase snake_case). | -| [`pascal(s)`](#fn-pascals) | Convert string to PascalCase. | -| [`kebab(s)`](#fn-kebabs) | Convert string to kebab-case. | -| [`dot(s)`](#fn-dots) | Convert string to dot.case. | -| [`space(s)`](#fn-spaces) | Convert string to space case (spaces between words). | -| [`path(s)`](#fn-paths) | Convert string to path/case (slashes between words). | - -**Quick Reference: Letter Case**: - -| Function | Description | -| ------------------------------ | ------------------------------------------------------------------------- | -| [`swap(s)`](#fn-swaps) | Swap case of each letter. | -| [`capital(s)`](#fn-capitals) | Capitalize the first letter and lowercase the rest. | -| [`sentence(s)`](#fn-sentences) | Convert string to sentence case (first letter uppercase, rest lowercase). | - ## Functions +**Basic**: + +| Function | Description | +| ----------------- | -------------------------------- | +| [`lower`](#lower) | Convert string to all lowercase. | +| [`upper`](#upper) | Convert string to all uppercase. | + +**Word Case**: + +| Function | Description | +| ----------------------- | --------------------------------------------------------------------- | +| [`snake`](#snake) | Convert string to snake_case. | +| [`camel`](#camel) | Convert string to camelCase. | +| [`replace`](#replace) | Normalize to snake_case, then replace underscores with a separator. | +| [`acronym`](#acronym) | Get acronym of words in string (first letters only). | +| [`title`](#title) | Convert string to Title Case (first letter of each word capitalized). | +| [`constant`](#constant) | Convert string to CONSTANT_CASE (uppercase snake_case). | +| [`pascal`](#pascal) | Convert string to PascalCase. | +| [`kebab`](#kebab) | Convert string to kebab-case. | +| [`dot`](#dot) | Convert string to dot.case. | +| [`space`](#space) | Convert string to space case (spaces between words). | +| [`path`](#path) | Convert string to path/case (slashes between words). | + +**Letter Case**: + +| Function | Description | +| ----------------------- | ------------------------------------------------------------------------- | +| [`swap`](#swap) | Swap case of each letter. | +| [`capital`](#capital) | Capitalize the first letter and lowercase the rest. | +| [`sentence`](#sentence) | Convert string to sentence case (first letter uppercase, rest lowercase). | + ### Basic -#### `lower(s)` {#fn-lowers} +#### `lower` Convert string to all lowercase. ```lua -sc.lower("foo_bar-baz") --> "foo_bar-baz" -sc.lower("FooBar baz") --> "foobar baz" +lower("foo_bar-baz") --> "foo_bar-baz" +lower("FooBar baz") --> "foobar baz" ``` -#### `upper(s)` {#fn-uppers} +#### `upper` Convert string to all uppercase. ```lua -sc.upper("foo_bar-baz") --> "FOO_BAR-BAZ" -sc.upper("FooBar baz") --> "FOOBAR BAZ" +upper("foo_bar-baz") --> "FOO_BAR-BAZ" +upper("FooBar baz") --> "FOOBAR BAZ" ``` ### Word Case -#### `snake(s)` {#fn-snakes} +#### `snake` Convert string to snake_case. ```lua -sc.snake("foo_bar-baz") --> "foo_bar_baz" -sc.snake("FooBar baz") --> "foo_bar_baz" +snake("foo_bar-baz") --> "foo_bar_baz" +snake("FooBar baz") --> "foo_bar_baz" ``` -#### `camel(s)` {#fn-camels} +#### `camel` Convert string to camelCase. ```lua -sc.camel("foo_bar-baz") --> "fooBarBaz" -sc.camel("FooBar baz") --> "fooBarBaz" +camel("foo_bar-baz") --> "fooBarBaz" +camel("FooBar baz") --> "fooBarBaz" ``` -#### `replace(s, sep)` {#fn-replaces-sep} +#### `replace` Normalize to snake_case, then replace underscores with a separator. ```lua -sc.replace("foo_bar-baz", "-") --> "foo-bar-baz" -sc.replace("FooBar baz", "-") --> "foo-bar-baz" +replace("foo_bar-baz", "-") --> "foo-bar-baz" +replace("FooBar baz", "-") --> "foo-bar-baz" ``` -#### `acronym(s)` {#fn-acronyms} +#### `acronym` Get acronym of words in string (first letters only). ```lua -sc.acronym("foo_bar-baz") --> "FBB" +acronym("foo_bar-baz") --> "FBB" +acronym("FooBar baz") --> "FBB" ``` -#### `title(s)` {#fn-titles} +#### `title` Convert string to Title Case (first letter of each word capitalized). ```lua -sc.title("foo_bar-baz") --> "Foo Bar Baz" -sc.title("FooBar baz") --> "Foo Bar Baz" +title("foo_bar-baz") --> "Foo Bar Baz" +title("FooBar baz") --> "Foo Bar Baz" ``` -#### `constant(s)` {#fn-constants} +#### `constant` Convert string to CONSTANT_CASE (uppercase snake_case). ```lua -sc.constant("foo_bar-baz") --> "FOO_BAR_BAZ" +constant("foo_bar-baz") --> "FOO_BAR_BAZ" +constant("FooBar baz") --> "FOO_BAR_BAZ" ``` -#### `pascal(s)` {#fn-pascals} +#### `pascal` Convert string to PascalCase. ```lua -sc.pascal("foo_bar-baz") --> "FooBarBaz" -sc.pascal("FooBar baz") --> "FooBarBaz" +pascal("foo_bar-baz") --> "FooBarBaz" +pascal("FooBar baz") --> "FooBarBaz" ``` -#### `kebab(s)` {#fn-kebabs} +#### `kebab` Convert string to kebab-case. ```lua -sc.kebab("foo_bar-baz") --> "foo-bar-baz" -sc.kebab("FooBar baz") --> "foo-bar-baz" +kebab("foo_bar-baz") --> "foo-bar-baz" +kebab("FooBar baz") --> "foo-bar-baz" ``` -#### `dot(s)` {#fn-dots} +#### `dot` Convert string to dot.case. ```lua -sc.dot("foo_bar-baz") --> "foo.bar.baz" -sc.dot("FooBar baz") --> "foo.bar.baz" +dot("foo_bar-baz") --> "foo.bar.baz" +dot("FooBar baz") --> "foo.bar.baz" ``` -#### `space(s)` {#fn-spaces} +#### `space` Convert string to space case (spaces between words). ```lua -sc.space("foo_bar-baz") --> "foo bar baz" -sc.space("FooBar baz") --> "foo bar baz" +space("foo_bar-baz") --> "foo bar baz" +space("FooBar baz") --> "foo bar baz" ``` -#### `path(s)` {#fn-paths} +#### `path` Convert string to path/case (slashes between words). ```lua -sc.path("foo_bar-baz") --> "foo/bar/baz" -sc.path("FooBar baz") --> "foo/bar/baz" +path("foo_bar-baz") --> "foo/bar/baz" +path("FooBar baz") --> "foo/bar/baz" ``` ### Letter Case -#### `swap(s)` {#fn-swaps} +#### `swap` Swap case of each letter. ```lua -sc.swap("foo_bar-baz") --> "FOO_BAR-BAZ" -sc.swap("FooBar baz") --> "fOObAR BAZ" +swap("foo_bar-baz") --> "FOO_BAR-BAZ" +swap("FooBar baz") --> "fOObAR BAZ" ``` -#### `capital(s)` {#fn-capitals} +#### `capital` Capitalize the first letter and lowercase the rest. ```lua -sc.capital("foo_bar-baz") --> "Foo_bar-baz" -sc.capital("FooBar baz") --> "Foobar baz" +capital("foo_bar-baz") --> "Foo_bar-baz" +capital("FooBar baz") --> "Foobar baz" ``` -#### `sentence(s)` {#fn-sentences} +#### `sentence` Convert string to sentence case (first letter uppercase, rest lowercase). ```lua -sc.sentence("foo_bar-baz") --> "Foo_bar-baz" -sc.sentence("FooBar baz") --> "FooBar baz" +sentence("foo_bar-baz") --> "Foo_bar-baz" +sentence("FooBar baz") --> "FooBar baz" ``` diff --git a/docs/src/modules/tbl.md b/docs/src/modules/tbl.md index 802dfde..f86ac0e 100644 --- a/docs/src/modules/tbl.md +++ b/docs/src/modules/tbl.md @@ -1,392 +1,204 @@ --- -description: - Lua table helpers for copy, query, transform, and safe nested access patterns. +desc: "Utility functions for working with Lua tables." --- # `tbl` Utility functions for working with Lua tables. -## Quick Reference +## Usage + +```lua +tbl = require "mods.tbl" + +print(tbl.count({ a = 1, b = 2 })) --> 2 +``` + +## Functions **Basics**: -| Function | Description | -| ------------------------ | --------------------------------------- | -| [`clear(t)`](#fn-cleart) | Remove all entries from the table. | -| [`count(t)`](#fn-countt) | Return the number of keys in the table. | +| Function | Description | +| ----------------- | --------------------------------------- | +| [`clear`](#clear) | Remove all entries from the table. | +| [`count`](#count) | Return the number of keys in the table. | **Copying**: -| Function | Description | -| ------------------------------ | ----------------------------------- | -| [`copy(t)`](#fn-copyt) | Create a shallow copy of the table. | -| [`deepcopy(v)`](#fn-deepcopyv) | Create a deep copy of a value. | +| Function | Description | +| ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| [`copy`](#copy) | Create a shallow copy of the table. | +| [`deepcopy`](#deepcopy) | Create a deep copy of a value. If `v` is a table, all nested tables are copied recursively; other types are returned as-is. | **Query**: -| Function | Description | -| --------------------------------------- | ------------------------------------------------------ | -| [`filter(t, pred)`](#fn-filtert-pred) | Filter entries by a value predicate. | -| [`find(t, v)`](#fn-findt-v) | Find the first key whose value equals the given value. | -| [`find_if(t, pred)`](#fn-find_ift-pred) | Find first value and key matching predicate. | -| [`get(t, ...)`](#fn-gett-varargs) | Safely get nested value by keys. | +| Function | Description | +| --------------------- | ---------------------------------------------------------------------------------- | +| [`filter`](#filter) | Filter entries by a value predicate. | +| [`find`](#find) | Find the first key whose value equals the given value. | +| [`find_if`](#find-if) | Find first value and key matching predicate. | +| [`get`](#get) | Safely get nested value by keys. If no keys are provided, returns the input table. | **Transforms**: -| Function | Description | -| ----------------------------------- | ---------------------------------------------------------- | -| [`invert(t)`](#fn-invertt) | Invert keys/values into new table. | -| [`isempty(t)`](#fn-isemptyt) | Return true if table has no entries. | -| [`keys(t)`](#fn-keyst) | Return a list of all keys in the table. | -| [`map(t, fn)`](#fn-mapt-fn) | Return a new table by mapping each value (keys preserved). | -| [`pairmap(t, fn)`](#fn-pairmapt-fn) | Return a new table by mapping each key-value pair. | -| [`update(t1, t2)`](#fn-updatet1-t2) | Merge entries from t2 into t1 and return t1. | -| [`values(t)`](#fn-valuest) | Return a list of all values in the table. | - -## Functions +| Function | Description | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| [`invert`](#invert) | Invert keys/values into new table. | +| [`isempty`](#isempty) | Return true if table has no entries. | +| [`keys`](#keys) | Return a list of all keys in the table. | +| [`map`](#map) | Return a new table by mapping each value (keys preserved). | +| [`pairmap`](#pairmap) | Return a new table by mapping each key-value pair. The resulting table keeps the same keys, with values transformed by `fn`. | +| [`update`](#update) | Merge entries from t2 into t1 and return t1. | +| [`values`](#values) | Return a list of all values in the table. | ### Basics -#### `clear(t)` {#fn-cleart} +Core table utilities for clearing and counting. -Remove all entries from the table. +#### `clear` -::: code-group - -```lua [example.lua] -local t = { a = 1, b = 2 } -clear(t) --- result: {} -``` +Remove all entries from the table. -```lua [signature.lua] ----@param t table ----@return nil -function clear(t) end +```lua +t = { a = 1, b = 2 } +clear(t) --> t = {} ``` -::: - -#### `count(t)` {#fn-countt} +#### `count` Return the number of keys in the table. -::: code-group - -```lua [example.lua] -local n = count({ a = 1, b = 2 }) --- result: 2 -``` - -```lua [signature.lua] ----@param t table ----@return integer ----@nodiscard -function count(t) end +```lua +n = count({ a = 1, b = 2 }) --> 2 ``` -::: - ### Copying -#### `copy(t)` {#fn-copyt} - -Create a shallow copy of the table. +Shallow and deep copy helpers. -::: code-group +#### `copy` -```lua [example.lua] -local t = copy({ a = 1, b = 2 }) --- result: { a = 1, b = 2 } -``` +Create a shallow copy of the table. -```lua [signature.lua] ----@generic T:table ----@param t T ----@return T ----@nodiscard -function copy(t) end +```lua +t = copy({ a = 1, b = 2 }) --> { a = 1, b = 2 } ``` -::: - -#### `deepcopy(v)` {#fn-deepcopyv} +#### `deepcopy` Create a deep copy of a value. If `v` is a table, all nested tables are copied recursively; other types are returned as-is. -::: code-group - -```lua [example.lua] -local n = deepcopy(42) --- result: 42 - -local t = deepcopy({ a = { b = 1 } }) --- result: { a = { b = 1 } } +```lua +t = deepcopy({ a = { b = 1 } }) --> { a = { b = 1 } } +n = deepcopy(42) --> 42 ``` -```lua [signature.lua] ----@generic T ----@param v T ----@return T ----@nodiscard -function deepcopy(v) end -``` - -::: - ### Query -#### `filter(t, pred)` {#fn-filtert-pred} - -Filter entries by a value predicate. +Read-only lookup and selection helpers. -::: code-group +#### `filter` -```lua [example.lua] -local even = filter({ a = 1, b = 2, c = 3 }, function(v) - return v % 2 == 0 -end) --- result: { b = 2 } -``` +Filter entries by a value predicate. -```lua [signature.lua] ----@param t table ----@param pred fun(v:any):boolean ----@return table ----@nodiscard -function filter(t, pred) end +```lua +even = filter({ a = 1, b = 2, c = 3 }, function(v) + return v % 2 == 0 +end) --> { b = 2 } ``` -::: - -#### `find(t, v)` {#fn-findt-v} +#### `find` Find the first key whose value equals the given value. -::: code-group - -```lua [example.lua] -local key = find({ a = 1, b = 2, c = 2 }, 2) --- result: "b" or "c" +```lua +key = find({ a = 1, b = 2, c = 2 }, 2) --> "b" or "c" ``` -```lua [signature.lua] ----@generic T1,T2 ----@param t {[T1]:T2} ----@param v T2 ----@return T1? ----@nodiscard -function find(t, v) end -``` - -::: - -#### `find_if(t, pred)` {#fn-find_ift-pred} +#### `find_if` Find first value and key matching predicate. -::: code-group - -```lua [example.lua] -local v, k = find_if({ a = 1, b = 2 }, function(v, k) - return k == "b" and v == 2 -end) --- result: 2, "b" +```lua +v, k = find_if({ a = 1, b = 2 }, function(v, k) + return k == "b" and v == 2 +end) --> 2, "b" ``` -```lua [signature.lua] ----@generic T1,T2 ----@param t table ----@param pred fun(v:T1,k:T2):boolean ----@return T1? v ----@return T2? k ----@nodiscard -function find_if(t, pred) end -``` - -::: - -#### `get(t, ...)` {#fn-gett-varargs} +#### `get` Safely get nested value by keys. If no keys are provided, returns the input table. -::: code-group - -```lua [example.lua] -local t = { a = { b = { c = 1 } } } -local v = get(t, "a", "b", "c") --- result: 1 - -local v2 = get(t) --- result: { a = { b = { c = 1 } } } +```lua +t = { a = { b = { c = 1 } } } +v1 = get(t, "a", "b", "c") --> 1 +v2 = get(t) --> { a = { b = { c = 1 } } } ``` -```lua [signature.lua] ----@param t table ----@param ... any ----@return any ----@nodiscard -function get(t, ...) end -``` - -::: - ### Transforms -#### `invert(t)` {#fn-invertt} - -Invert keys/values into new table. +Table transformation and conversion utilities. -::: code-group +#### `invert` -```lua [example.lua] -local t = invert({ a = 1, b = 2 }) --- result: { [1] = "a", [2] = "b" } -``` +Invert keys/values into new table. -```lua [signature.lua] ----@generic T1,T2 ----@param t {[T1]:T2} ----@return {[T2]:T1} ----@nodiscard -function invert(t) end +```lua +t = invert({ a = 1, b = 2 }) --> { [1] = "a", [2] = "b" } ``` -::: - -#### `isempty(t)` {#fn-isemptyt} +#### `isempty` Return true if table has no entries. -::: code-group - -```lua [example.lua] -local empty = isempty({}) --- result: true +```lua +empty = isempty({}) --> true ``` -```lua [signature.lua] ----@param t table ----@return boolean ----@nodiscard -function isempty(t) end -``` - -::: - -#### `keys(t)` {#fn-keyst} +#### `keys` Return a list of all keys in the table. -::: code-group - -```lua [example.lua] -local keys = keys({ a = 1, b = 2 }) --- result: { "a", "b" } -``` - -```lua [signature.lua] ----@generic T ----@param t {[any]:T} ----@return mods.List|T[] ----@nodiscard -function keys(t) end +```lua +keys = keys({ a = 1, b = 2 }) --> { "a", "b" } ``` -::: - -#### `map(t, fn)` {#fn-mapt-fn} +#### `map` Return a new table by mapping each value (keys preserved). -::: code-group - -```lua [example.lua] -local t = map({ a = 1, b = 2 }, function(v) - return v * 10 -end) --- result: { a = 10, b = 20 } -``` - -```lua [signature.lua] ----@generic T1,T2,T3 ----@param t {[T1]:T2} ----@param fn fun(v:T2):T3 ----@return {[T1]:T3} ----@nodiscard -function map(t, fn) end +```lua +t = map({ a = 1, b = 2 }, function(v) + return v * 10 +end) --> { a = 10, b = 20 } ``` -::: - -#### `pairmap(t, fn)` {#fn-pairmapt-fn} +#### `pairmap` Return a new table by mapping each key-value pair. The resulting table keeps the same keys, with values transformed by `fn`. -::: code-group - -```lua [example.lua] -local t = pairmap({ a = 1, b = 2 }, function(k, v) - return k .. v -end) --- result: { a = "a1", b = "b2" } -``` - -```lua [signature.lua] ----@generic T1,T2,T3 ----@param t {[T1]:T2} ----@param fn fun(k:T1, v:T2):T3 ----@return {[T1]:T3} ----@nodiscard -function pairmap(t, fn) end +```lua +t = pairmap({ a = 1, b = 2 }, function(k, v) + return k .. v +end) --> { a = "a1", b = "b2" } ``` -::: - -#### `update(t1, t2)` {#fn-updatet1-t2} +#### `update` Merge entries from t2 into t1 and return t1. -::: code-group - -```lua [example.lua] -local t1 = { a = 1, b = 2 } -update(t1, { b = 3, c = 4 }) --- result: t1 is { a = 1, b = 3, c = 4 } +```lua +t1 = { a = 1, b = 2 } +update(t1, { b = 3, c = 4 }) --> t1 is { a = 1, b = 3, c = 4 } ``` -```lua [signature.lua] ----@generic T:table ----@param t1 T ----@param t2 table ----@return T ----@nodiscard -function update(t1, t2) end -``` - -::: - -#### `values(t)` {#fn-valuest} +#### `values` Return a list of all values in the table. -::: code-group - -```lua [example.lua] -local vals = values({ a = 1, b = 2 }) --- result: { 1, 2 } -``` - -```lua [signature.lua] ----@generic T ----@param t {[any]:T} ----@return mods.List|T[] ----@nodiscard -function values(t) end +```lua +vals = values({ a = 1, b = 2 }) --> { 1, 2 } ``` - -::: diff --git a/docs/src/modules/template.md b/docs/src/modules/template.md index c166c0a..a95234f 100644 --- a/docs/src/modules/template.md +++ b/docs/src/modules/template.md @@ -1,142 +1,90 @@ --- -description: - Render lightweight templates with dot-path placeholders and function-aware - values. +desc: + "Render lightweight templates with dot-path placeholders and function-aware + values." --- # `template` -Render a simple template using the provided context. - -## Import - -```lua -local mods = require("mods") -local template = mods.template -``` +Render lightweight templates with dot-path placeholders and function-aware +values. ## Usage ```lua -local view = { - user = { - name = "Ada", - meta = { role = "Engineer" }, - name_func = function() - return "Ada" - end, - }, - count = 3, - name_func = function() - return "Ada" - end, -} - -local out = template("Hello {{user.name}}!", view) --- result: "Hello Ada!" - -out = template(view) --- result: --- { --- count=3, --- name_func=, --- user={...} --- } - -out = template("You have {{count}} new messages.", view) --- result: "You have 3 new messages." +template = require "mods.template" -out = template('Missing key: "{{missing}}"', view) --- result: 'Missing key: ""' - -out = template("Role: {{ user.meta.role }}", view) --- result: "Role: Engineer" - -out = template("Hi {{name_func}}", view) --- result: "Hi Ada" +view = { + user = { name = "World" }, +} -out = template("Stats: {{stats}}", { stats = { count = 3, ok = true } }) --- result: --- Stats: { --- count=3, --- ok=true --- } +out = template("Hello {{user.name}}!", view) --> "Hello World!" ``` ## Rules - Placeholders use {{name}}. - ```lua - template("Hi {{name}}", { name = "Ada" }) - -- result: "Hi Ada" - ``` +```lua +template("Hi {{name}}", { name = "Ada" }) --> "Hi Ada" +``` - Whitespace inside placeholders is ignored. - ```lua - template("Hi {{ name }}", { name = "Ada" }) - -- result: "Hi Ada" - ``` +```lua +template("Hi {{ name }}", { name = "Ada" }) --> "Hi Ada" +``` - Dot paths are supported. - ```lua - template("Role: {{user.meta.role}}", { - user = { meta = { role = "Engineer" } }, - }) -- result: "Role: Engineer" - ``` +```lua +template("Role: {{user.meta.role}}", { + user = { meta = { role = "Engineer" } }, +}) --> "Role: Engineer" +``` - {{.}} resolves to the whole `view`. - ```lua - template("Value: {{.}}", 123) - -- result: "Value: 123" - ``` +```lua +template("Value: {{.}}", 123) --> "Value: 123" +``` - Function values are called and their return value is rendered. - ```lua - template("Hi {{name_func}}", { - name_func = function() - return "Ada" - end, - }) -- result: "Hi Ada" - ``` +```lua +template("Hi {{name_func}}", { name_func = function() return "Ada" end }) +--> "Hi Ada" +``` - Table values render as first-depth key/value pairs. String values are quoted; nested tables/functions are summarized. - ```lua - template("Data: {{data}}", { data = { a = 1, b = true } }) - -- result: - -- Data: { - -- a=1, - -- b=true - -- } - ``` +```lua +template("Data: {{data}}", { data = { a = 1, b = true } }) +--> { +-- a = 1, +-- b = true +-- } +``` - Missing keys render as an empty string. - ```lua - template("Missing: {{unknown}}", {}) - -- result: "Missing: " - ``` +```lua +template("Missing: {{unknown}}", {}) --> "Missing: " +``` - If a tag is not closed ({{name), it is emitted as-is. - ```lua - template("Hi {{name", { name = "Ada" }) - -- result: "Hi {{name" - ``` +```lua +template("Hi {{name", { name = "Ada" }) --> "Hi {{name" +``` - `template(view)` is shorthand for template("{{.}}", view). - ```lua - template({ a = 1, b = true }) - -- result: - -- { - -- a=1, - -- b=true - -- } - ``` +```lua +template({ a = 1, b = true }) +--> { +-- a = 1, +-- b = true +-- } +``` diff --git a/docs/src/modules/utils.md b/docs/src/modules/utils.md index c87fe07..65d38bd 100644 --- a/docs/src/modules/utils.md +++ b/docs/src/modules/utils.md @@ -1,33 +1,26 @@ --- -description: Common utility helpers. +desc: "Small shared utility helpers used by modules in this library." --- # `utils` -Common utility helpers. +Small shared utility helpers used by modules in this library. -## Import +## Usage ```lua -local utils = require("mods.utils") -``` - -## Quick Reference +utils = require "mods.utils" -| Function | Description | -| -------------------------------------- | ----------------------------------------------------- | -| [`quote(v)`](#fn-quotev) | Smart-quotes a string for readable Lua-like output. | +print(utils.quote('hello "world"')) --> 'hello "world"' +``` ## Functions -### `quote(v)` {#fn-quotev} +### `quote` -Smart-quotes a string for readable Lua-like output. +Smart-quote a string for readable Lua-like output. ```lua -print(utils.quote('He said "hi"')) --- 'He said "hi"' - -print(utils.quote([[say "hi" and 'bye']])) --- "say \"hi\" and 'bye'" +print(utils.quote('He said "hi"')) -- 'He said "hi"' +print(utils.quote('say "hi" and \\'bye\\'')) -- "say \"hi\" and 'bye'" ``` diff --git a/docs/src/modules/validate.md b/docs/src/modules/validate.md index 3c6ff65..8da88fd 100644 --- a/docs/src/modules/validate.md +++ b/docs/src/modules/validate.md @@ -1,84 +1,70 @@ --- -description: - Validate Lua values and path kinds with flexible aliases and customizable - messages. +desc: "Validation checks for values and filesystem path types." --- # `validate` Validation checks for values and filesystem path types. -## Import - -```lua -local mods = require("mods") -local validate = mods.validate -``` - ## Usage -```lua -local ok, err = validate.is.number("nope") --- result: false, "expected number, got string" +```lua [.lua] +validate= require "mods.validate" + +ok, err = validate.is.number("nope") +--> false, "expected number, got string" ok, err = validate.is_not.number(3.14) --- result: false, "expected not number" +--> false, "expected not number" ``` -> [!NOTE] -> -> When called without an explicit type (`validate(v, tp)`), `validate` defaults -> to checking `nil`. `validate()` is equivalent to `validate(nil, "nil")`, so it -> passes. `validate(1)` is equivalent to `validate(1, "nil")`, so it fails with -> `expected nil, got number`. - - - > [!IMPORTANT] > -> Validator access is case-insensitive. +> Behavior without `tp`: > -> - `validate.is.number`, `validate.IS.Number`, and `validate.I_s.NuMbEr` are -> equivalent. -> - Top-level aliases are underscore-insensitive too: `validate.is_number`, -> `validate.IS_NUMBER`, and `validate.isnumber` are equivalent. -> - Negated validators can be accessed as `is_not`, `isnot`, `isNot`, `not`, or -> `Not`. Example: `validate.is_not.number`, `validate.isNot.number`, -> `validate.isnot.number`, `validate.not.number`, `validate.is_not_number`, -> `validate.isnotnumber`. +> - `validate()` is equivalent to `validate(nil, "nil")` (passes) +> - `validate(1)` is equivalent to `validate(1, "nil")` (fails with +> `expected nil, got number`) +> +> Validator access is case-insensitive: +> +> - `validate.is.number` and `validate.IS.Number` are equivalent. +> - Top-level aliases are underscore-insensitive: `validate.is_number`, +> `validate.IS_NUMBER`, and `validate.isnumber`. +> - Negated validators are available via `is_not`, `isnot`, `isNot`, `not`, and +> `Not`, including underscore-insensitive top-level aliases (for example, +> `validate.is_not_number` and `validate.isnotnumber`). ## Callable Forms -All three are callable: - -- `validate(v, tp?)` -- `validate.is(v, tp?)` -- `validate.is_not(v, tp?)` - -Callable access is also alias/case flexible: - -- `validate.is`, `validate.IS` -- `validate.is_not`, `validate.isnot`, `validate.isNot`, `validate.not`, - `validate.Not` +`validate`, `validate.is`, and `validate.is_not` are all callable. ```lua -local ok, err - -ok, err = validate(1, "number") -- true -ok, err = validate.is(1, "number") -- true -ok, err = validate.IS(1, "number") -- true -ok, err = validate.is_not(1, "number") -- false, "expected not number" -ok, err = validate.Not(1, "number") -- false, "expected not number" +ok, err = validate(1, "number") --> true, nil +ok, err = validate.is("x", "string") --> true, nil +ok, err = validate.is_not(1, "number") --> false, "expected not number" ``` -## Quick Reference - -| Area | Common checks | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `is` (type) | [`boolean`](#is-boolean), [`function`](#is-function), [`nil`](#is-nil), [`number`](#is-number), [`string`](#is-string), [`table`](#is-table), [`thread`](#is-thread), [`userdata`](#is-userdata) | -| `is` (value) | [`false`](#is-false), [`true`](#is-true), [`falsy`](#is-falsy), [`callable`](#is-callable), [`integer`](#is-integer), [`truthy`](#is-truthy) | -| `is` (path) | [`block`](#is-block), [`char`](#is-char), [`device`](#is-device), [`dir`](#is-dir), [`fifo`](#is-fifo), [`file`](#is-file), [`link`](#is-link), [`socket`](#is-socket) | -| `is_not` (type/value) | [`boolean`](#is-not-boolean), [`function`](#is-not-function), [`nil`](#is-not-nil), [`number`](#is-not-number), [`string`](#is-not-string), [`table`](#is-not-table), [`thread`](#is-not-thread), [`userdata`](#is-not-userdata), [`false`](#is-not-false), [`true`](#is-not-true), [`falsy`](#is-not-falsy), [`callable`](#is-not-callable), [`integer`](#is-not-integer), [`truthy`](#is-not-truthy) | +> [!IMPORTANT] +> +> When `tp` is omitted, the default check is `"nil"`: +> +> - `validate()` is equivalent to `validate(nil, "nil")` (passes) +> - `validate(1)` is equivalent to `validate(1, "nil")` (fails) +> +> Callable namespace aliases are case-insensitive, and negated aliases are +> underscore-insensitive: +> +> - `validate.is`, `validate.IS` +> - `validate.is_not`, `validate.isnot`, `validate.isNot`, `validate["not"]`, +> `validate.Not` +> +> ```lua +> validate.is(1, "number") --> true +> validate.IS(1, "number") --> true +> validate.is_not(1, "number") --> false, "expected not number" +> validate.Not(1, "number") --> false, "expected not number" +> ``` ## Custom Messages @@ -105,11 +91,11 @@ Available placeholders: validate.messages.positive.number = "need {{expected}}, got {{got}} (value={{value}})" validate.messages.negative.number = "must not be {{expected}} (value={{value}})" -local ok, err = validate.is.number("x") --- result: false, 'need number, got string (value="x")' +ok, err = validate.is.number("x") +--> false, 'need number, got string (value="x")' ok, err = validate.is_not.number(42) --- result: false, "must not be number (value=42)" +--> false, "must not be number (value=42)" ``` ## Default Messages @@ -144,429 +130,425 @@ validate.on_fail = function(errmsg) return "custom failure" end -local ok, err = validate.number("x") --- prints: validation failed: expected number, got string --- result: false, "custom failure" +ok, err = validate.number("x") +--> prints -> validation failed: expected number, got string +--> false, "custom failure" ``` ## Functions -### `is` +**Type Checks**: -Positive validators. These checks pass only when the value matches the expected -type or check. +| Function | Description | +| ------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`boolean`](#boolean) | Returns `true` when `v` is a boolean. Otherwise returns `false` and an error message. | +| [`function`](#function) | Returns `true` when `v` is a function. Otherwise returns `false` and an error message. | +| [`nil`](#nil) | Returns `true` when `v` is `nil`. Otherwise returns `false` and an error message. | +| [`number`](#number) | Returns `true` when `v` is a number. Otherwise returns `false` and an error message. | +| [`string`](#string) | Returns `true` when `v` is a string. Otherwise returns `false` and an error message. | +| [`table`](#table) | Returns `true` when `v` is a table. Otherwise returns `false` and an error message. | +| [`thread`](#thread) | Returns `true` when `v` is a thread. Otherwise returns `false` and an error message. | +| [`userdata`](#userdata) | Returns `true` when `v` is userdata. Otherwise returns `false` and an error message. | +| [`not_boolean`](#not-boolean) | Returns `true` when `v` is **not** a boolean. Otherwise returns `false` and an error message. | +| [`not_function`](#not-function) | Returns `true` when `v` is **not** a function. Otherwise returns `false` and an error message. | +| [`not_nil`](#not-nil) | Returns `true` when `v` is **not** `nil`. Otherwise returns `false` and an error message. | +| [`not_number`](#not-number) | Returns `true` when `v` is **not** a number. Otherwise returns `false` and an error message. | +| [`not_string`](#not-string) | Returns `true` when `v` is **not** a string. Otherwise returns `false` and an error message. | +| [`not_table`](#not-table) | Returns `true` when `v` is **not** a table. Otherwise returns `false` and an error message. | +| [`not_thread`](#not-thread) | Returns `true` when `v` is **not** a thread. Otherwise returns `false` and an error message. | +| [`not_userdata`](#not-userdata) | Returns `true` when `v` is **not** userdata. Otherwise returns `false` and an error message. | -#### Negated Type Checks +**Value Checks**: -Lua type validators for [`boolean`](#is-boolean), [`function`](#is-function), -[`nil`](#is-nil), [`number`](#is-number), [`string`](#is-string), -[`table`](#is-table), [`thread`](#is-thread), and [`userdata`](#is-userdata). +| Function | Description | +| ------------------------------- | --------------------------------------------------------------------------------------------------- | +| [`false`](#false) | Returns `true` when `v` is exactly `false`. Otherwise returns `false` and an error message. | +| [`true`](#true) | Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an error message. | +| [`falsy`](#falsy) | Returns `true` when `v` is falsy. Otherwise returns `false` and an error message. | +| [`callable`](#callable) | Returns `true` when `v` is callable. Otherwise returns `false` and an error message. | +| [`integer`](#integer) | Returns `true` when `v` is an integer. Otherwise returns `false` and an error message. | +| [`truthy`](#truthy) | Returns `true` when `v` is truthy. Otherwise returns `false` and an error message. | +| [`not_false`](#not-false) | Returns `true` when `v` is **not** exactly `false`. Otherwise returns `false` and an error message. | +| [`not_true`](#not-true) | Returns `true` when `v` is **not** exactly `true`. Otherwise returns `false` and an error message. | +| [`not_falsy`](#not-falsy) | Returns `true` when `v` is **not** falsy. Otherwise returns `false` and an error message. | +| [`not_callable`](#not-callable) | Returns `true` when `v` is **not** callable. Otherwise returns `false` and an error message. | +| [`not_integer`](#not-integer) | Returns `true` when `v` is **not** an integer. Otherwise returns `false` and an error message. | +| [`not_truthy`](#not-truthy) | Returns `true` when `v` is **not** truthy. Otherwise returns `false` and an error message. | -##### `boolean(v)` {#is-boolean} +**Path Checks**: -Returns `true` when `v` is a Lua boolean. +| Function | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------- | +| [`block`](#block) | Returns `true` when `v` is a block device path. Otherwise returns `false` and an error message. | +| [`char`](#char) | Returns `true` when `v` is a char device path. Otherwise returns `false` and an error message. | +| [`device`](#device) | Returns `true` when `v` is a block or char device path. Otherwise returns `false` and an error message. | +| [`dir`](#dir) | Returns `true` when `v` is a directory path. Otherwise returns `false` and an error message. | +| [`fifo`](#fifo) | Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error message. | +| [`file`](#file) | Returns `true` when `v` is a file path. Otherwise returns `false` and an error message. | +| [`link`](#link) | Returns `true` when `v` is a symlink path. Otherwise returns `false` and an error message. | +| [`socket`](#socket) | Returns `true` when `v` is a socket path. Otherwise returns `false` and an error message. | -```lua -local ok, err = validate.is.boolean(true) -- true -ok, err = validate.is.boolean(1) --- result: false, "expected boolean, got number" -``` +### Type Checks + +Basic Lua type validators (and their negated variants). -##### `function(v)` {#is-function} +#### `boolean` -Returns `true` when `v` is a function value. +Returns `true` when `v` is a boolean. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.Function(function() end) -- true -ok, err = validate.is.Function("hello") --- result: false, "expected function, got string" +ok, err = validate.is.boolean(true) --> true, nil +ok, err = validate.is.boolean(1) --> false, "expected boolean, got number" ``` -##### `nil(v)` {#is-nil} +#### `function` -Returns `true` when `v` is `nil`. +Returns `true` when `v` is a function. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.Nil(nil) -- true -ok, err = validate.is.Nil(0) --- result: false, "expected nil, got number" +ok, err = validate.is.Function(function() end) --> true, nil +ok, err = validate.is.Function(1) +--> false, "expected function, got number" ``` -##### `number(v)` {#is-number} +#### `nil` -Returns `true` when `v` is a number. +Returns `true` when `v` is `nil`. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.number(3.14) -- true -ok, err = validate.is.number("3.14") --- result: false, "expected number, got string" +ok, err = validate.is.Nil(nil) --> true, nil +ok, err = validate.is.Nil(0) --> false, "expected nil, got number" ``` -##### `string(v)` {#is-string} +#### `number` -Returns `true` when `v` is a string. +Returns `true` when `v` is a number. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.string("hello") -- true -ok, err = validate.is.string(false) --- result: false, "expected string, got boolean" +ok, err = validate.is.number(42) --> true, nil +ok, err = validate.is.number("x") --> false, "expected number, got string" ``` -##### `table(v)` {#is-table} +#### `string` -Returns `true` when `v` is a table. +Returns `true` when `v` is a string. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.table({}) -- true -ok, err = validate.is.table("x") --- result: false, "expected table, got string" +ok, err = validate.is.string("hello") --> true, nil +ok, err = validate.is.string(1) --> false, "expected string, got number" ``` -##### `thread(v)` {#is-thread} +#### `table` -Returns `true` when `v` is a coroutine thread. +Returns `true` when `v` is a table. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.thread(coroutine.create(function() end)) -- true -ok, err = validate.is.thread(function() end) --- result: false, "expected thread, got function" +ok, err = validate.is.table({}) --> true, nil +ok, err = validate.is.table(1) --> false, "expected table, got number" ``` -##### `userdata(v)` {#is-userdata} +#### `thread` -Returns `true` when `v` is a userdata value. +Returns `true` when `v` is a thread. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.userdata(io.stdout) -- true -ok, err = validate.is.userdata({}) --- result: false, "expected userdata, got table" +co = coroutine.create(function() end) +ok, err = validate.is.thread(co) --> true, nil +ok, err = validate.is.thread(1) --> false, "expected thread, got number" ``` -#### Negated Value Checks - -Validators for common truth/value semantics. +#### `userdata` -##### `false(v)` {#is-false} - -Returns `true` when `v` is exactly `false`. +Returns `true` when `v` is userdata. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.False(false) -- true -ok, err = validate.is.False(true) --- result: false, "expected false, got true" +ok, err = validate.is.userdata(io.stdout) --> true, nil +ok, err = validate.is.userdata(1) --> false, "expected userdata, got number" ``` -##### `true(v)` {#is-true} +#### `not_boolean` -Returns `true` when `v` is exactly `true`. +Returns `true` when `v` is **not** a boolean. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.True(true) -- true -ok, err = validate.is.True(false) --- result: false, "expected true, got false" +ok, err = validate.is_not.boolean(1) --> true, nil +ok, err = validate.is_not.boolean(true) --> false, "expected not boolean" ``` -##### `falsy(v)` {#is-falsy} +#### `not_function` -Returns `true` when `v` is `false` or `nil`. +Returns `true` when `v` is **not** a function. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.falsy(false) -- true -ok, err = validate.is.falsy(1) --- result: false, "expected falsy, got number" +ok, err = validate.is_not.Function(1) --> true, nil +ok, err = validate.is_not.Function(function() end) --> false, "expected not function" ``` -##### `callable(v)` {#is-callable} +#### `not_nil` -Returns `true` when `v` can be called (function or callable table). +Returns `true` when `v` is **not** `nil`. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.callable(function() end) -- true -ok, err = validate.is.callable({}) --- result: false, "expected callable, got table" +ok, err = validate.is_not.Nil(0) --> true, nil +ok, err = validate.is_not.Nil(nil) --> false, "expected not nil" ``` -##### `integer(v)` {#is-integer} +#### `not_number` -Returns `true` when `v` is a whole number. +Returns `true` when `v` is **not** a number. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.integer(42) -- true -ok, err = validate.is.integer(4.2) --- result: false, "expected integer, got 4.2" +ok, err = validate.is_not.number("x") --> true, nil +ok, err = validate.is_not.number(42) --> false, "expected not number" ``` -##### `truthy(v)` {#is-truthy} +#### `not_string` -Returns `true` when `v` is neither `false` nor `nil`. +Returns `true` when `v` is **not** a string. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.truthy("non-empty") -- true -ok, err = validate.is.truthy(nil) --- result: false, "expected truthy, got nil" +ok, err = validate.is_not.string(1) --> true, nil +ok, err = validate.is_not.string("hello") --> false, "expected not string" ``` -#### Path Checks +#### `not_table` -Filesystem kind validators for paths. - -> [!IMPORTANT] -> -> Path checks require LuaFileSystem -> ([`lfs`](https://github.com/lunarmodules/luafilesystem)). -> -> These functions raise an error if `lfs` is not installed. - -##### `block(path)` {#is-block} - -Returns `true` when `path` points to a block device. +Returns `true` when `v` is **not** a table. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.block("/dev/sda") -- true -ok, err = validate.is.block(false) --- result: false, "false is not a valid block path" -ok, err = validate.is.block("/dev/null") --- result: false, '"/dev/null" is not a valid block path' +ok, err = validate.is_not.table(1) --> true, nil +ok, err = validate.is_not.table({}) --> false, "expected not table" ``` -##### `char(path)` {#is-char} +#### `not_thread` -Returns `true` when `path` points to a character device. +Returns `true` when `v` is **not** a thread. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.char("/dev/null") -- true -ok, err = validate.is.char(nil) --- result: false, "nil is not a valid char path" -ok, err = validate.is.char("/tmp") --- result: false, '"/tmp" is not a valid char path' +co = coroutine.create(function() end) +ok, err = validate.is_not.thread(1) --> true, nil +ok, err = validate.is_not.thread(co) --> false, "expected not thread" ``` -##### `device(path)` {#is-device} +#### `not_userdata` -Returns `true` when `path` points to any device node. +Returns `true` when `v` is **not** userdata. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.device("/dev/null") -- true -ok, err = validate.is.device(true) --- result: false, "true is not a valid device path" -ok, err = validate.is.device("README.md") --- result: false, '"README.md" is not a valid device path' +ok, err = validate.is_not.userdata(1) --> true, nil +ok, err = validate.is_not.userdata(io.stdout) --> false, "expected not userdata" ``` -##### `dir(path)` {#is-dir} +### Value Checks + +Value-state validators (exact true/false, truthy/falsy, callable, integer). -Returns `true` when `path` points to a directory. +#### `false` + +Returns `true` when `v` is exactly `false`. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.dir("/tmp") -- true -ok, err = validate.is.dir(0) --- result: false, "0 is not a valid dir path" -ok, err = validate.is.dir("README.md") --- result: false, '"README.md" is not a valid dir path' +ok, err = validate.is.False(false) --> true, nil +ok, err = validate.is.False(true) --> false, "expected false, got true" ``` -##### `fifo(path)` {#is-fifo} +#### `true` -Returns `true` when `path` points to a FIFO (named pipe). +Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is.fifo("/path/to/fifo") -- true -ok, err = validate.is.fifo(7) --- result: false, "7 is not a valid fifo path" -ok, err = validate.is.fifo("README.md") --- result: false, '"README.md" is not a valid fifo path' +ok, err = validate.is.True(true) --> true, nil +ok, err = validate.is.True(false) --> false, "expected true, got false" ``` -##### `file(path)` {#is-file} +#### `falsy` -Returns `true` when `path` points to a regular file. +Returns `true` when `v` is falsy. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.file("README.md") -- true -ok, err = validate.is.file(42) --- result: false, "42 is not a valid file path" -ok, err = validate.is.file("src") --- result: false, '"src" is not a valid file path' +ok, err = validate.is.falsy(false) --> true, nil +ok, err = validate.is.falsy(1) --> false, "expected falsy, got number" ``` -##### `link(path)` {#is-link} +#### `callable` -Returns `true` when `path` points to a symbolic link. +Returns `true` when `v` is callable. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.link("/path/to/link") -- true -ok, err = validate.is.link(99) --- result: false, "99 is not a valid link path" -ok, err = validate.is.link("README.md") --- result: false, '"README.md" is not a valid link path' +ok, err = validate.is.callable(type) --> true, nil +ok, err = validate.is.callable(1) --> false, "expected callable, got number" ``` -##### `socket(path)` {#is-socket} +#### `integer` -Returns `true` when `path` points to a socket. +Returns `true` when `v` is an integer. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is.socket("/path/to/socket") -- true -ok, err = validate.is.socket(500) --- result: false, "500 is not a valid socket path" -ok, err = validate.is.socket("README.md") --- result: false, '"README.md" is not a valid socket path' +ok, err = validate.is.integer(1) --> true, nil +ok, err = validate.is.integer(1.5) --> false, "expected integer, got 1.5" ``` -### `is_not` - -Negated validators. These checks pass only when the value does not match the -expected type or check. - -> [!IMPORTANT] -> -> `is_not` supports type and value checks. Path checks are available on -> [`is`](#is) only. +#### `truthy` -#### Type Checks +Returns `true` when `v` is truthy. Otherwise returns `false` and an error +message. -Negated Lua type validators. +```lua +ok, err = validate.is.truthy(1) --> true, nil +ok, err = validate.is.truthy(false) --> false, "expected truthy, got boolean" +``` -##### `boolean(v)` {#is-not-boolean} +#### `not_false` -Returns `true` when `v` is not a Lua boolean. +Returns `true` when `v` is **not** exactly `false`. Otherwise returns `false` +and an error message. ```lua -local ok, err = validate.is_not.boolean(123) -- true -ok, err = validate.is_not.boolean(true) --- result: false, "expected not boolean" +ok, err = validate.is_not.False(true) --> true, nil +ok, err = validate.is_not.False(false) --> false, "expected not false" ``` -##### `function(v)` {#is-not-function} +#### `not_true` -Returns `true` when `v` is not a function value. +Returns `true` when `v` is **not** exactly `true`. Otherwise returns `false` and +an error message. ```lua -local ok, err = validate.is_not.Function("abc") -- true -ok, err = validate.is_not.Function(function() end) --- result: false, "expected not function" +ok, err = validate.is_not.True(false) --> true, nil +ok, err = validate.is_not.True(true) --> false, "expected not true" ``` -##### `nil(v)` {#is-not-nil} +#### `not_falsy` -Returns `true` when `v` is not `nil`. +Returns `true` when `v` is **not** falsy. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is_not.Nil(123) -- true -ok, err = validate.is_not.Nil(nil) --- result: false, "expected not nil" +ok, err = validate.is_not.falsy(1) --> true, nil +ok, err = validate.is_not.falsy(false) --> false, "expected not falsy" ``` -##### `number(v)` {#is-not-number} +#### `not_callable` -Returns `true` when `v` is not a number. +Returns `true` when `v` is **not** callable. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.number("3.14") -- true -ok, err = validate.is_not.number(3.14) --- result: false, "expected not number" +ok, err = validate.is_not.callable(1) --> true, nil +ok, err = validate.is_not.callable(function() end) --> false, "expected not callable" ``` -##### `string(v)` {#is-not-string} +#### `not_integer` -Returns `true` when `v` is not a string. +Returns `true` when `v` is **not** an integer. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.string(false) -- true -ok, err = validate.is_not.string("hello") --- result: false, "expected not string" +ok, err = validate.is_not.integer(1.5) --> true, nil +ok, err = validate.is_not.integer(1) --> false, "expected non-integer, got 1" ``` -##### `table(v)` {#is-not-table} +#### `not_truthy` -Returns `true` when `v` is not a table. +Returns `true` when `v` is **not** truthy. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.table(false) -- true -ok, err = validate.is_not.table({}) --- result: false, "expected not table" +ok, err = validate.is_not.truthy(false) --> true, nil +ok, err = validate.is_not.truthy(1) --> false, "expected not truthy" ``` -##### `thread(v)` {#is-not-thread} +### Path Checks + +#### `block` -Returns `true` when `v` is not a coroutine thread. +Returns `true` when `v` is a block device path. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.thread(function() end) -- true -ok, err = validate.is_not.thread(coroutine.create(function() end)) --- result: false, "expected not thread" +ok, err = validate.is.block(".") ``` -##### `userdata(v)` {#is-not-userdata} +#### `char` -Returns `true` when `v` is not a userdata value. +Returns `true` when `v` is a char device path. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.userdata({}) -- true -ok, err = validate.is_not.userdata(io.stdout) --- result: false, "expected not userdata" +ok, err = validate.is.char(".") ``` -#### Value Checks - -Negated validators for common truth/value semantics. - -##### `false(v)` {#is-not-false} +#### `device` -Returns `true` when `v` is not exactly `false`. +Returns `true` when `v` is a block or char device path. Otherwise returns +`false` and an error message. ```lua -local ok, err = validate.is_not.False(true) -- true -ok, err = validate.is_not.False(false) --- result: false, "expected not false" +ok, err = validate.is.device(".") ``` -##### `true(v)` {#is-not-true} +#### `dir` -Returns `true` when `v` is not exactly `true`. +Returns `true` when `v` is a directory path. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.True(false) -- true -ok, err = validate.is_not.True(true) --- result: false, "expected not true" +ok, err = validate.is.dir(".") ``` -##### `falsy(v)` {#is-not-falsy} +#### `fifo` -Returns `true` when `v` is neither `false` nor `nil`. +Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is_not.falsy(true) -- true -ok, err = validate.is_not.falsy(nil) --- result: false, "expected not falsy" +ok, err = validate.is.fifo(".") ``` -##### `callable(v)` {#is-not-callable} +#### `file` -Returns `true` when `v` cannot be called. +Returns `true` when `v` is a file path. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is_not.callable({}) -- true -ok, err = validate.is_not.callable(function() end) --- result: false, "expected not callable" +ok, err = validate.is.file(".") ``` -##### `integer(v)` {#is-not-integer} +#### `link` -Returns `true` when `v` is not a whole number. +Returns `true` when `v` is a symlink path. Otherwise returns `false` and an +error message. ```lua -local ok, err = validate.is_not.integer(13.4) -- true -ok, err = validate.is_not.integer(13) --- result: false, "expected non-integer, got 13" +ok, err = validate.is.link(".") ``` -##### `truthy(v)` {#is-not-truthy} +#### `socket` -Returns `true` when `v` is `false` or `nil`. +Returns `true` when `v` is a socket path. Otherwise returns `false` and an error +message. ```lua -local ok, err = validate.is_not.truthy(nil) -- true -ok, err = validate.is_not.truthy(1) --- result: false, "expected not truthy" +ok, err = validate.is.socket(".") ```