From 06e5bdd5f628a0ae04028eadc34c67cced200df6 Mon Sep 17 00:00:00 2001 From: haithium <128622475+haithium@users.noreply.github.com> Date: Thu, 7 May 2026 08:06:15 +0000 Subject: [PATCH] chore(docs): auto-generate docs --- docs/src/modules/calendar.md | 316 ++++++++++++++++ docs/src/modules/fs.md | 404 +++++++++++++-------- docs/src/modules/glob.md | 267 ++++++++++++++ docs/src/modules/is.md | 288 +++++++-------- docs/src/modules/json.md | 165 +++++++++ docs/src/modules/keyword.md | 102 +++--- docs/src/modules/list.md | 610 ++++++++++++++++--------------- docs/src/modules/log.md | 94 +++++ docs/src/modules/ntpath.md | 12 + docs/src/modules/operator.md | 222 ++++++------ docs/src/modules/path.md | 633 ++++++++++++++++----------------- docs/src/modules/runtime.md | 109 +++--- docs/src/modules/set.md | 337 +++++++++--------- docs/src/modules/str.md | 592 ++++++++++++++---------------- docs/src/modules/stringcase.md | 170 ++++----- docs/src/modules/tbl.md | 249 +++++++------ docs/src/modules/utils.md | 139 ++++---- docs/src/modules/validate.md | 373 +++++++++---------- 18 files changed, 3062 insertions(+), 2020 deletions(-) create mode 100644 docs/src/modules/calendar.md create mode 100644 docs/src/modules/glob.md create mode 100644 docs/src/modules/json.md create mode 100644 docs/src/modules/log.md diff --git a/docs/src/modules/calendar.md b/docs/src/modules/calendar.md new file mode 100644 index 0000000..059a955 --- /dev/null +++ b/docs/src/modules/calendar.md @@ -0,0 +1,316 @@ +--- +description: "Calendar and date helpers." +--- + +# `calendar` + +Calendar and date helpers. + +## Usage + +```lua +cal = require "mods.calendar" + +print(cal.weekday(2026, 3, 26)) --> 4 +``` + +## Functions + +| Function | Description | +| ------------------------------------------------------ | --------------------------------- | +| [`getfirstweekday()`](#fn-getfirstweekday) | Return the default first weekday. | +| [`setfirstweekday(firstweekday)`](#fn-setfirstweekday) | Set the default first weekday. | + +**Calendar Calculations**: + +| Function | Description | +| ------------------------------------------- | ----------------------------------------------------------------------- | +| [`isleap(year)`](#fn-isleap) | Return `true` for leap years. | +| [`leapdays(y1, y2)`](#fn-leapdays) | Return the number of leap years from `y1` up to but not including `y2`. | +| [`monthrange(year, month)`](#fn-monthrange) | Return the first weekday and number of days for a month. | +| [`weekday(year, month, day)`](#fn-weekday) | Return weekday number where Monday is `1` and Sunday is `7`. | + +**Formatting**: + +| Function | Description | +| ----------------------------------------------------- | ------------------------------------------- | +| [`weekheader(width?, firstweekday?)`](#fn-weekheader) | Return the formatted weekday header string. | + +**Iterators**: + +| Function | Description | +| -------------------------------------------------------- | ---------------------------------------------------------------------- | +| [`monthdays(year, month, firstweekday?)`](#fn-monthdays) | Iterate `(year, month, day, weekday)` tuples for a full calendar grid. | +| [`weekdays(firstweekday?)`](#fn-weekdays) | Iterate weekday numbers for one full week. | + + + +### `getfirstweekday()` + +Return the default first weekday. + +**Return**: + +- `firstweekday` (`1|2|3|4|5|6|7`) + +**Example**: + +```lua +print(cal.getfirstweekday()) --> 1 +``` + +> [!NOTE] +> +> This returns the same value as `cal.firstweekday`. + + + +### `setfirstweekday(firstweekday)` + +Set the default first weekday. + +**Parameters**: + +- `firstweekday` (`1|2|3|4|5|6|7`) + +**Example**: + +```lua +cal.setfirstweekday(cal.SUNDAY) +``` + +> [!NOTE] +> +> This updates the same value as `cal.firstweekday = ...`. + +### Calendar Calculations + + + +#### `isleap(year)` + +Return `true` for leap years. + +**Parameters**: + +- `year` (`integer`) + +**Return**: + +- `isLeap` (`boolean`) + +**Example**: + +```lua +print(cal.isleap(2024)) --> true +``` + + + +#### `leapdays(y1, y2)` + +Return the number of leap years from `y1` up to but not including `y2`. + +**Parameters**: + +- `y1` (`integer`) +- `y2` (`integer`) + +**Return**: + +- `count` (`integer`) + +**Example**: + +```lua +print(cal.leapdays(2000, 2025)) --> 7 +``` + + + +#### `monthrange(year, month)` + +Return the first weekday and number of days for a month. + +**Parameters**: + +- `year` (`integer`) +- `month` (`integer`) + +**Return**: + +- `weekday` (`1|2|3|4|5|6|7`) +- `ndays` (`integer`) + +**Example**: + +```lua +wday, ndays = cal.monthrange(2026, 2) +print(wday, ndays) --> 7 28 +``` + + + +#### `weekday(year, month, day)` + +Return weekday number where Monday is `1` and Sunday is `7`. + +**Parameters**: + +- `year` (`integer`) +- `month` (`1|2|3|4|5|6|7|8|9|10|11|12`) +- `day` + (`1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31`) + +**Return**: + +- `weekday` (`1|2|3|4|5|6|7`) + +**Example**: + +```lua +print(cal.weekday(2026, 3, 26)) --> 4 +``` + +### Formatting + + + +#### `weekheader(width?, firstweekday?)` + +Return the formatted weekday header string. + +**Parameters**: + +- `width?` (`integer`) +- `firstweekday?` (`1|2|3|4|5|6|7`) + +**Return**: + +- `header` (`string`) + +**Example**: + +```lua +print(cal.weekheader(1, cal.SUNDAY)) --> "S M T W T F S" +print(cal.weekheader(2, cal.SUNDAY)) --> "Su Mo Tu We Th Fr Sa" +print(cal.weekheader(3, cal.SUNDAY)) --> "Sun Mon Tue Wed Thu Fri Sat" +``` + +### Iterators + + + +#### `monthdays(year, month, firstweekday?)` + +Iterate `(year, month, day, weekday)` tuples for a full calendar grid. + +**Parameters**: + +- `year` (`integer`) +- `month` (`1|2|3|4|5|6|7|8|9|10|11|12`) +- `firstweekday?` (`1|2|3|4|5|6|7`) + +**Return**: + +- `iter` + (`fun():year:integer,month:modsCalendarMonth,day:modsCalendarMonthday,weekday:modsCalendarWeekday`) + +**Example**: + +```lua +local mods = require "mods" + +local List = mods.List +local cal = mods.calendar +local str = mods.str + +local header = cal.weekheader(2) +local lines = List({ + str.center(("%s %d"):format(cal.months[cal.FEBRUARY], 2026), #header), + header, +}) + +local cells = List() +for _, m, d, _ in cal.monthdays(2026, cal.FEBRUARY) do + cells:append(m == cal.FEBRUARY and ("%2d"):format(d) or " ") + if #cells == 7 then + lines:append(cells:join(" ")) + cells = List() + end +end + +print(lines:join("\n")) +-- February 2026 +-- Mo Tu We Th Fr Sa Su +-- 1 +-- 2 3 4 5 6 7 8 +-- 9 10 11 12 13 14 15 +-- 16 17 18 19 20 21 22 +-- 23 24 25 26 27 28 +``` + + + +#### `weekdays(firstweekday?)` + +Iterate weekday numbers for one full week. + +**Parameters**: + +- `firstweekday?` (`1|2|3|4|5|6|7`) + +**Return**: + +- `iter` (`fun():modsCalendarWeekday`) + +**Example**: + +```lua +local weekdays = {} +for day in cal.weekdays() do + weekdays[#weekdays + 1] = day +end +print(table.concat(weekdays, ", ")) --> "1, 2, 3, 4, 5, 6, 7" +``` + +## Fields + + + +### `days` (`mods.List`) + +Weekday names indexed from `1` (Monday) to `7` (Sunday). + +```lua +print(cal.days[1]) --> Monday +print(cal.days[7]) --> Sunday +``` + + + +### `firstweekday` (`1|2|3|4|5|6|7`) + +The default first weekday field. + +```lua +print(cal.firstweekday) --> 1 +cal.firstweekday = cal.SUNDAY +print(cal.firstweekday) --> 7 +``` + +> [!NOTE] +> +> Reading or writing this property is equivalent to calling `getfirstweekday()` +> or `setfirstweekday()`. + + + +### `months` (`mods.List`) + +Month names indexed from `1` to `12`. + +```lua +print(cal.months[1]) --> January +print(cal.months[12]) --> December +``` diff --git a/docs/src/modules/fs.md b/docs/src/modules/fs.md index 0dabb2e..a646a9b 100644 --- a/docs/src/modules/fs.md +++ b/docs/src/modules/fs.md @@ -6,6 +6,11 @@ description: "Filesystem I/O, metadata, and filesystem path operations." Filesystem I/O, metadata, and filesystem path operations. +> [!NOTE] +> +> This module requires +> [LuaFileSystem (`lfs`)](https://github.com/lunarmodules/luafilesystem). + ## Usage ```lua @@ -18,53 +23,57 @@ print(fs.read_text("tmp/cache/app/data.txt")) --> "hello" ## Functions -**Reading**: +**Existence Checks**: -| Function | Description | -| ------------------------------------- | -------------------------------------- | -| [`read_bytes(path)`](#fn-read-bytes) | Read full file in binary mode. | -| [`read_text(path)`](#fn-read-text) | Read full file in text mode. | -| [`dir(path, opts?)`](#fn-dir) | Iterator over items in `path`. | -| [`listdir(path, opts?)`](#fn-listdir) | Return direct children of a directory. | +| Function | Description | +| ------------------------------ | ------------------------------------------------------------ | +| [`exists(path)`](#fn-exists) | Return `true` when a path exists. | +| [`lexists(path)`](#fn-lexists) | Return `true` when a path exists without following symlinks. | -**Writing**: +**Filesystem Mutations**: | Function | Description | | -------------------------------------------- | ----------------------------------------------------------------------------- | -| [`write_bytes(path, data)`](#fn-write-bytes) | Write full file in binary mode. | -| [`write_text(path, data)`](#fn-write-text) | Write full file in text mode. | -| [`touch(path)`](#fn-touch) | Create file if missing without truncating, or update timestamps if it exists. | +| [`cd(path)`](#fn-cd) | Change the current working directory. | +| [`cp(src, dst)`](#fn-cp) | Copy a file or directory tree. | +| [`cwd()`](#fn-cwd) | Return the current working directory. | +| [`link(path, linkpath)`](#fn-link) | Create a hard link. | +| [`mkdir(path, parents?)`](#fn-mkdir) | Create a directory. | | [`rename(oldname, newname)`](#fn-rename) | Rename or move a filesystem entry. | | [`rm(path, recursive?)`](#fn-rm) | Remove a filesystem entry, or a directory tree when `recursive` is `true`. | -| [`mkdir(path, parents?)`](#fn-mkdir) | Create a directory. | -| [`cp(src, dst)`](#fn-cp) | Copy a file or directory tree. | +| [`symlink(path, linkpath)`](#fn-symlink) | Create a symbolic link. | +| [`touch(path)`](#fn-touch) | Create file if missing without truncating, or update timestamps if it exists. | +| [`write_bytes(path, data)`](#fn-write-bytes) | Write full file in binary mode. | +| [`write_text(path, data)`](#fn-write-text) | Write full file in text mode. | **Metadata**: -| Function | Description | -| ------------------------------------------ | ---------------------------------------------------------------------------------- | -| [`getsize(path)`](#fn-getsize) | Return file size in bytes. | -| [`getatime(path)`](#fn-getatime) | Return last access time. | -| [`getmtime(path)`](#fn-getmtime) | Return last modification time. | -| [`getctime(path)`](#fn-getctime) | Return metadata change time. | -| [`lstat(path)`](#fn-lstat) | Return symlink-aware file attributes. | -| [`stat(path)`](#fn-stat) | Return file attributes. | -| [`samefile(path_a, path_b)`](#fn-samefile) | Return whether two paths refer to the same file, or `nil` and an error on failure. | +| Function | Description | +| -------------------------------- | ---------------------------------------------------------------------------------- | +| [`getatime(path)`](#fn-getatime) | Return last access time. | +| [`getctime(path)`](#fn-getctime) | Return metadata change time. | +| [`getmtime(path)`](#fn-getmtime) | Return last modification time. | +| [`getsize(path)`](#fn-getsize) | Return file size in bytes. | +| [`lstat(path)`](#fn-lstat) | Return symlink-aware file attributes. | +| [`samefile(a, b)`](#fn-samefile) | Return whether two paths refer to the same file, or `nil` and an error on failure. | +| [`stat(path)`](#fn-stat) | Return file attributes. | -**Existence Checks**: +**Reading**: -| Function | Description | -| ------------------------------ | ------------------------------------------------------------ | -| [`exists(path)`](#fn-exists) | Return `true` when a path exists. | -| [`lexists(path)`](#fn-lexists) | Return `true` when a path exists without following symlinks. | +| Function | Description | +| ------------------------------------- | -------------------------------------- | +| [`dir(path, opts?)`](#fn-dir) | Iterator over items in `path`. | +| [`listdir(path, opts?)`](#fn-listdir) | Return direct children of a directory. | +| [`read_bytes(path)`](#fn-read-bytes) | Read full file in binary mode. | +| [`read_text(path)`](#fn-read-text) | Read full file in text mode. | -### Reading +### Existence Checks - + -#### `read_bytes(path)` +#### `exists(path)` -Read full file in binary mode. +Return `true` when a path exists. **Parameters**: @@ -72,21 +81,23 @@ Read full file in binary mode. **Return**: -- `body` (`string?`): File contents read in binary mode, or `nil` on failure. -- `errmsg` (`string?`): Error message when the check fails. -- `errcode` (`integer?`): OS error code when available. +- `exists` (`boolean`): True when the path exists. **Example**: ```lua -fs.read_bytes("README.md") +fs.exists("README.md") --> true ``` - +> [!NOTE] +> +> Broken symlinks return `false`. -#### `read_text(path)` + -Read full file in text mode. +#### `lexists(path)` + +Return `true` when a path exists without following symlinks. **Parameters**: @@ -94,130 +105,121 @@ Read full file in text mode. **Return**: -- `body` (`string?`): File contents read in text mode, or `nil` on failure. -- `errmsg` (`string?`): Error message when the check fails. -- `errcode` (`integer?`): OS error code when available. +- `exists` (`boolean`): True when the path or symlink entry exists. **Example**: ```lua -fs.read_text("README.md") +fs.lexists("README.md") --> true ``` - +> [!NOTE] +> +> Broken symlinks return `true`. -#### `dir(path, opts?)` +### Filesystem Mutations -Iterator over items in `path`. + + +#### `cd(path)` + +Change the current working directory. **Parameters**: -- `path` (`string`): Input path. -- `opts?` - (`{hidden?:boolean, recursive?:boolean, follow_links?:boolean, type?:string}`): - Optional traversal options. +- `path` (`string`): Directory path to switch into. **Return**: -- `iterator` - (`(fun(state:table, prev?:string):basename:string?, type:"file"|"directory"|"link"|"fifo"|"socket"|"char"|"block"|"unknown"?)?`): - Iterator, or `nil` on failure. -- `state` (`table|string`): Iterator state on success, or error message on +- `changed` (`true?`): `true` when the directory change succeeds, or `nil` on failure. +- `errmsg` (`string?`): Error message when the change fails. **Example**: ```lua -for name, type in fs.dir(path.cwd(), { recursive = true }) do - print(name, type) -end +fs.cd("src") ``` - + -#### `listdir(path, opts?)` +#### `cp(src, dst)` -Return direct children of a directory. +Copy a file or directory tree. **Parameters**: -- `path` (`string`): Input path. -- `opts?` - (`{hidden?:boolean, recursive?:boolean, follow_links?:boolean, type?:string}`): - Optional traversal options. +- `src` (`string`): Source path. +- `dst` (`string`): Destination path. **Return**: -- `paths` (`mods.List?`): Direct child paths. -- `err` (`string?`): Error message when traversal setup fails. +- `copied` (`true?`): `true` when copying succeeds, or `nil` on failure. +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.listdir("src") +fs.cp("a.txt", "b.txt") +fs.cp("src", "backup/src") ``` -### Writing - - - -#### `write_bytes(path, data)` + -Write full file in binary mode. +#### `cwd()` -**Parameters**: - -- `path` (`string`): Input path. -- `data` (`string`): Input data. +Return the current working directory. **Return**: -- `written` (`true?`): `true` when writing succeeds, or `nil` on failure. -- `errmsg` (`string?`): Error message when the check fails. +- `cwd` (`string?`): Current working directory, or `nil` on failure. +- `errmsg` (`string?`): Error message when the lookup fails. - `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.write_bytes("tmp.bin", "abc") --> true, nil +fs.cwd() ``` - + -#### `write_text(path, data)` +#### `link(path, linkpath)` -Write full file in text mode. +Create a hard link. **Parameters**: -- `path` (`string`): Input path. -- `data` (`string`): Input data. +- `path` (`string`): Existing path to link to. +- `linkpath` (`string`): New link path to create. **Return**: -- `written` (`true?`): `true` when writing succeeds, or `nil` on failure. +- `linked` (`true?`): `true` when link creation succeeds, or `nil` on failure. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.write_text("tmp.txt", "abc") --> true, nil +fs.link("target.txt", "hardlink.txt") ``` - + -#### `touch(path)` +#### `mkdir(path, parents?)` -Create file if missing without truncating, or update timestamps if it exists. +Create a directory. **Parameters**: - `path` (`string`): Input path. +- `parents?` (`boolean`): Create missing parent directories when `true`. **Return**: -- `touched` (`true?`): `true` when the file exists after touch, or `nil` on +- `created` (`true?`): `true` when directory creation succeeds, or `nil` on failure. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. @@ -225,7 +227,7 @@ Create file if missing without truncating, or update timestamps if it exists. **Example**: ```lua -fs.touch("tmp.txt") --> true, nil +fs.mkdir("tmp/a/b", true) ``` @@ -279,20 +281,42 @@ fs.rm("tmp.txt") --> true, nil fs.rm("tmp/cache", true) --> true, nil ``` - + -#### `mkdir(path, parents?)` +#### `symlink(path, linkpath)` -Create a directory. +Create a symbolic link. + +**Parameters**: + +- `path` (`string`): Path to reference from the new symlink. +- `linkpath` (`string`): New symlink path to create. + +**Return**: + +- `linked` (`true?`): `true` when link creation succeeds, or `nil` on failure. +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. + +**Example**: + +```lua +fs.symlink("target.txt", "symlink.txt") +``` + + + +#### `touch(path)` + +Create file if missing without truncating, or update timestamps if it exists. **Parameters**: - `path` (`string`): Input path. -- `parents?` (`boolean`): Create missing parent directories when `true`. **Return**: -- `created` (`true?`): `true` when directory creation succeeds, or `nil` on +- `touched` (`true?`): `true` when the file exists after touch, or `nil` on failure. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. @@ -300,57 +324,57 @@ Create a directory. **Example**: ```lua -fs.mkdir("tmp/a/b", true) +fs.touch("tmp.txt") --> true, nil ``` - + -#### `cp(src, dst)` +#### `write_bytes(path, data)` -Copy a file or directory tree. +Write full file in binary mode. **Parameters**: -- `src` (`string`): Source path. -- `dst` (`string`): Destination path. +- `path` (`string`): Input path. +- `data` (`string`): Input data. **Return**: -- `copied` (`true?`): `true` when copying succeeds, or `nil` on failure. +- `written` (`true?`): `true` when writing succeeds, or `nil` on failure. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.cp("a.txt", "b.txt") -fs.cp("src", "backup/src") +fs.write_bytes("tmp.bin", "abc") --> true, nil ``` -### Metadata - - + -#### `getsize(path)` +#### `write_text(path, data)` -Return file size in bytes. +Write full file in text mode. **Parameters**: - `path` (`string`): Input path. +- `data` (`string`): Input data. **Return**: -- `size` (`integer?`): File size in bytes. +- `written` (`true?`): `true` when writing succeeds, or `nil` on failure. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.getsize("README.md") --> 1234 +fs.write_text("tmp.txt", "abc") --> true, nil ``` +### Metadata + #### `getatime(path)` @@ -373,6 +397,28 @@ Return last access time. fs.getatime("README.md") --> 1712345678 ``` + + +#### `getctime(path)` + +Return metadata change time. + +**Parameters**: + +- `path` (`string`): Input path. + +**Return**: + +- `timestamp` (`number?`): Change time (seconds since epoch). +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. + +**Example**: + +```lua +fs.getctime("README.md") --> 1712345678 +``` + #### `getmtime(path)` @@ -395,11 +441,11 @@ Return last modification time. fs.getmtime("README.md") --> 1712345678 ``` - + -#### `getctime(path)` +#### `getsize(path)` -Return metadata change time. +Return file size in bytes. **Parameters**: @@ -407,14 +453,14 @@ Return metadata change time. **Return**: -- `timestamp` (`number?`): Change time (seconds since epoch). +- `size` (`integer?`): File size in bytes. - `errmsg` (`string?`): Error message when the check fails. - `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.getctime("README.md") --> 1712345678 +fs.getsize("README.md") --> 1234 ``` @@ -440,6 +486,30 @@ Return symlink-aware file attributes. fs.lstat("README.md") ``` + + +#### `samefile(a, b)` + +Return whether two paths refer to the same file, or `nil` and an error on +failure. + +**Parameters**: + +- `a` (`string`): Input path. +- `b` (`string`): Input path. + +**Return**: + +- `isSameFile` (`boolean?`): True when both paths refer to the same file. +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. + +**Example**: + +```lua +fs.samefile("README.md", "README.md") --> true +``` + #### `stat(path)` @@ -464,61 +534,85 @@ Return file attributes. fs.stat("README.md") ``` - +### Reading + + -#### `samefile(path_a, path_b)` +#### `dir(path, opts?)` -Return whether two paths refer to the same file, or `nil` and an error on -failure. +Iterator over items in `path`. + +**Options**: + +- `recursive`: recurse into subdirectories; defaults to `false`. +- `hidden`: include hidden entries; defaults to `true`. +- `follow`: recurse into symlinked directories; defaults to `false`. +- `type`: filter by entry type, such as `"file"` or `"directory"`; defaults to + `nil`. **Parameters**: -- `path_a` (`string`): Input path. -- `path_b` (`string`): Input path. +- `path` (`string`): Input path. +- `opts?` + (`{hidden?:boolean, recursive?:boolean, follow?:boolean, type?:modsFsEntryType}`): + Optional traversal options. **Return**: -- `isSameFile` (`boolean?`): True when both paths refer to the same file. -- `errmsg` (`string?`): Error message when the check fails. -- `errcode` (`integer?`): OS error code when available. +- `iterator` + (`(fun(state:table, prev?:string):basename:string?, type:modsFsEntryType?)?`): + Iterator, or `nil` on failure. +- `state` (`table|string`): Iterator state on success, or error message on + failure. **Example**: ```lua -fs.samefile("README.md", "README.md") --> true +for name, type in fs.dir(path.cwd(), { recursive = true }) do + print(name, type) +end ``` -### Existence Checks + - +#### `listdir(path, opts?)` -#### `exists(path)` +Return direct children of a directory. -Return `true` when a path exists. +**Options**: + +- `recursive`: recurse into subdirectories; defaults to `false`. +- `hidden`: include hidden entries; defaults to `true`. +- `follow`: recurse into symlinked directories; defaults to `false`. +- `type`: filter by entry type, such as `"file"` or `"directory"`; defaults to + `nil`. +- `names`: return basenames; defaults to `false`. **Parameters**: - `path` (`string`): Input path. +- `opts?` + (`{hidden?:boolean, recursive?:boolean, follow?:boolean, type?:modsFsEntryType, names?:boolean}`): + Optional traversal options. **Return**: -- `exists` (`boolean`): True when the path exists. +- `paths` (`mods.List?`): Direct child paths, or basenames when + `opts.names` is `true`. +- `err` (`string?`): Error message when traversal setup fails. **Example**: ```lua -fs.exists("README.md") --> true +fs.listdir("src") +fs.listdir("src", { names = true }) ``` -> [!NOTE] -> -> Broken symlinks return `false`. - - + -#### `lexists(path)` +#### `read_bytes(path)` -Return `true` when a path exists without following symlinks. +Read full file in binary mode. **Parameters**: @@ -526,14 +620,34 @@ Return `true` when a path exists without following symlinks. **Return**: -- `exists` (`boolean`): True when the path or symlink entry exists. +- `body` (`string?`): File contents read in binary mode, or `nil` on failure. +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. **Example**: ```lua -fs.lexists("README.md") --> true +fs.read_bytes("README.md") ``` -> [!NOTE] -> -> Broken symlinks return `true`. + + +#### `read_text(path)` + +Read full file in text mode. + +**Parameters**: + +- `path` (`string`): Input path. + +**Return**: + +- `body` (`string?`): File contents read in text mode, or `nil` on failure. +- `errmsg` (`string?`): Error message when the check fails. +- `errcode` (`integer?`): OS error code when available. + +**Example**: + +```lua +fs.read_text("README.md") +``` diff --git a/docs/src/modules/glob.md b/docs/src/modules/glob.md new file mode 100644 index 0000000..045a163 --- /dev/null +++ b/docs/src/modules/glob.md @@ -0,0 +1,267 @@ +--- +description: "Glob-style matching and filesystem expansion helpers." +--- + +# `glob` + +Glob-style matching and filesystem expansion helpers. + +## Usage + +```lua +glob = require "mods.glob" + +print(glob.match("src/mods/fs.lua", "**/*.lua")) --> true +print(glob.match("DATA.TXT", "*.txt", true)) --> true +print(glob.filter({ "a.lua", "b.txt" }, "*.lua")[1]) --> "a.lua" +print(glob.glob("src", "*.lua")[1]) +``` + +## Supported wildcards + +- `*`: match zero or more characters within one path segment. + + ```lua + match("main.lua", "*.lua") + ``` + +- `?`: match exactly one character within one path segment. + + ```lua + match("a1.lua", "a?.lua") + ``` + +- `[]`: match one character from a bracket class like `[a-z]`. + + ```lua + match("file7.lua", "file[0-9].lua") + ``` + +- `[!]`: negate a bracket class, like `[!0-9]`. + + ```lua + match("filex.lua", "file[!0-9].lua") + ``` + +- `{a,b}`: match one of several brace alternatives. + + ```lua + match("init.lua", "init.{lua,luac}") + ``` + +- `**`: match across path segments recursively. + + ```lua + match("src/mods/fs.lua", "**/*.lua") + ``` + +## Functions + +**Glob Operations**: + +| Function | Description | +| --------------------------------------------------- | ----------------------------------------------------------- | +| [`escape(s)`](#fn-escape) | Escape glob metacharacters in a literal string. | +| [`filter(names, pattern, ignorecase?)`](#fn-filter) | Return the values from `names` that match the glob pattern. | +| [`glob(path, pattern?, opts?)`](#fn-glob) | Return glob matches under `path`. | +| [`has_magic(s)`](#fn-has-magic) | Return whether a pattern contains glob metacharacters. | +| [`iglob(path, pattern?, opts?)`](#fn-iglob) | Iterator over glob matches under `path`. | +| [`match(path, pattern, ignorecase?)`](#fn-match) | Match a path against a glob pattern. | +| [`translate(pattern)`](#fn-translate) | Translate one glob segment into an equivalent Lua pattern. | + +### Glob Operations + + + +#### `escape(s)` + +Escape glob metacharacters in a literal string. + +**Parameters**: + +- `s` (`string`): Input literal string. + +**Return**: + +- `pattern` (`string`): Escaped glob pattern. + +**Example**: + +```lua +glob.escape("a*b") --> "a\\*b" +``` + + + +#### `filter(names, pattern, ignorecase?)` + +Return the values from `names` that match the glob pattern. + +**Parameters**: + +- `names` (`string[]`): Input names. +- `pattern` (`string`): Input glob pattern. +- `ignorecase?` (`boolean`): Override platform-default case matching. + +**Return**: + +- `matches` (`mods.List`): Matching values from `names`. + +**Example**: + +```lua +glob.filter({ "a.lua", "b.txt", "c.lua" }, "*.lua") --> { "a.lua", "c.lua" } +``` + + + +#### `glob(path, pattern?, opts?)` + +Return glob matches under `path`. + +**Options**: + +- `hidden`: include hidden paths; defaults to `true`. +- `recursive`: recurse into subdirectories; defaults to `false`. +- `follow`: recurse into symlinked directories; defaults to `false`. +- `ignorecase`: use case-insensitive matching; defaults to platform semantics. + +**Parameters**: + +- `path` (`string`): Input path. +- `pattern?` (`string`): Optional pattern to match. +- `opts?` + (`{hidden?:boolean, recursive?:boolean, follow?:boolean, ignorecase?:boolean}`): + Optional glob options. + +**Return**: + +- `paths` (`mods.List`): Matching paths under `path`. + +**Example**: + +```lua +glob.glob("src", "*.lua") +glob.glob("src", "*.lua", { recursive = true }) +``` + + + +#### `has_magic(s)` + +Return whether a pattern contains glob metacharacters. + +**Parameters**: + +- `s` (`string`): Input string. + +**Return**: + +- `has_magic` (`boolean`): True when the string contains glob syntax. + +**Example**: + +```lua +glob.has_magic("foo.txt") --> false +glob.has_magic("*.txt") --> true +``` + + + +#### `iglob(path, pattern?, opts?)` + +Iterator over glob matches under `path`. + +**Options**: + +- `hidden`: include hidden paths; defaults to `true`. +- `recursive`: recurse into subdirectories; defaults to `false`. +- `follow`: recurse into symlinked directories; defaults to `false`. +- `ignorecase`: use case-insensitive matching; defaults to platform semantics. + +**Parameters**: + +- `path` (`string`): Input path. +- `pattern?` (`string`): Optional pattern to match. +- `opts?` + (`{hidden?:boolean, recursive?:boolean, follow?:boolean, ignorecase?:boolean}`): + Optional glob options. + +**Return**: + +- `iterator` (`(fun(state:table, prev?:string): (path:string?))`): Iterator + function. +- `state` (`table`): Iterator state table. +- `initial` (`nil`): Initial iterator value. + +**Example**: + +```lua +for path in glob.iglob("src", "*.lua") do + print(path) +end +``` + + + +#### `match(path, pattern, ignorecase?)` + +Match a path against a glob pattern. + +**Parameters**: + +- `path` (`string`): Input path. +- `pattern` (`string`): Input glob pattern. +- `ignorecase?` (`boolean`): Override platform-default case matching. + +**Return**: + +- `matches` (`boolean`): True when the path matches the pattern. + +**Example**: + +```lua +glob.match("src/mods/fs.lua", "**/*.lua") --> true +``` + + + +#### `translate(pattern)` + +Translate one glob segment into an equivalent Lua pattern. + +**Parameters**: + +- `pattern` (`string`): Input glob segment. + +**Return**: + +- `lua_pattern` (`string`): Lua pattern string. + +**Example**: + +```lua +local s = "init.lua" +local pattern = "*.lua" +local matches = glob.match(s, pattern) +local translated_matches = s:match(glob.translate(pattern)) ~= nil +print(matches == translated_matches) --> true +``` + +> [!NOTE] +> +> - `*` and `?` stay within a single path segment. +> +> ```lua +> local pattern = "*.txt" +> print(glob.translate(pattern)) --> "^[^/]*%.txt$" +> print(glob.match("foo/bar.txt", pattern)) --> false +> ``` +> +> - `**` and `{a,b}` need higher-level matching logic. +> +> ```lua +> pattern = "src/{x,y}.lua" +> print(("src/x.lua"):match(glob.translate(pattern))) --> nil +> print(glob.match("src/x.lua", pattern)) --> true +> ``` diff --git a/docs/src/modules/is.md b/docs/src/modules/is.md index 7c89073..39ab354 100644 --- a/docs/src/modules/is.md +++ b/docs/src/modules/is.md @@ -26,6 +26,12 @@ ok = is.table({}) --> true > is.tAbLe({}) --> true > ``` +> [!IMPORTANT] +> +> Path checks require **LuaFileSystem** +> ([`lfs`](https://github.com/lunarmodules/luafilesystem)) and raise an error if +> it is not installed. + ## `is()` `is` is also callable as `is(value, type)` to check if a value is of a given @@ -39,6 +45,20 @@ is("hello", "STRING") --> true ## Functions +**Path Checks**: + +| Function | Description | +| ------------------------- | ------------------------------------------------------------ | +| [`block(v)`](#fn-block) | Returns `true` when `v` is a block device path. | +| [`char(v)`](#fn-char) | Returns `true` when `v` is a character device path. | +| [`device(v)`](#fn-device) | Returns `true` when `v` is a block or character device path. | +| [`dir(v)`](#fn-dir) | Returns `true` when `v` is a directory path. | +| [`fifo(v)`](#fn-fifo) | Returns `true` when `v` is a FIFO path. | +| [`file(v)`](#fn-file) | Returns `true` when `v` is a file path. | +| [`link(v)`](#fn-link) | Returns `true` when `v` is a symlink path. | +| [`path(v)`](#fn-path) | Returns `true` when `v` is a valid filesystem path. | +| [`socket(v)`](#fn-socket) | Returns `true` when `v` is a socket path. | + **Type Checks**: | Function | Description | @@ -56,34 +76,20 @@ is("hello", "STRING") --> true | Function | Description | | ----------------------------- | ------------------------------------------- | +| [`callable(v)`](#fn-callable) | Returns `true` when `v` is callable. | | [`false(v)`](#fn-false) | Returns `true` when `v` is exactly `false`. | -| [`true(v)`](#fn-true) | Returns `true` when `v` is exactly `true`. | | [`falsy(v)`](#fn-falsy) | Returns `true` when `v` is falsy. | -| [`callable(v)`](#fn-callable) | Returns `true` when `v` is callable. | | [`integer(v)`](#fn-integer) | Returns `true` when `v` is an integer. | +| [`true(v)`](#fn-true) | Returns `true` when `v` is exactly `true`. | | [`truthy(v)`](#fn-truthy) | Returns `true` when `v` is truthy. | -**Path Checks**: - -| Function | Description | -| ------------------------- | ------------------------------------------------------------ | -| [`path(v)`](#fn-path) | Returns `true` when `v` is a valid filesystem path. | -| [`block(v)`](#fn-block) | Returns `true` when `v` is a block device path. | -| [`char(v)`](#fn-char) | Returns `true` when `v` is a character device path. | -| [`device(v)`](#fn-device) | Returns `true` when `v` is a block or character device path. | -| [`dir(v)`](#fn-dir) | Returns `true` when `v` is a directory path. | -| [`fifo(v)`](#fn-fifo) | Returns `true` when `v` is a FIFO path. | -| [`file(v)`](#fn-file) | Returns `true` when `v` is a file path. | -| [`link(v)`](#fn-link) | Returns `true` when `v` is a symlink path. | -| [`socket(v)`](#fn-socket) | Returns `true` when `v` is a socket path. | - -### Type Checks +### Path Checks -Core Lua type checks (`type(v)` family). + -#### `boolean(v)` +#### `block(v)` -Returns `true` when `v` is a boolean. +Returns `true` when `v` is a block device path. **Parameters**: @@ -91,19 +97,19 @@ Returns `true` when `v` is a boolean. **Return**: -- `isBoolean` (`boolean`): Whether the check succeeds. +- `isBlock` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.boolean(true) +is.block("/dev/sda") ``` - + -#### `function(v)` +#### `char(v)` -Returns `true` when `v` is a function. +Returns `true` when `v` is a character device path. **Parameters**: @@ -111,19 +117,19 @@ Returns `true` when `v` is a function. **Return**: -- `isFunction` (`boolean`): Whether the check succeeds. +- `isChar` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.Function(function() end) +is.char("/dev/null") ``` - + -#### `nil(v)` +#### `device(v)` -Returns `true` when `v` is `nil`. +Returns `true` when `v` is a block or character device path. **Parameters**: @@ -131,19 +137,19 @@ Returns `true` when `v` is `nil`. **Return**: -- `isNil` (`boolean`): Whether the check succeeds. +- `isDevice` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.Nil(nil) +is.device("/dev/null") ``` - + -#### `number(v)` +#### `dir(v)` -Returns `true` when `v` is a number. +Returns `true` when `v` is a directory path. **Parameters**: @@ -151,19 +157,19 @@ Returns `true` when `v` is a number. **Return**: -- `isNumber` (`boolean`): Whether the check succeeds. +- `isDir` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.number(3.14) +is.dir("/tmp") ``` - + -#### `string(v)` +#### `fifo(v)` -Returns `true` when `v` is a string. +Returns `true` when `v` is a FIFO path. **Parameters**: @@ -171,19 +177,19 @@ Returns `true` when `v` is a string. **Return**: -- `isString` (`boolean`): Whether the check succeeds. +- `isFifo` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.string("hello") +is.fifo("/path/to/fifo") ``` - + -#### `table(v)` +#### `file(v)` -Returns `true` when `v` is a table. +Returns `true` when `v` is a file path. **Parameters**: @@ -191,19 +197,19 @@ Returns `true` when `v` is a table. **Return**: -- `isTable` (`boolean`): Whether the check succeeds. +- `isFile` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.table({}) +is.file("README.md") ``` - + -#### `thread(v)` +#### `link(v)` -Returns `true` when `v` is a thread. +Returns `true` when `v` is a symlink path. **Parameters**: @@ -211,19 +217,19 @@ Returns `true` when `v` is a thread. **Return**: -- `isThread` (`boolean`): Whether the check succeeds. +- `isLink` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.thread(coroutine.create(function() end)) +is.link("/path/to/link") ``` - + -#### `userdata(v)` +#### `path(v)` -Returns `true` when `v` is userdata. +Returns `true` when `v` is a valid filesystem path. **Parameters**: @@ -231,21 +237,23 @@ Returns `true` when `v` is userdata. **Return**: -- `isUserdata` (`boolean`): Whether the check succeeds. +- `isPath` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.userdata(io.stdout) +is.path("README.md") ``` -### Value Checks +> [!NOTE] +> +> Returns `true` for broken symlinks. -Truthiness, exact-value, and callable checks. + -#### `false(v)` +#### `socket(v)` -Returns `true` when `v` is exactly `false`. +Returns `true` when `v` is a socket path. **Parameters**: @@ -253,19 +261,21 @@ Returns `true` when `v` is exactly `false`. **Return**: -- `isFalse` (`boolean`): Whether the check succeeds. +- `isSocket` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.False(false) +is.socket("/path/to/socket") ``` - +### Type Checks -#### `true(v)` + -Returns `true` when `v` is exactly `true`. +#### `boolean(v)` + +Returns `true` when `v` is a boolean. **Parameters**: @@ -273,19 +283,19 @@ Returns `true` when `v` is exactly `true`. **Return**: -- `isTrue` (`boolean`): Whether the check succeeds. +- `isBoolean` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.True(true) +is.boolean(true) ``` - + -#### `falsy(v)` +#### `function(v)` -Returns `true` when `v` is falsy. +Returns `true` when `v` is a function. **Parameters**: @@ -293,19 +303,19 @@ Returns `true` when `v` is falsy. **Return**: -- `isFalsy` (`boolean`): Whether the check succeeds. +- `isFunction` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.falsy(false) +is.Function(function() end) ``` - + -#### `callable(v)` +#### `nil(v)` -Returns `true` when `v` is callable. +Returns `true` when `v` is `nil`. **Parameters**: @@ -313,19 +323,19 @@ Returns `true` when `v` is callable. **Return**: -- `isCallable` (`boolean`): Whether the check succeeds. +- `isNil` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.callable(function() end) +is.Nil(nil) ``` - + -#### `integer(v)` +#### `number(v)` -Returns `true` when `v` is an integer. +Returns `true` when `v` is a number. **Parameters**: @@ -333,19 +343,19 @@ Returns `true` when `v` is an integer. **Return**: -- `isInteger` (`boolean`): Whether the check succeeds. +- `isNumber` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.integer(42) +is.number(3.14) ``` - + -#### `truthy(v)` +#### `string(v)` -Returns `true` when `v` is truthy. +Returns `true` when `v` is a string. **Parameters**: @@ -353,27 +363,19 @@ Returns `true` when `v` is truthy. **Return**: -- `isTruthy` (`boolean`): Whether the check succeeds. +- `isString` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.truthy("non-empty") +is.string("hello") ``` -### Path Checks - -Filesystem path type checks. - -> [!IMPORTANT] -> -> Path checks require **LuaFileSystem** -> ([`lfs`](https://github.com/lunarmodules/luafilesystem)) and raise an error if -> it is not installed. + -#### `path(v)` +#### `table(v)` -Returns `true` when `v` is a valid filesystem path. +Returns `true` when `v` is a table. **Parameters**: @@ -381,21 +383,19 @@ Returns `true` when `v` is a valid filesystem path. **Return**: -- `isPath` (`boolean`): Whether the check succeeds. +- `isTable` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.path("README.md") +is.table({}) ``` -> [!NOTE] Returns `true` for broken symlinks. - - + -#### `block(v)` +#### `thread(v)` -Returns `true` when `v` is a block device path. +Returns `true` when `v` is a thread. **Parameters**: @@ -403,19 +403,19 @@ Returns `true` when `v` is a block device path. **Return**: -- `isBlock` (`boolean`): Whether the check succeeds. +- `isThread` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.block("/dev/sda") +is.thread(coroutine.create(function() end)) ``` - + -#### `char(v)` +#### `userdata(v)` -Returns `true` when `v` is a character device path. +Returns `true` when `v` is userdata. **Parameters**: @@ -423,19 +423,21 @@ Returns `true` when `v` is a character device path. **Return**: -- `isChar` (`boolean`): Whether the check succeeds. +- `isUserdata` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.char("/dev/null") +is.userdata(io.stdout) ``` - +### Value Checks -#### `device(v)` + -Returns `true` when `v` is a block or character device path. +#### `callable(v)` + +Returns `true` when `v` is callable. **Parameters**: @@ -443,19 +445,19 @@ Returns `true` when `v` is a block or character device path. **Return**: -- `isDevice` (`boolean`): Whether the check succeeds. +- `isCallable` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.device("/dev/null") +is.callable(function() end) ``` - + -#### `dir(v)` +#### `false(v)` -Returns `true` when `v` is a directory path. +Returns `true` when `v` is exactly `false`. **Parameters**: @@ -463,19 +465,19 @@ Returns `true` when `v` is a directory path. **Return**: -- `isDir` (`boolean`): Whether the check succeeds. +- `isFalse` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.dir("/tmp") +is.False(false) ``` - + -#### `fifo(v)` +#### `falsy(v)` -Returns `true` when `v` is a FIFO path. +Returns `true` when `v` is falsy. **Parameters**: @@ -483,19 +485,19 @@ Returns `true` when `v` is a FIFO path. **Return**: -- `isFifo` (`boolean`): Whether the check succeeds. +- `isFalsy` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.fifo("/path/to/fifo") +is.falsy(false) ``` - + -#### `file(v)` +#### `integer(v)` -Returns `true` when `v` is a file path. +Returns `true` when `v` is an integer. **Parameters**: @@ -503,19 +505,19 @@ Returns `true` when `v` is a file path. **Return**: -- `isFile` (`boolean`): Whether the check succeeds. +- `isInteger` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.file("README.md") +is.integer(42) ``` - + -#### `link(v)` +#### `true(v)` -Returns `true` when `v` is a symlink path. +Returns `true` when `v` is exactly `true`. **Parameters**: @@ -523,19 +525,19 @@ Returns `true` when `v` is a symlink path. **Return**: -- `isLink` (`boolean`): Whether the check succeeds. +- `isTrue` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.link("/path/to/link") +is.True(true) ``` - + -#### `socket(v)` +#### `truthy(v)` -Returns `true` when `v` is a socket path. +Returns `true` when `v` is truthy. **Parameters**: @@ -543,10 +545,10 @@ Returns `true` when `v` is a socket path. **Return**: -- `isSocket` (`boolean`): Whether the check succeeds. +- `isTruthy` (`boolean`): Whether the check succeeds. **Example**: ```lua -is.socket("/path/to/socket") +is.truthy("non-empty") ``` diff --git a/docs/src/modules/json.md b/docs/src/modules/json.md new file mode 100644 index 0000000..f123b42 --- /dev/null +++ b/docs/src/modules/json.md @@ -0,0 +1,165 @@ +--- +description: "JSON encoding and decoding helpers." +--- + +# `json` + +JSON encoding and decoding helpers. + +> [!NOTE] +> +> This module aims to implement strict +> [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259) JSON behavior. + +## Usage + +```lua +local json = require "mods.json" + +local encoded = json.encode({ ok = true, value = 42 }) +local decoded = json.decode(encoded) + +print(encoded) --> {"ok":true,"value":42} +print(decoded.ok) --> true +print(decoded.value) --> 42 +``` + +## Behavior + +- booleans, strings, and finite numbers map directly to JSON values + +```lua +print(json.encode(true)) --> true +print(json.encode("hi")) --> "hi" +print(json.encode(12.5)) --> 12.5 +``` + +- `json.null` encodes to `null`, and decoded `null` becomes `json.null` + +```lua +local value = json.decode('{"a":null}') +print(value.a == json.null) --> true +print(json.encode(json.null)) --> null +``` + +- tables encode as arrays or objects based on their keys + +```lua +print(json.encode({})) --> [] +print(json.encode({ "a", "b" })) --> ["a","b"] +print(json.encode({ a = 1 })) --> {"a":1} +``` + +- standalone JSON values like booleans, strings, and numbers are valid + +```lua +print(json.decode("true")) --> true +print(json.decode('"text"')) --> text +print(json.decode("42")) --> 42 +``` + +- encoding rejects unsupported Lua types, cyclic tables, mixed tables, sparse + arrays, `NaN`, and infinities + +```lua +local t = {} +t.self = t + +assert(json.encode(function() end)) +--> unsupported type: function + +assert(json.encode(t)) +--> cannot encode cyclic table + +assert(json.encode({ [1] = "a", b = true })) +--> cannot encode mixed table + +assert(json.encode({ [1] = "a", [3] = "c" })) +--> cannot encode sparse array + +assert(json.encode(0 / 0)) +--> cannot encode NaN + +assert(json.encode(math.huge)) +--> cannot encode infinity +``` + +- decoding rejects comments, trailing commas, single-quoted strings, and invalid + escapes + +```lua +assert(json.decode('{"a":1,}')) +--> expected string key at line 1, column 8 + +assert(json.decode('{"a":1}// comment')) +--> unexpected trailing content at line 1, column 8 + +assert(json.decode("{'a':1}")) +--> expected string key at line 1, column 2 + +assert(json.decode('["\\x"]')) +--> invalid escape sequence at line 1, column 3 +``` + +## Functions + + + +### `decode(s)` + +Decode a JSON string into Lua values. + +**Parameters**: + +- `s` (`string`): JSON string. + +**Return**: + +- `value` (`any`): Decoded Lua value. +- `err` (`string?`): Error message when decoding fails. + +**Example**: + +```lua +local value = json.decode('{"user":"Ada","active":true,"note":null}') +print(value.user) --> Ada +print(value.active) --> true +print(value.note == json.null) --> true +``` + + + +### `encode(value, opts?)` + +Encode a Lua value as JSON. + +**Parameters**: + +- `value` (`any`): Lua value to encode. +- `opts?` (`{sort_keys?:boolean, indent?:string}`): Encoding options. + +**Return**: + +- `json` (`string?`): JSON string. +- `err` (`string?`): Error message when encoding fails. + +**Example**: + +```lua +local s = json.encode({ + ok = true, + items = { 1, 2, 3 }, +}, { + indent = " ", +}) + +print(s) +-- { +-- "ok": true, +-- "items": [ +-- 1, +-- 2, +-- 3 +-- ] +-- } +``` diff --git a/docs/src/modules/keyword.md b/docs/src/modules/keyword.md index 97a0aaf..8e0cb94 100644 --- a/docs/src/modules/keyword.md +++ b/docs/src/modules/keyword.md @@ -17,104 +17,124 @@ kw.isidentifier("hello_world") --> true ## Functions -| Function | Description | -| ----------------------------------------------------- | ------------------------------------------------------------- | -| [`iskeyword(v)`](#fn-iskeyword) | Return `true` when `v` is a reserved Lua keyword. | -| [`isidentifier(v)`](#fn-isidentifier) | Return `true` when `v` is a valid non-keyword Lua identifier. | -| [`kwlist()`](#fn-kwlist) | Return Lua keywords as a [`mods.List`](/modules/list). | -| [`kwset()`](#fn-kwset) | Return Lua keywords as a [`mods.Set`](/modules/set). | -| [`normalize_identifier(s)`](#fn-normalize-identifier) | Normalize an input into a safe Lua identifier. | +**Collections**: - +| Function | Description | +| ------------------------ | ------------------------------------------------------ | +| [`kwlist()`](#fn-kwlist) | Return Lua keywords as a [`mods.List`](/modules/list). | +| [`kwset()`](#fn-kwset) | Return Lua keywords as a [`mods.Set`](/modules/set). | -### `iskeyword(v)` +**Normalization**: -Return `true` when `v` is a reserved Lua keyword. +| Function | Description | +| ----------------------------------------------------- | ---------------------------------------------- | +| [`normalize_identifier(s)`](#fn-normalize-identifier) | Normalize an input into a safe Lua identifier. | -**Parameters**: +**Predicates**: -- `v` (`any`): Value to validate. +| Function | Description | +| ------------------------------------- | ------------------------------------------------------------- | +| [`isidentifier(v)`](#fn-isidentifier) | Return `true` when `v` is a valid non-keyword Lua identifier. | +| [`iskeyword(v)`](#fn-iskeyword) | Return `true` when `v` is a reserved Lua keyword. | + +### Collections + + + +#### `kwlist()` + +Return Lua keywords as a [`mods.List`](/modules/list). **Return**: -- `isKeyword` (`boolean`): Whether the check succeeds. +- `words` (`mods.List`): List of Lua keywords. **Example**: ```lua -kw.iskeyword("function") --> true -kw.iskeyword("hello") --> false +kw.kwlist():contains("and") --> true +kw.kwlist():contains("global") --> true -- Lua 5.5+ ``` - - -### `isidentifier(v)` - -Return `true` when `v` is a valid non-keyword Lua identifier. + -**Parameters**: +#### `kwset()` -- `v` (`any`): Value to validate. +Return Lua keywords as a [`mods.Set`](/modules/set). **Return**: -- `isIdentifier` (`boolean`): Whether the check succeeds. +- `words` (`mods.Set`): Set of Lua keywords. **Example**: ```lua -kw.isidentifier("hello_world") --> true -kw.isidentifier("local") --> false +kw.kwlset():contains("and") --> true +kw.kwlset():contains("global") --> true -- Lua 5.5+ ``` - +### Normalization -### `kwlist()` + -Return Lua keywords as a [`mods.List`](/modules/list). +#### `normalize_identifier(s)` + +Normalize an input into a safe Lua identifier. + +**Parameters**: + +- `s` (`string`): Input string. **Return**: -- `words` (`mods.List`): List of Lua keywords. +- `identifier` (`string`): Normalized Lua identifier. **Example**: ```lua -kw.kwlist():contains("and") --> true +kw.normalize_identifier(" 2 bad-name ") --> "_2_bad_name" ``` - +### Predicates -### `kwset()` + -Return Lua keywords as a [`mods.Set`](/modules/set). +#### `isidentifier(v)` + +Return `true` when `v` is a valid non-keyword Lua identifier. + +**Parameters**: + +- `v` (`any`): Value to validate. **Return**: -- `words` (`mods.Set`): Set of Lua keywords. +- `isIdentifier` (`boolean`): Whether the check succeeds. **Example**: ```lua -kw.kwlset():contains("and") --> true +kw.isidentifier("hello_world") --> true +kw.isidentifier("local") --> false ``` - + -### `normalize_identifier(s)` +#### `iskeyword(v)` -Normalize an input into a safe Lua identifier. +Return `true` when `v` is a reserved Lua keyword. **Parameters**: -- `s` (`string`): Input string. +- `v` (`any`): Value to validate. **Return**: -- `identifier` (`string`): Normalized Lua identifier. +- `isKeyword` (`boolean`): Whether the check succeeds. **Example**: ```lua -kw.normalize_identifier(" 2 bad-name ") --> "_2_bad_name" +kw.iskeyword("function") --> true +kw.iskeyword("hello") --> false ``` diff --git a/docs/src/modules/list.md b/docs/src/modules/list.md index 3b30c4a..7a6bba1 100644 --- a/docs/src/modules/list.md +++ b/docs/src/modules/list.md @@ -25,15 +25,18 @@ print(ls:index("b")) --> 2 ## Functions -**Predicates**: +**Access**: -| Function | Description | -| -------------------------- | ------------------------------------------------- | -| [`all(pred)`](#fn-all) | Return `true` if all values match the predicate. | -| [`any(pred)`](#fn-any) | Return `true` if any value matches the predicate. | -| [`equals(ls)`](#fn-equals) | Compare two lists using shallow element equality. | -| [`lt(ls)`](#fn-lt) | Compare two lists lexicographically. | -| [`le(ls)`](#fn-le) | Compare two lists lexicographically. | +| Function | Description | +| ---------------------- | ------------------------------------------- | +| [`first()`](#fn-first) | Return the first element or `nil` if empty. | +| [`last()`](#fn-last) | Return the last element or `nil` if empty. | + +**Copies**: + +| Function | Description | +| -------------------- | ---------------------------------- | +| [`copy()`](#fn-copy) | Return a shallow copy of the list. | **Mutation**: @@ -49,15 +52,20 @@ print(ls:index("b")) --> 2 | [`pop(pos)`](#fn-pop) | Remove and return the element at the given position. | | [`prepend(v)`](#fn-prepend) | Insert a value at the start of the list. | | [`remove(v)`](#fn-remove) | Remove the first matching value. | +| [`shuffle(rng?)`](#fn-shuffle) | Shuffle the list in place. | | [`sort(comp?)`](#fn-sort) | Sort the list in place. | -**Copying**: +**Predicates**: -| Function | Description | -| -------------------- | ---------------------------------- | -| [`copy()`](#fn-copy) | Return a shallow copy of the list. | +| Function | Description | +| -------------------------- | ------------------------------------------------- | +| [`all(pred)`](#fn-all) | Return `true` if all values match the predicate. | +| [`any(pred)`](#fn-any) | Return `true` if any value matches the predicate. | +| [`equals(ls)`](#fn-equals) | Compare two lists using shallow element equality. | +| [`le(ls)`](#fn-le) | Compare two lists lexicographically. | +| [`lt(ls)`](#fn-lt) | Compare two lists lexicographically. | -**Query**: +**Queries**: | Function | Description | | -------------------------------- | ----------------------------------------------------------- | @@ -65,20 +73,14 @@ print(ls:index("b")) --> 2 | [`count(v)`](#fn-count) | Count how many times a value appears. | | [`index(v)`](#fn-index) | Return the index of the first matching value. | | [`index_if(pred)`](#fn-index-if) | Return the index of the first value matching the predicate. | -| [`len()`](#fn-len) | Return the number of elements in the list. | | [`isempty()`](#fn-isempty) | Return whether the list has no elements. | +| [`len()`](#fn-len) | Return the number of elements in the list. | -**Access**: - -| Function | Description | -| ---------------------- | ------------------------------------------- | -| [`first()`](#fn-first) | Return the first element or `nil` if empty. | -| [`last()`](#fn-last) | Return the last element or `nil` if empty. | - -**Transform**: +**Transforms**: | Function | Description | | ------------------------------------- | -------------------------------------------------------------------- | +| [`concat(sep?, i?, j?)`](#fn-concat) | Concatenate list values using Lua's native `table.concat` behavior. | | [`difference(t)`](#fn-difference) | Return a new list with values not in the given list or set. | | [`drop(n)`](#fn-drop) | Return a new list without the first n elements. | | [`filter(pred)`](#fn-filter) | Return a new list with values matching the predicate. | @@ -87,17 +89,17 @@ print(ls:index("b")) --> 2 | [`group_by(fn)`](#fn-group-by) | Group list values by a computed key. | | [`intersection(t)`](#fn-intersection) | Return values that are also present in the given list or set. | | [`invert()`](#fn-invert) | Invert values to indices in a new table. | -| [`concat(sep?, i?, j?)`](#fn-concat) | Concatenate list values using Lua's native `table.concat` behavior. | | [`join(sep?, quoted?)`](#fn-join) | Join list values into a string. | -| [`tostring()`](#fn-tostring) | Render the list to a string via the regular method form. | | [`keypath()`](#fn-keypath) | Render list items as a table-access key path. | | [`map(fn)`](#fn-map) | Return a new list by mapping each value. | +| [`mirror()`](#fn-mirror) | Mirror values into a new table as both keys and values. | | [`mul(n)`](#fn-mul) | Return a new list repeated `n` times (list multiplication behavior). | | [`reduce(fn, init?)`](#fn-reduce) | Reduce the list to a single value using an accumulator. | | [`reverse()`](#fn-reverse) | Reverse the list in place. | -| [`toset()`](#fn-toset) | Convert the list to a set. | | [`slice(i?, j?)`](#fn-slice) | Return a new list containing items from i to j (inclusive). | | [`take(n)`](#fn-take) | Return the first n elements as a new list. | +| [`toset()`](#fn-toset) | Convert the list to a set. | +| [`tostring()`](#fn-tostring) | Render the list to a string via the regular method form. | | [`uniq()`](#fn-uniq) | Return a new list with duplicates removed (first occurrence kept). | | [`zip(t)`](#fn-zip) | Zip two collections into a list of 2-element tables. | @@ -105,166 +107,69 @@ print(ls:index("b")) --> 2 | Function | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------- | +| [`__add(ls)`](#fn-add) | Extend the left-hand list in place with right-hand values, then return the same left-hand list reference (`+`). | | [`__eq(ls)`](#fn-eq) | Compare two lists using shallow element equality (`==`). | -| [`__lt(ls)`](#fn-lt) | Compare two lists lexicographically (`<`). | | [`__le(ls)`](#fn-le) | Compare two lists lexicographically (`<=`). | +| [`__lt(ls)`](#fn-lt) | Compare two lists lexicographically (`<`). | | [`__mul(n)`](#fn-mul) | Repeat a list `n` times (`*`). | -| [`__add(ls)`](#fn-add) | Extend the left-hand list in place with right-hand values, then return the same left-hand list reference (`+`). | | [`__sub(ls)`](#fn-sub) | Return values from the left list that are not present in the right list (`-`). | | [`__tostring()`](#fn-tostring) | Render the list to a string like `{ "a", "b", 1 }`. | -### Predicates - -Boolean checks for list-wide conditions. - -#### `all(pred)` - -Return `true` if all values match the predicate. - -**Parameters**: - -- `pred` (`fun(v:any):boolean`): Predicate function. - -**Return**: - -- `allMatch` (`boolean`): Whether the condition is met. - -**Example**: - -```lua -is_even = function(v) return v % 2 == 0 end -ok = List({ 2, 4 }):all(is_even) --> true -``` - -> [!NOTE] -> -> Empty lists return `true`. - - - -#### `any(pred)` - -Return `true` if any value matches the predicate. - -**Parameters**: - -- `pred` (`fun(v:any):boolean`): Predicate function. - -**Return**: - -- `anyMatch` (`boolean`): Whether the condition is met. - -**Example**: - -```lua -has_len_2 = function(v) return #v == 2 end -ok = List({ "a", "bb" }):any(has_len_2) --> true -``` - - - -#### `equals(ls)` +### Access -Compare two lists using shallow element equality. + -**Parameters**: +#### `first()` -- `ls` (`mods.List|any[]`): Other list value. +Return the first element or `nil` if empty. **Return**: -- `isEqual` (`boolean`): Whether the condition is met. +- `firstValue` (`any`): First value, or `nil` if empty. **Example**: ```lua -a = List({ "x", "y" }) -b = List({ "x", "y" }) -ok = a:equals(b) --> true +v = List({ "a", "b" }):first() --> "a" ``` -> [!NOTE] -> -> - `equals` is also available through the `==` operator when both operands are -> `List`. -> -> ```lua -> a = List({ "a", 1 }) -> b = List({ "a", 1 }) -> ok = (a == b) --> true -> ``` -> -> - Unlike `==`, this method also works when `ls` is a plain array table. -> -> ```lua -> a = List({ "a", 1 }) -> b = { "a", 1 } -> ok = a:equals(b) --> true -> ``` -> -> - `equals` checks only array positions (`1..#list`), so extra non-array keys -> are ignored: -> -> ```lua -> t = {} -> a = List({ "a", t }) -> b = { "a", t, a = 1 } -> ok = a:equals(b) --> true -> ``` - - - -#### `lt(ls)` - -Compare two lists lexicographically. + -**Parameters**: +#### `last()` -- `ls` (`mods.List|any[]`): Other list value. +Return the last element or `nil` if empty. **Return**: -- `isLess` (`boolean`): Whether the condition is met. +- `lastValue` (`any`): Last value, or `nil` if empty. **Example**: ```lua -ok = List({ 1, 2 }):lt({ 1, 3 }) --> true -ok = List({ 1, 2 }):lt({ 1, 2, 0 }) --> true +v = List({ "a", "b" }):last() --> "b" ``` -> [!NOTE] -> -> `lt` is also available through the `<` operator. - - - -#### `le(ls)` +### Copies -Compare two lists lexicographically. + -**Parameters**: +#### `copy()` -- `ls` (`mods.List|any[]`): Other list value. +Return a shallow copy of the list. **Return**: -- `isLessOrEqual` (`boolean`): Whether the condition is met. +- `ls` (`mods.List`): New list. **Example**: ```lua -ok = List({ 1, 2 }):le({ 1, 2 }) --> true -ok = List({ 1, 2 }):le({ 1, 1 }) --> false +c = List({ "a", "b" }):copy() --> { "a", "b" } ``` -> [!NOTE] -> -> `le` is also available through the `<=` operator. - ### Mutation -In-place operations that modify the current list. + #### `append()` @@ -464,6 +369,27 @@ ls = List({ "a", "b", "b" }) ls:remove("b") --> { "a", "b" } ``` + + +#### `shuffle(rng?)` + +Shuffle the list in place. + +**Parameters**: + +- `rng?` (`fun(lo:integer, hi:integer):integer`): Optional random index picker; + defaults to `math.random`. + +**Return**: + +- `self` (`T`): Current list. + +**Example**: + +```lua +ls = List({ "a", "b", "c" }):shuffle() --> { "b", "c", "a" } -- order varies +``` + #### `sort(comp?)` @@ -491,27 +417,158 @@ words:sort(function(a, b) end) --> { "a", "bb", "ccc" } ``` -### Copying +### Predicates -Operations that return copied list data. + -#### `copy()` +#### `all(pred)` + +Return `true` if all values match the predicate. + +**Parameters**: + +- `pred` (`fun(v:any):boolean`): Predicate function. + +**Return**: + +- `allMatch` (`boolean`): Whether the condition is met. + +**Example**: + +```lua +is_even = function(v) return v % 2 == 0 end +ok = List({ 2, 4 }):all(is_even) --> true +``` + +> [!NOTE] +> +> Empty lists return `true`. + + + +#### `any(pred)` + +Return `true` if any value matches the predicate. + +**Parameters**: + +- `pred` (`fun(v:any):boolean`): Predicate function. + +**Return**: + +- `anyMatch` (`boolean`): Whether the condition is met. + +**Example**: + +```lua +has_len_2 = function(v) return #v == 2 end +ok = List({ "a", "bb" }):any(has_len_2) --> true +``` + + + +#### `equals(ls)` + +Compare two lists using shallow element equality. + +**Parameters**: + +- `ls` (`mods.List|any[]`): Other list value. + +**Return**: + +- `isEqual` (`boolean`): Whether the condition is met. + +**Example**: + +```lua +a = List({ "x", "y" }) +b = List({ "x", "y" }) +ok = a:equals(b) --> true +``` + +> [!NOTE] +> +> - `equals` is also available through the `==` operator when both operands are +> `List`. +> +> ```lua +> a = List({ "a", 1 }) +> b = List({ "a", 1 }) +> ok = (a == b) --> true +> ``` +> +> - Unlike `==`, this method also works when `ls` is a plain array table. +> +> ```lua +> a = List({ "a", 1 }) +> b = { "a", 1 } +> ok = a:equals(b) --> true +> ``` +> +> - `equals` checks only array positions (`1..#list`), so extra non-array keys +> are ignored: +> +> ```lua +> t = {} +> a = List({ "a", t }) +> b = { "a", t, a = 1 } +> ok = a:equals(b) --> true +> ``` + + + +#### `le(ls)` + +Compare two lists lexicographically. + +**Parameters**: + +- `ls` (`mods.List|any[]`): Other list value. + +**Return**: + +- `isLessOrEqual` (`boolean`): Whether the condition is met. + +**Example**: + +```lua +ok = List({ 1, 2 }):le({ 1, 2 }) --> true +ok = List({ 1, 2 }):le({ 1, 1 }) --> false +``` + +> [!NOTE] +> +> `le` is also available through the `<=` operator. + + + +#### `lt(ls)` + +Compare two lists lexicographically. + +**Parameters**: -Return a shallow copy of the list. +- `ls` (`mods.List|any[]`): Other list value. **Return**: -- `ls` (`mods.List`): New list. +- `isLess` (`boolean`): Whether the condition is met. **Example**: ```lua -c = List({ "a", "b" }):copy() --> { "a", "b" } +ok = List({ 1, 2 }):lt({ 1, 3 }) --> true +ok = List({ 1, 2 }):lt({ 1, 2, 0 }) --> true ``` -### Query +> [!NOTE] +> +> `lt` is also available through the `<` operator. + +### Queries -Read-only queries for membership, counts, and indices. + #### `contains(v)` @@ -592,26 +649,6 @@ 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. - -**Return**: - -- `count` (`integer`): Element count. - -**Example**: - -```lua -n = List({ "a", "b", "c" }):len() --> 3 -``` - -> [!NOTE] -> -> Uses Lua's `#` operator. - #### `isempty()` @@ -628,43 +665,55 @@ Return whether the list has no elements. ok = List():isempty() --> true ``` -### Access - -Direct element access helpers. + -#### `first()` +#### `len()` -Return the first element or `nil` if empty. +Return the number of elements in the list. **Return**: -- `firstValue` (`any`): First value, or `nil` if empty. +- `count` (`integer`): Element count. **Example**: ```lua -v = List({ "a", "b" }):first() --> "a" +n = List({ "a", "b", "c" }):len() --> 3 ``` - +> [!NOTE] +> +> Uses Lua's `#` operator. -#### `last()` +### Transforms -Return the last element or `nil` if empty. + + +#### `concat(sep?, i?, j?)` + +Concatenate list values using Lua's native `table.concat` behavior. + +**Parameters**: + +- `sep?` (`string`): Optional separator value (defaults to `""`). +- `i?` (`integer`): Optional start index (defaults to `1`). +- `j?` (`integer`): Optional end index (defaults to `#self`). **Return**: -- `lastValue` (`any`): Last value, or `nil` if empty. +- `concatenated` (`string`): Concatenated string. **Example**: ```lua -v = List({ "a", "b" }):last() --> "b" +s = List({ "a", "b", "c" }):concat(",") --> "a,b,c" ``` -### Transform +> [!NOTE] +> +> This method forwards to `table.concat` directly and keeps its strict element +> rules. -Non-mutating transformations and derived-list operations. #### `difference(t)` @@ -830,33 +879,6 @@ Invert values to indices in a new table. t = List({ "a", "b", "c" }):invert() --> { a = 1, b = 2, c = 3 } ``` - - -#### `concat(sep?, i?, j?)` - -Concatenate list values using Lua's native `table.concat` behavior. - -**Parameters**: - -- `sep?` (`string`): Optional separator value (defaults to `""`). -- `i?` (`integer`): Optional start index (defaults to `1`). -- `j?` (`integer`): Optional end index (defaults to `#self`). - -**Return**: - -- `concatenated` (`string`): Concatenated string. - -**Example**: - -```lua -s = List({ "a", "b", "c" }):concat(",") --> "a,b,c" -``` - -> [!NOTE] -> -> This method forwards to `table.concat` directly and keeps its strict element -> rules. - #### `join(sep?, quoted?)` @@ -884,22 +906,6 @@ s = List({ "a", "b", "c" }):join(", ", true) --> '"a", "b", "c"' > Values are converted with `tostring` before joining. Set `quoted = true` to > quote string values. - - -#### `tostring()` - -Render the list to a string via the regular method form. - -**Return**: - -- `renderedList` (`string`): Rendered list string. - -**Example**: - -```lua -s = List({ "a", "b", 1 }):tostring() --> '{ "a", "b", 1 }' -``` - #### `keypath()` @@ -937,6 +943,22 @@ to_upper = function(v) return v:upper() end m = List({ "a", "b" }):map(to_upper) --> { "A", "B" } ``` + + +#### `mirror()` + +Mirror values into a new table as both keys and values. + +**Return**: + +- `mirroredValues` (`table`): Table mapping each value to itself. + +**Example**: + +```lua +t = List({ "a", "b", "c" }):mirror() --> { a = "a", b = "b", c = "c" } +``` + #### `mul(n)` @@ -1005,26 +1027,6 @@ Reverse the list in place. r = List({ "a", "b", "c" }):reverse() --> { "c", "b", "a" } ``` - - -#### `toset()` - -Convert the list to a set. - -**Return**: - -- `set` (`mods.Set`): New set. - -**Example**: - -```lua -s = List({ "a", "b", "a" }):toset() --> { a = true, b = true } -``` - -> [!NOTE] -> -> Order is preserved from the original list. - #### `slice(i?, j?)` @@ -1070,6 +1072,42 @@ Return the first n elements as a new list. t = List({ "a", "b", "c" }):take(2) --> { "a", "b" } ``` + + +#### `toset()` + +Convert the list to a set. + +**Return**: + +- `set` (`mods.Set`): New set. + +**Example**: + +```lua +s = List({ "a", "b", "a" }):toset() --> { a = true, b = true } +``` + +> [!NOTE] +> +> Order is preserved from the original list. + + + +#### `tostring()` + +Render the list to a string via the regular method form. + +**Return**: + +- `renderedList` (`string`): Rendered list string. + +**Example**: + +```lua +s = List({ "a", "b", 1 }):tostring() --> '{ "a", "b", 1 }' +``` + #### `uniq()` @@ -1113,6 +1151,33 @@ z = List({ "a", "b" }):zip(Set({ 1, 2 })) --> { {"a",1}, {"b",2} } ### Metamethods + + +#### `__add(ls)` + +Extend the left-hand list in place with right-hand values, then return the same +left-hand list reference (`+`). + +**Parameters**: + +- `ls` (`mods.List|any[]`): Other list value. + +**Return**: + +- `self` (`mods.List|any[]`): Current list. + +**Example**: + +```lua +a = List({ "a", "b" }) +b = { "c", "d" } +c = a + b --> c and a are the same reference: { "a", "b", "c", "d" } +``` + +> [!NOTE] +> +> `+` operator is equivalent to `:extend(ls)`. + #### `__eq(ls)` @@ -1162,11 +1227,11 @@ ok = a == b --> true (same nested table reference) > ok = (a == b) --> true > ``` - + -#### `__lt(ls)` +#### `__le(ls)` -Compare two lists lexicographically (`<`). +Compare two lists lexicographically (`<=`). **Parameters**: @@ -1174,23 +1239,23 @@ Compare two lists lexicographically (`<`). **Return**: -- `isLess` (`boolean`): Whether the condition is met. +- `isLessOrEqual` (`boolean`): Whether the condition is met. **Example**: ```lua -ok = List({ 1, 2 }) < List({ 1, 3 }) --> true +ok = List({ 1, 2 }) <= List({ 1, 2 }) --> true ``` > [!NOTE] > -> `<` is equivalent to `:lt(ls)`. +> `<=` is equivalent to `:le(ls)`. - + -#### `__le(ls)` +#### `__lt(ls)` -Compare two lists lexicographically (`<=`). +Compare two lists lexicographically (`<`). **Parameters**: @@ -1198,17 +1263,17 @@ Compare two lists lexicographically (`<=`). **Return**: -- `isLessOrEqual` (`boolean`): Whether the condition is met. +- `isLess` (`boolean`): Whether the condition is met. **Example**: ```lua -ok = List({ 1, 2 }) <= List({ 1, 2 }) --> true +ok = List({ 1, 2 }) < List({ 1, 3 }) --> true ``` > [!NOTE] > -> `<=` is equivalent to `:le(ls)`. +> `<` is equivalent to `:lt(ls)`. @@ -1235,33 +1300,6 @@ l2 = 3 * List({ "a", "b" }) --> { "a", "b", "a", "b", "a", "b" } > > `*` is equivalent to `:mul(n)`. - - -#### `__add(ls)` - -Extend the left-hand list in place with right-hand values, then return the same -left-hand list reference (`+`). - -**Parameters**: - -- `ls` (`mods.List|any[]`): Other list value. - -**Return**: - -- `self` (`mods.List|any[]`): Current list. - -**Example**: - -```lua -a = List({ "a", "b" }) -b = { "c", "d" } -c = a + b --> c and a are the same reference: { "a", "b", "c", "d" } -``` - -> [!NOTE] -> -> `+` operator is equivalent to `:extend(ls)`. - #### `__sub(ls)` diff --git a/docs/src/modules/log.md b/docs/src/modules/log.md new file mode 100644 index 0000000..749b4f0 --- /dev/null +++ b/docs/src/modules/log.md @@ -0,0 +1,94 @@ +--- +description: + "Logger factory that emits normalized records through an optional custom + handler." +--- + +# `log` + +Logger factory that emits normalized records through an optional custom handler. +When `opts.handler` is omitted, records are written to `io.stderr`. + +## Usage + +```lua +log = require "mods.log" + +local logger = log.new() +logger:warn("config missing") --> writes: [WARN]: config missing +``` + +## Functions + +**Factory**: + +| Function | Description | +| ----------------------- | -------------------- | +| [`new(opts?)`](#fn-new) | Create a new logger. | + +**Logger Methods**: + +| Function | Description | +| -------------------------------- | ----------------------------------------------------------- | +| [`debug(...)`](#fn-debug) | Emit a `debug` record. | +| [`error(...)`](#fn-error) | Emit an `error` record. | +| [`info(...)`](#fn-info) | Emit an `info` record. | +| [`log(levelname, ...)`](#fn-log) | Emit a record for `level` when it passes the logger filter. | +| [`warn(...)`](#fn-warn) | Emit a `warn` record. | + +### Factory + + + +#### `new(opts?)` + +Create a new logger. **Parameters**: + +- `opts?` (`mods.log.new.opts`): Logger configuration. + +**Return**: + +- `logger` (`mods.log.logger`): Logger instance. + +### Logger Methods + + + +#### `debug(...)` + +Emit a `debug` record. **Parameters**: + +- `...` (`any`): Additional values joined with spaces. + + + +#### `error(...)` + +Emit an `error` record. **Parameters**: + +- `...` (`any`): Additional values joined with spaces. + + + +#### `info(...)` + +Emit an `info` record. **Parameters**: + +- `...` (`any`): Additional values joined with spaces. + + + +#### `log(levelname, ...)` + +Emit a record for `level` when it passes the logger filter. **Parameters**: + +- `levelname` (`string|"debug"|"info"|"warn"|"error"|"off"`): Log level to emit. +- `...` (`any`): Additional values joined with spaces. + + + +#### `warn(...)` + +Emit a `warn` record. **Parameters**: + +- `...` (`any`): Additional values joined with spaces. diff --git a/docs/src/modules/ntpath.md b/docs/src/modules/ntpath.md index 119a590..01bdbcb 100644 --- a/docs/src/modules/ntpath.md +++ b/docs/src/modules/ntpath.md @@ -24,6 +24,18 @@ print(ntpath.isreserved([[C:\Temp\CON.txt]])) --> true ## Functions + + +### `_expand_percent_vars(p)` + +Expand percent-style variables in a string. **Parameters**: + +- `p` (`string`) + +**Return**: + +- `expanded` (`string`) + ### `ismount(path)` diff --git a/docs/src/modules/operator.md b/docs/src/modules/operator.md index 51951c7..31670a5 100644 --- a/docs/src/modules/operator.md +++ b/docs/src/modules/operator.md @@ -21,12 +21,12 @@ print(operator.add(1, 2)) --> 3 | Function | Description | | ------------------------ | --------------------------------------------------------- | | [`add(a, b)`](#fn-add) | Add two numbers. | -| [`sub(a, b)`](#fn-sub) | Subtract `b` from `a`. | -| [`mul(a, b)`](#fn-mul) | Multiply two numbers. | | [`div(a, b)`](#fn-div) | Divide `a` by `b` using Lua's floating-point division. | | [`idiv(a, b)`](#fn-idiv) | Divide `a` by `b` and return the floor-division quotient. | | [`mod(a, b)`](#fn-mod) | Return the modulo remainder of `a` divided by `b`. | +| [`mul(a, b)`](#fn-mul) | Multiply two numbers. | | [`pow(a, b)`](#fn-pow) | Raise `a` to the power of `b`. | +| [`sub(a, b)`](#fn-sub) | Subtract `b` from `a`. | | [`unm(a)`](#fn-unm) | Negate a number. | **Comparison**: @@ -34,19 +34,19 @@ print(operator.add(1, 2)) --> 3 | Function | Description | | ---------------------- | -------------------------------------------------- | | [`eq(a, b)`](#fn-eq) | Check whether two values are equal. | -| [`neq(a, b)`](#fn-neq) | Check whether two values are not equal. | -| [`lt(a, b)`](#fn-lt) | Check whether `a` is strictly less than `b`. | -| [`le(a, b)`](#fn-le) | Check whether `a` is less than or equal to `b`. | -| [`gt(a, b)`](#fn-gt) | Check whether `a` is strictly greater than `b`. | | [`ge(a, b)`](#fn-ge) | Check whether `a` is greater than or equal to `b`. | +| [`gt(a, b)`](#fn-gt) | Check whether `a` is strictly greater than `b`. | +| [`le(a, b)`](#fn-le) | Check whether `a` is less than or equal to `b`. | +| [`lt(a, b)`](#fn-lt) | Check whether `a` is strictly less than `b`. | +| [`neq(a, b)`](#fn-neq) | Check whether two values are not equal. | **Logical**: | Function | Description | | ------------------------ | ---------------------------------------------------- | | [`land(a, b)`](#fn-land) | Evaluate `a and b` with Lua short-circuit semantics. | -| [`lor(a, b)`](#fn-lor) | Evaluate `a or b` with Lua short-circuit semantics. | | [`lnot(a)`](#fn-lnot) | Return the boolean negation of `a`. | +| [`lor(a, b)`](#fn-lor) | Evaluate `a or b` with Lua short-circuit semantics. | **String & Length**: @@ -59,13 +59,13 @@ print(operator.add(1, 2)) --> 3 | Function | Description | | ----------------------------------- | -------------------------------------------------------------- | +| [`call(f, ...)`](#fn-call) | Call a function with variadic arguments and return its result. | | [`index(t, k)`](#fn-index) | Return the value at key/index `k` in table `t`. | | [`setindex(t, k, v)`](#fn-setindex) | Set `t[k] = v` and return the assigned value. | -| [`call(f, ...)`](#fn-call) | Call a function with variadic arguments and return its result. | ### Arithmetic -Numeric arithmetic operators as functions. + #### `add(a, b)` @@ -86,53 +86,53 @@ Add two numbers. add(1, 2) --> 3 ``` - + -#### `sub(a, b)` +#### `div(a, b)` -Subtract `b` from `a`. +Divide `a` by `b` using Lua's floating-point division. **Parameters**: -- `a` (`number`): Left numeric value. -- `b` (`number`): Right numeric value. +- `a` (`number`): Dividend value. +- `b` (`number`): Divisor value. **Return**: -- `difference` (`number`): Difference `a - b`. +- `quotient` (`number`): Quotient `a / b`. **Example**: ```lua -sub(5, 3) --> 2 +div(10, 4) --> 2.5 ``` - + -#### `mul(a, b)` +#### `idiv(a, b)` -Multiply two numbers. +Divide `a` by `b` and return the floor-division quotient. **Parameters**: -- `a` (`number`): Left numeric value. -- `b` (`number`): Right numeric value. +- `a` (`number`): Dividend value. +- `b` (`number`): Divisor value. **Return**: -- `product` (`number`): Product `a * b`. +- `quotient` (`integer`): Floor-division result. **Example**: ```lua -mul(3, 4) --> 12 +idiv(5, 2) --> 2 ``` - + -#### `div(a, b)` +#### `mod(a, b)` -Divide `a` by `b` using Lua's floating-point division. +Return the modulo remainder of `a` divided by `b`. **Parameters**: @@ -141,75 +141,75 @@ Divide `a` by `b` using Lua's floating-point division. **Return**: -- `quotient` (`number`): Quotient `a / b`. +- `remainder` (`number`): Remainder of `a % b`. **Example**: ```lua -div(10, 4) --> 2.5 +mod(5, 2) --> 1 ``` - + -#### `idiv(a, b)` +#### `mul(a, b)` -Divide `a` by `b` and return the floor-division quotient. +Multiply two numbers. **Parameters**: -- `a` (`number`): Dividend value. -- `b` (`number`): Divisor value. +- `a` (`number`): Left numeric value. +- `b` (`number`): Right numeric value. **Return**: -- `quotient` (`integer`): Floor-division result. +- `product` (`number`): Product `a * b`. **Example**: ```lua -idiv(5, 2) --> 2 +mul(3, 4) --> 12 ``` - + -#### `mod(a, b)` +#### `pow(a, b)` -Return the modulo remainder of `a` divided by `b`. +Raise `a` to the power of `b`. **Parameters**: -- `a` (`number`): Dividend value. -- `b` (`number`): Divisor value. +- `a` (`number`): Base value. +- `b` (`number`): Exponent value. **Return**: -- `remainder` (`number`): Remainder of `a % b`. +- `power` (`number`): Result of `a ^ b`. **Example**: ```lua -mod(5, 2) --> 1 +pow(2, 4) --> 16 ``` - + -#### `pow(a, b)` +#### `sub(a, b)` -Raise `a` to the power of `b`. +Subtract `b` from `a`. **Parameters**: -- `a` (`number`): Base value. -- `b` (`number`): Exponent value. +- `a` (`number`): Left numeric value. +- `b` (`number`): Right numeric value. **Return**: -- `power` (`number`): Result of `a ^ b`. +- `difference` (`number`): Difference `a - b`. **Example**: ```lua -pow(2, 4) --> 16 +sub(5, 3) --> 2 ``` @@ -234,7 +234,7 @@ unm(3) --> -3 ### Comparison -Equality and ordering comparison operators. + #### `eq(a, b)` @@ -255,32 +255,32 @@ Check whether two values are equal. eq(1, 1) --> true ``` - + -#### `neq(a, b)` +#### `ge(a, b)` -Check whether two values are not equal. +Check whether `a` is greater than or equal to `b`. **Parameters**: -- `a` (`any`): Left value. -- `b` (`any`): Right value. +- `a` (`number`): Left numeric value. +- `b` (`number`): Right numeric value. **Return**: -- `isNotEqual` (`boolean`): True when `a ~= b`. +- `isGreaterOrEqual` (`boolean`): True when `a >= b`. **Example**: ```lua -neq(1, 2) --> true +ge(2, 2) --> true ``` - + -#### `lt(a, b)` +#### `gt(a, b)` -Check whether `a` is strictly less than `b`. +Check whether `a` is strictly greater than `b`. **Parameters**: @@ -289,12 +289,12 @@ Check whether `a` is strictly less than `b`. **Return**: -- `isLess` (`boolean`): True when `a < b`. +- `isGreater` (`boolean`): True when `a > b`. **Example**: ```lua -lt(1, 2) --> true +gt(3, 2) --> true ``` @@ -318,11 +318,11 @@ Check whether `a` is less than or equal to `b`. le(2, 2) --> true ``` - + -#### `gt(a, b)` +#### `lt(a, b)` -Check whether `a` is strictly greater than `b`. +Check whether `a` is strictly less than `b`. **Parameters**: @@ -331,38 +331,38 @@ Check whether `a` is strictly greater than `b`. **Return**: -- `isGreater` (`boolean`): True when `a > b`. +- `isLess` (`boolean`): True when `a < b`. **Example**: ```lua -gt(3, 2) --> true +lt(1, 2) --> true ``` - + -#### `ge(a, b)` +#### `neq(a, b)` -Check whether `a` is greater than or equal to `b`. +Check whether two values are not equal. **Parameters**: -- `a` (`number`): Left numeric value. -- `b` (`number`): Right numeric value. +- `a` (`any`): Left value. +- `b` (`any`): Right value. **Return**: -- `isGreaterOrEqual` (`boolean`): True when `a >= b`. +- `isNotEqual` (`boolean`): True when `a ~= b`. **Example**: ```lua -ge(2, 2) --> true +neq(1, 2) --> true ``` ### Logical -Boolean logic operators with Lua truthiness semantics. + #### `land(a, b)` @@ -383,50 +383,50 @@ Evaluate `a and b` with Lua short-circuit semantics. land(true, false) --> false ``` - + -#### `lor(a, b)` +#### `lnot(a)` -Evaluate `a or b` with Lua short-circuit semantics. +Return the boolean negation of `a`. **Parameters**: -- `a` (`T1`): First operand. -- `b` (`T2`): Second operand. +- `a` (`any`): Input value. **Return**: -- `orValue` (`T1|T2`): Result of `a or b`. +- `isNot` (`boolean`): Result of `not a`. **Example**: ```lua -lor(false, true) --> true +lnot(true) --> false ``` - + -#### `lnot(a)` +#### `lor(a, b)` -Return the boolean negation of `a`. +Evaluate `a or b` with Lua short-circuit semantics. **Parameters**: -- `a` (`any`): Input value. +- `a` (`T1`): First operand. +- `b` (`T2`): Second operand. **Return**: -- `isNot` (`boolean`): Result of `not a`. +- `orValue` (`T1|T2`): Result of `a or b`. **Example**: ```lua -lnot(true) --> false +lor(false, true) --> true ``` ### String & Length -String concatenation and length operators. + #### `concat(a, b)` @@ -469,66 +469,66 @@ len("abc") --> 3 ### Tables & Calls -Table indexing helpers and function invocation. + -#### `index(t, k)` +#### `call(f, ...)` -Return the value at key/index `k` in table `t`. +Call a function with variadic arguments and return its result. **Parameters**: -- `t` (`table`): Source table. -- `k` (`T`): Key/index value. +- `f` (`fun(...:T1):T2`): Function to call. +- `...` (`T1`): Additional arguments. **Return**: -- `indexedValue` (`T`): Value stored at `t[k]`. +- `callResult` (`T2`): Return value(s) from `f(...)`. **Example**: ```lua -index({ a = 1 }, "a") --> 1 +call(math.max, 1, 2) --> 2 ``` - + -#### `setindex(t, k, v)` +#### `index(t, k)` -Set `t[k] = v` and return the assigned value. +Return the value at key/index `k` in table `t`. **Parameters**: -- `t` (`table`): Target table. -- `k` (`any`): Key/index value. -- `v` (`T`): Value to set. +- `t` (`table`): Source table. +- `k` (`T`): Key/index value. **Return**: -- `assignedValue` (`T`): Assigned value `v`. +- `indexedValue` (`T`): Value stored at `t[k]`. **Example**: ```lua -setindex({}, "a", 1) --> 1 +index({ a = 1 }, "a") --> 1 ``` - + -#### `call(f, ...)` +#### `setindex(t, k, v)` -Call a function with variadic arguments and return its result. +Set `t[k] = v` and return the assigned value. **Parameters**: -- `f` (`fun(...:T1):T2`): Function to call. -- `...` (`T1`): Additional arguments. +- `t` (`table`): Target table. +- `k` (`any`): Key/index value. +- `v` (`T`): Value to set. **Return**: -- `callResult` (`T2`): Return value(s) from `f(...)`. +- `assignedValue` (`T`): Assigned value `v`. **Example**: ```lua -call(math.max, 1, 2) --> 2 +setindex({}, "a", 1) --> 1 ``` diff --git a/docs/src/modules/path.md b/docs/src/modules/path.md index fe5f7a5..92c0272 100644 --- a/docs/src/modules/path.md +++ b/docs/src/modules/path.md @@ -18,161 +18,192 @@ print(path.splitext("archive.tar.gz")) --> "archive.tar", ".gz" ## Functions -**Normalization**: +| Function | Description | +| ------------------------------------------------------- | ---------------------------- | +| [`_splitext(path, sep, altsep?, extsep)`](#fn-splitext) | Split extension from a path. | -| Function | Description | -| -------------------------------- | ---------------------------------------------------- | -| [`normcase(s)`](#fn-normcase) | Normalize path case using the active path semantics. | -| [`join(path, ...)`](#fn-join) | Join path components. | -| [`normpath(path)`](#fn-normpath) | Normalize separators and dot segments. | -| [`isabs(path)`](#fn-isabs) | Return `true` when `path` is absolute. | +**Anchors**: + +| Function | Description | +| ---------------------------- | ------------------------------------------- | +| [`anchor(path)`](#fn-anchor) | Return drive and root combined. | +| [`drive(path)`](#fn-drive) | Return drive prefix when present. | +| [`root(path)`](#fn-root) | Return root separator segment when present. | + +**Components**: + +| Function | Description | +| -------------------------------- | ------------------------------------------------------------- | +| [`parents(path)`](#fn-parents) | Return logical parent paths from nearest to farthest. | +| [`parts(path)`](#fn-parts) | Split path into logical parts, including anchor when present. | +| [`stem(path)`](#fn-stem) | Return filename without its final suffix. | +| [`suffixes(path)`](#fn-suffixes) | Return all filename suffixes in order. | + +**Conversions**: + +| Function | Description | +| -------------------------------- | --------------------------------------------------- | +| [`as_posix(path)`](#fn-as-posix) | Convert backslashes (`\`) to forward slashes (`/`). | +| [`as_uri(path)`](#fn-as-uri) | Convert a local path to a `file://` URI. | +| [`from_uri(uri)`](#fn-from-uri) | Convert a `file://` URI to a local absolute path. | **Decomposition**: | Function | Description | | ------------------------------------ | -------------------------------------------------- | +| [`basename(path)`](#fn-basename) | Return final path component. | +| [`dirname(path)`](#fn-dirname) | Return directory portion of a path. | | [`split(path)`](#fn-split) | Split path into directory head and tail component. | -| [`splitext(path)`](#fn-splitext) | Split path into a root and extension. | | [`splitdrive(path)`](#fn-splitdrive) | Split drive prefix from remainder. | +| [`splitext(path)`](#fn-splitext) | Split path into a root and extension. | | [`splitroot(path)`](#fn-splitroot) | Split path into drive, root, and tail components. | -| [`basename(path)`](#fn-basename) | Return final path component. | -| [`dirname(path)`](#fn-dirname) | Return directory portion of a path. | - -**Environment**: - -| Function | Description | -| ------------------------------------ | ----------------------------------------------------------------------- | -| [`expanduser(path)`](#fn-expanduser) | Expand `~` home segment when available. | -| [`expandvars(path)`](#fn-expandvars) | Expand vars in a path (`$VAR`/`${VAR}` everywhere, `%VAR%` on Windows). | -| [`home()`](#fn-home) | Return the current user's home directory path. | -| [`cwd()`](#fn-cwd) | Return the current working directory path. | **Derived**: | Function | Description | | ----------------------------------------- | ------------------------------------------------ | | [`abspath(path)`](#fn-abspath) | Return normalized absolute path. | -| [`relpath(path, start?)`](#fn-relpath) | Return `path` relative to optional `start` path. | | [`commonpath(paths)`](#fn-commonpath) | Return longest common sub-path from a path list. | | [`commonprefix(paths)`](#fn-commonprefix) | Return longest common leading string prefix. | +| [`relpath(path, start?)`](#fn-relpath) | Return `path` relative to optional `start` path. | -**Anchors**: +**Environment**: -| Function | Description | -| ---------------------------- | ------------------------------------------- | -| [`drive(path)`](#fn-drive) | Return drive prefix when present. | -| [`root(path)`](#fn-root) | Return root separator segment when present. | -| [`anchor(path)`](#fn-anchor) | Return drive and root combined. | +| Function | Description | +| ------------------------------------ | ----------------------------------------------------------------------- | +| `cwd` | Return the current working directory path. | +| [`expanduser(path)`](#fn-expanduser) | Expand `~` home segment when available. | +| [`expandvars(path)`](#fn-expandvars) | Expand vars in a path (`$VAR`/`${VAR}` everywhere, `%VAR%` on Windows). | +| [`home()`](#fn-home) | Return the current user's home directory path. | -**Components**: +**Normalization**: -| Function | Description | -| -------------------------------- | ------------------------------------------------------------- | -| [`parts(path)`](#fn-parts) | Split path into logical parts, including anchor when present. | -| [`stem(path)`](#fn-stem) | Return filename without its final suffix. | -| [`suffixes(path)`](#fn-suffixes) | Return all filename suffixes in order. | -| [`parents(path)`](#fn-parents) | Return logical parent paths from nearest to farthest. | +| Function | Description | +| -------------------------------- | ---------------------------------------------------- | +| [`isabs(path)`](#fn-isabs) | Return `true` when `path` is absolute. | +| [`join(path, ...)`](#fn-join) | Join path components. | +| [`normcase(s)`](#fn-normcase) | Normalize path case using the active path semantics. | +| [`normpath(path)`](#fn-normpath) | Normalize separators and dot segments. | **Relations**: | Function | Description | | ------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| [`relative_to(path, other, walk_up?)`](#fn-relative-to) | Return `path` relative to `other`, or `nil` with an error when it is not under `other`. | | [`is_relative_to(path, other)`](#fn-is-relative-to) | Return `true` when `path` is under `other`. | +| [`relative_to(path, other, walk_up?)`](#fn-relative-to) | Return `path` relative to `other`, or `nil` with an error when it is not under `other`. | | [`with_name(path, name)`](#fn-with-name) | Return a path with the final filename replaced. | | [`with_stem(path, stem)`](#fn-with-stem) | Return a path with the final filename stem replaced. | | [`with_suffix(path, suffix)`](#fn-with-suffix) | Return a path with the final filename suffix replaced. | -**Conversions**: + -| Function | Description | -| ---------------------------------------------------- | --------------------------------------------------------------------------- | -| [`as_posix(path)`](#fn-as-posix) | Convert backslashes (`\`) to forward slashes (`/`). | -| [`as_uri(path)`](#fn-as-uri) | Convert a local path to a `file://` URI. | -| [`match(path, pattern, case_sensitive?)`](#fn-match) | Match a path against a glob-style pattern using only `*` and `?` wildcards. | -| [`from_uri(uri)`](#fn-from-uri) | Convert a `file://` URI to a local absolute path. | +### `_splitext(path, sep, altsep?, extsep)` -### Normalization +Split extension from a path. **Parameters**: - +- `path` (`string`) +- `sep` (`string`) +- `altsep?` (`string`) +- `extsep` (`string`) -#### `normcase(s)` +**Return**: -Normalize path case using the active path semantics. +- `root` (`string`) +- `ext` (`string`) + +### Anchors + + + +#### `anchor(path)` + +Return drive and root combined. **Parameters**: -- `s` (`string`): Input path value. +- `path` (`string`): Input path. **Return**: -- `normalizedPath` (`string`): Path after case normalization. +- `anchor` (`string`): Drive and root anchor. **Example**: ```lua -path.normcase("ABC") --> "abc" -path.normcase("/A/B") --> "\\a\\b" +path.anchor("c:\\") --> "c:\\" ``` -> [!NOTE] -> -> On POSIX semantics this returns the input unchanged. Use -> [`mods.ntpath`](/modules/ntpath) to force Windows-style case folding and -> separator normalization. + - +#### `drive(path)` -#### `join(path, ...)` +Return drive prefix when present. -Join path components. +**Parameters**: + +- `path` (`string`): Input path. + +**Return**: + +- `drivePrefix` (`string`): Drive prefix. + +**Example**: + +```lua +path.drive("c:a/b") --> "c:" +path.drive("a/b") --> "" +``` + + + +#### `root(path)` + +Return root separator segment when present. **Parameters**: -- `path` (`string`): Base path component. -- `...` (`string`): Additional path components. +- `path` (`string`): Input path. **Return**: -- `joinedPath` (`string`): Joined path. +- `rootSeparator` (`string`): Root separator segment. **Example**: ```lua -path.join("/usr", "bin") --> "/usr/bin" -path.join([[C:/a]], [[b]]) --> [[C:/a\b]] +path.root("/tmp/a.txt") --> "/" +path.root("c:/") --> "\\" +path.root("a/b") --> "" ``` -> [!NOTE] -> -> Single input is returned as-is. +### Components - + -#### `normpath(path)` +#### `parents(path)` -Normalize separators and dot segments. +Return logical parent paths from nearest to farthest. **Parameters**: -- `path` (`string`): Path to normalize. +- `path` (`string`): Input path. **Return**: -- `normalizedPath` (`string`): Normalized path. +- `parents` (`mods.List`): Ancestor paths from nearest to farthest. **Example**: ```lua -path.normpath("/a//./b/..") --> "/a" -path.normpath([[A/foo/../B]]) --> [[A\B]] +path.parents("a/b/c") --> {"a/b", "a", "."} +path.parents("c:a/b") --> {"c:a", "c:"} ``` - + -#### `isabs(path)` +#### `parts(path)` -Return `true` when `path` is absolute. +Split path into logical parts, including anchor when present. **Parameters**: @@ -180,21 +211,21 @@ Return `true` when `path` is absolute. **Return**: -- `isAbsolute` (`boolean`): True when `path` is absolute. +- `paths` (`mods.List`): Path parts including anchor when present. **Example**: ```lua -path.isabs("/a/b") --> true +path.parts("a/b.txt") --> {"a", "b.txt"} +path.parts("/a/b") --> {"/", "a", "b"} +path.parts("c:a\\b") --> {"c:", "a", "b"} ``` -### Decomposition - - + -#### `split(path)` +#### `stem(path)` -Split path into directory head and tail component. +Return filename without its final suffix. **Parameters**: @@ -202,20 +233,20 @@ Split path into directory head and tail component. **Return**: -- `head` (`string`): Directory portion. -- `tail` (`string`): Final path component. +- `stem` (`string`): Filename stem. **Example**: ```lua -path.split("/a/b.txt") --> "/a", "b.txt" +path.stem("archive.tar.gz") --> "archive.tar" +path.stem("c:a/b") --> "b" ``` - + -#### `splitext(path)` +#### `suffixes(path)` -Split path into a root and extension. +Return all filename suffixes in order. **Parameters**: @@ -223,20 +254,22 @@ Split path into a root and extension. **Return**: -- `root` (`string`): Path without the final extension. -- `ext` (`string`): Final extension including leading dot. +- `suffixes` (`mods.List`): Filename suffixes. **Example**: ```lua -path.splitext("archive.tar.gz") --> "archive.tar", ".gz" +path.suffixes("archive.tar.gz") --> {".tar", ".gz"} +path.suffixes("a/b") --> {} ``` - +### Conversions -#### `splitdrive(path)` + -Split drive prefix from remainder. +#### `as_posix(path)` + +Convert backslashes (`\`) to forward slashes (`/`). **Parameters**: @@ -244,42 +277,60 @@ Split drive prefix from remainder. **Return**: -- `drive` (`string`): Drive or share prefix when present. -- `rest` (`string`): Path remainder. +- `posixPath` (`string`): POSIX-style path. **Example**: ```lua -path.splitdrive("/a/b") --> "", "/a/b" +path.as_posix("a\\b\\c") --> "a/b/c" ``` -> [!NOTE] -> -> On POSIX semantics the drive portion is always empty. + - +#### `as_uri(path)` -#### `splitroot(path)` +Convert a local path to a `file://` URI. -Split path into drive, root, and tail components. +**Parameters**: + +- `path` (`string`): Input path. + +**Return**: + +- `fileUri` (`string?`): File URI. +- `err` (`string?`): Error message when conversion fails. + +**Example**: + +```lua +path.as_uri("/home/user/report.txt") --> "file:///home/user/report.txt" +path.as_uri("c:/a/b.c") --> "file:///c:/a/b.c" +path.as_uri("/a/b%#c") --> "file:///a/b%25%23c" +``` + + + +#### `from_uri(uri)` + +Convert a `file://` URI to a local absolute path. **Parameters**: -- `path` (`string`): Path to split. +- `uri` (`string`): URI value. **Return**: -- `drive` (`string`): Drive or share prefix (empty on POSIX). -- `root` (`string`): Root separator segment. -- `tail` (`string`): Remaining path without leading root separator. +- `path` (`string?`): Resolved absolute path. +- `err` (`string?`): Error message when conversion fails. **Example**: ```lua -path.splitroot("/a/b") --> "", "/", "a/b" -path.splitroot([[C:\a\b]]) --> "C:", [[\]], "a\\b" +path.from_uri("file://localhost/tmp/a.txt") --> "/tmp/a.txt" ``` +### Decomposition + #### `basename(path)` @@ -322,86 +373,94 @@ path.dirname("/a/b.txt") --> "/a" path.dirname([[C:\a\b.txt]]) --> [[C:\a]] ``` -### Environment - - + -#### `expanduser(path)` +#### `split(path)` -Expand `~` home segment when available. +Split path into directory head and tail component. **Parameters**: -- `path` (`string`): Path that may begin with `~`. +- `path` (`string`): Input path. **Return**: -- `expandedPath` (`string?`): Path with the home segment expanded when - available. -- `err` (`string?`): Error message when `~` expansion cannot be resolved. +- `head` (`string`): Directory portion. +- `tail` (`string`): Final path component. **Example**: ```lua -path.expanduser("~/tmp") --> "/tmp" (when HOME is set) -path.expanduser([[x\y]]) --> [[x\y]] +path.split("/a/b.txt") --> "/a", "b.txt" ``` - + -#### `expandvars(path)` +#### `splitdrive(path)` -Expand vars in a path (`$VAR`/`${VAR}` everywhere, `%VAR%` on Windows). +Split drive prefix from remainder. **Parameters**: -- `path` (`string`): Path containing variable placeholders. +- `path` (`string`): Input path. **Return**: -- `expandedPath` (`string`): Path with variable values substituted. +- `drive` (`string`): Drive or share prefix when present. +- `rest` (`string`): Path remainder. **Example**: ```lua -path.expandvars("$HOME/bin") --> "/home/me/bin" -path.expandvars("${XDG_CONFIG_HOME}/nvim") --> "/home/me/.config/nvim" -path.expandvars("%USERPROFILE%\\bin") --> "C:\\Users\\me\\bin" -path.expandvars("$UNKNOWN/bin") --> "$UNKNOWN/bin" +path.splitdrive("/a/b") --> "", "/a/b" ``` - +> [!NOTE] +> +> On POSIX semantics the drive portion is always empty. -#### `home()` + -Return the current user's home directory path. +#### `splitext(path)` + +Split path into a root and extension. + +**Parameters**: + +- `path` (`string`): Input path. **Return**: -- `homePath` (`string?`): Home directory path when available. -- `err` (`string?`): Error message when the home directory cannot be resolved. +- `root` (`string`): Path without the final extension. +- `ext` (`string`): Final extension including leading dot. **Example**: ```lua -path.home() +path.splitext("archive.tar.gz") --> "archive.tar", ".gz" ``` - + -#### `cwd()` +#### `splitroot(path)` -Return the current working directory path. +Split path into drive, root, and tail components. + +**Parameters**: + +- `path` (`string`): Path to split. **Return**: -- `cwd` (`string?`): Current working directory path. -- `err` (`string?`): Error message when the cwd cannot be resolved. +- `drive` (`string`): Drive or share prefix (empty on POSIX). +- `root` (`string`): Root separator segment. +- `tail` (`string`): Remaining path without leading root separator. **Example**: ```lua -path.cwd() +path.splitroot("/a/b") --> "", "/", "a/b" +path.splitroot([[C:\a\b]]) --> "C:", [[\]], "a\\b" ``` ### Derived @@ -424,30 +483,7 @@ Return normalized absolute path. ```lua path.abspath("/a/./b") --> "/a/b" -path.abspath([[C:\a\..\b]]) --> [[C:\b]] -``` - - - -#### `relpath(path, start?)` - -Return `path` relative to optional `start` path. - -**Parameters**: - -- `path` (`string`): Input path. -- `start?` (`string`): Optional base path. - -**Return**: - -- `relativePath` (`string?`): Relative path from `start` to `path`. -- `err` (`string?`): Error message when the path cannot be made relative. - -**Example**: - -```lua -path.relpath("/a/b/c", "/a") --> "b/c" -path.relpath([[C:\a\b\c]], [[C:\a]]) --> [[b\c]] +path.abspath([[C:\a\..\b]]) --> [[C:\b]] ``` @@ -494,100 +530,105 @@ path.commonprefix({"/home/swen/spam", "/home/swen/eggs"}) --> "/home/swen/" path.commonprefix({"abc", "xyz"}) --> "" ``` -### Anchors - - + -#### `drive(path)` +#### `relpath(path, start?)` -Return drive prefix when present. +Return `path` relative to optional `start` path. **Parameters**: - `path` (`string`): Input path. +- `start?` (`string`): Optional base path. **Return**: -- `drivePrefix` (`string`): Drive prefix. +- `relativePath` (`string?`): Relative path from `start` to `path`. +- `err` (`string?`): Error message when the path cannot be made relative. **Example**: ```lua -path.drive("c:a/b") --> "c:" -path.drive("a/b") --> "" +path.relpath("/a/b/c", "/a") --> "b/c" +path.relpath([[C:\a\b\c]], [[C:\a]]) --> [[b\c]] ``` - +### Environment -#### `root(path)` + -Return root separator segment when present. +#### `cwd` + +Return the current working directory path. + +#### `expanduser(path)` + +Expand `~` home segment when available. **Parameters**: -- `path` (`string`): Input path. +- `path` (`string`): Path that may begin with `~`. **Return**: -- `rootSeparator` (`string`): Root separator segment. +- `expandedPath` (`string?`): Path with the home segment expanded when + available. +- `err` (`string?`): Error message when `~` expansion cannot be resolved. **Example**: ```lua -path.root("/tmp/a.txt") --> "/" -path.root("c:/") --> "\\" -path.root("a/b") --> "" +path.expanduser("~/tmp") --> "/tmp" (when HOME is set) +path.expanduser([[x\y]]) --> [[x\y]] ``` - + -#### `anchor(path)` +#### `expandvars(path)` -Return drive and root combined. +Expand vars in a path (`$VAR`/`${VAR}` everywhere, `%VAR%` on Windows). **Parameters**: -- `path` (`string`): Input path. +- `path` (`string`): Path containing variable placeholders. **Return**: -- `anchor` (`string`): Drive and root anchor. +- `expandedPath` (`string`): Path with variable values substituted. **Example**: ```lua -path.anchor("c:\\") --> "c:\\" +path.expandvars("$HOME/bin") --> "/home/me/bin" +path.expandvars("${XDG_CONFIG_HOME}/nvim") --> "/home/me/.config/nvim" +path.expandvars("%USERPROFILE%\\bin") --> "C:\\Users\\me\\bin" +path.expandvars("$UNKNOWN/bin") --> "$UNKNOWN/bin" ``` -### Components - - - -#### `parts(path)` - -Split path into logical parts, including anchor when present. + -**Parameters**: +#### `home()` -- `path` (`string`): Input path. +Return the current user's home directory path. **Return**: -- `paths` (`mods.List`): Path parts including anchor when present. +- `homePath` (`string?`): Home directory path when available. +- `err` (`string?`): Error message when the home directory cannot be resolved. **Example**: ```lua -path.parts("a/b.txt") --> {"a", "b.txt"} -path.parts("/a/b") --> {"/", "a", "b"} -path.parts("c:a\\b") --> {"c:", "a", "b"} +path.home() ``` - +### Normalization -#### `stem(path)` + -Return filename without its final suffix. +#### `isabs(path)` + +Return `true` when `path` is absolute. **Parameters**: @@ -595,87 +636,90 @@ Return filename without its final suffix. **Return**: -- `stem` (`string`): Filename stem. +- `isAbsolute` (`boolean`): True when `path` is absolute. **Example**: ```lua -path.stem("archive.tar.gz") --> "archive.tar" -path.stem("c:a/b") --> "b" +path.isabs("/a/b") --> true ``` - + -#### `suffixes(path)` +#### `join(path, ...)` -Return all filename suffixes in order. +Join path components. **Parameters**: -- `path` (`string`): Input path. +- `path` (`string`): Base path component. +- `...` (`string`): Additional path components. **Return**: -- `suffixes` (`mods.List`): Filename suffixes. +- `joinedPath` (`string`): Joined path. **Example**: ```lua -path.suffixes("archive.tar.gz") --> {".tar", ".gz"} -path.suffixes("a/b") --> {} +path.join("/usr", "bin") --> "/usr/bin" +path.join([[C:/a]], [[b]]) --> [[C:/a\b]] ``` - +> [!NOTE] +> +> Single input is returned as-is. -#### `parents(path)` + -Return logical parent paths from nearest to farthest. +#### `normcase(s)` + +Normalize path case using the active path semantics. **Parameters**: -- `path` (`string`): Input path. +- `s` (`string`): Input path value. **Return**: -- `parents` (`mods.List`): Ancestor paths from nearest to farthest. +- `normalizedPath` (`string`): Path after case normalization. **Example**: ```lua -path.parents("a/b/c") --> {"a/b", "a", "."} -path.parents("c:a/b") --> {"c:a", "c:"} +path.normcase("ABC") --> "abc" +path.normcase("/A/B") --> "\\a\\b" ``` -### Relations - - +> [!NOTE] +> +> On POSIX semantics this returns the input unchanged. Use +> [`mods.ntpath`](/modules/ntpath) to force Windows-style case folding and +> separator normalization. -#### `relative_to(path, other, walk_up?)` + -Return `path` relative to `other`, or `nil` with an error when it is not under -`other`. +#### `normpath(path)` -When `walk_up` is `true`, allow `..` segments to walk up to a shared prefix. +Normalize separators and dot segments. **Parameters**: -- `path` (`string`): Input path. -- `other` (`string`): Reference path. -- `walk_up?` (`boolean`): Allow walking up to a shared prefix. +- `path` (`string`): Path to normalize. **Return**: -- `relativePath` (`string?`): Path relative to `other`, or `nil` on error. -- `err` (`string?`): Error message when the path cannot be made relative. +- `normalizedPath` (`string`): Normalized path. **Example**: ```lua -path.relative_to("/a/b/c.txt", "/a") --> "b/c.txt" -path.relative_to("/a/b", "/a/c", true) --> "../b" -path.relative_to("/a/b", "/a/x") --> nil, "'/a/b' is not in the subpath of '/a/x'" +path.normpath("/a//./b/..") --> "/a" +path.normpath([[A/foo/../B]]) --> [[A\B]] ``` +### Relations + #### `is_relative_to(path, other)` @@ -699,6 +743,34 @@ path.is_relative_to("C:A/B", "c:a") --> true path.is_relative_to("a/b", "a/b/c") --> false ``` + + +#### `relative_to(path, other, walk_up?)` + +Return `path` relative to `other`, or `nil` with an error when it is not under +`other`. + +When `walk_up` is `true`, allow `..` segments to walk up to a shared prefix. + +**Parameters**: + +- `path` (`string`): Input path. +- `other` (`string`): Reference path. +- `walk_up?` (`boolean`): Allow walking up to a shared prefix. + +**Return**: + +- `relativePath` (`string?`): Path relative to `other`, or `nil` on error. +- `err` (`string?`): Error message when the path cannot be made relative. + +**Example**: + +```lua +path.relative_to("/a/b/c.txt", "/a") --> "b/c.txt" +path.relative_to("/a/b", "/a/c", true) --> "../b" +path.relative_to("/a/b", "/a/x") --> nil, "'/a/b' is not in the subpath of '/a/x'" +``` + #### `with_name(path, name)` @@ -774,94 +846,3 @@ path.with_suffix("a/b.gz", ".lua") --> "a/b/.lua" path.with_suffix("a/b", "gz") --> nil, "invalid suffix 'gz'" path.with_suffix("//a/b", "gz") --> nil, "'//a/b' has an empty name" ``` - -### Conversions - - - -#### `as_posix(path)` - -Convert backslashes (`\`) to forward slashes (`/`). - -**Parameters**: - -- `path` (`string`): Input path. - -**Return**: - -- `posixPath` (`string`): POSIX-style path. - -**Example**: - -```lua -path.as_posix("a\\b\\c") --> "a/b/c" -``` - - - -#### `as_uri(path)` - -Convert a local path to a `file://` URI. - -**Parameters**: - -- `path` (`string`): Input path. - -**Return**: - -- `fileUri` (`string?`): File URI. -- `err` (`string?`): Error message when conversion fails. - -**Example**: - -```lua -path.as_uri("/home/user/report.txt") --> "file:///home/user/report.txt" -path.as_uri("c:/a/b.c") --> "file:///c:/a/b.c" -path.as_uri("/a/b%#c") --> "file:///a/b%25%23c" -``` - - - -#### `match(path, pattern, case_sensitive?)` - -Match a path against a glob-style pattern using only `*` and `?` wildcards. - -**Parameters**: - -- `path` (`string`): Input path. -- `pattern` (`string`): Pattern to match. -- `case_sensitive?` (`boolean`): Override platform-default case matching. - -**Return**: - -- `matchesPattern` (`boolean`): True when the path matches. - -**Example**: - -```lua -path.match("a/b.lua", "*.lua") --> true -path.match("A.lua", "a.LUA", false) --> true -path.match("notes.txt", "n?tes.*") --> true -path.match("a/b/c.lua", "a/*/c.lua") --> true -``` - - - -#### `from_uri(uri)` - -Convert a `file://` URI to a local absolute path. - -**Parameters**: - -- `uri` (`string`): URI value. - -**Return**: - -- `path` (`string?`): Resolved absolute path. -- `err` (`string?`): Error message when conversion fails. - -**Example**: - -```lua -path.from_uri("file://localhost/tmp/a.txt") --> "/tmp/a.txt" -``` diff --git a/docs/src/modules/runtime.md b/docs/src/modules/runtime.md index 6c3e236..805d576 100644 --- a/docs/src/modules/runtime.md +++ b/docs/src/modules/runtime.md @@ -11,102 +11,121 @@ Lua runtime metadata and version compatibility flags. ```lua runtime = require "mods.runtime" -print(runtime.version) --> "Lua 5.x" -print(runtime.version_num) --> 501 | 502 | 503 | 504 -print(runtime.is_lua54) --> true | false +print(runtime.version) --> 501 | 502 | 503 | 504 | 505 +print(runtime.is_lua55) --> true | false ``` ## Fields -| Field | Description | -| ----------------------------- | ------------------------------------------------- | -| [`version`](#version) | Version string reported by the runtime. | -| [`major`](#major) | Major version number parsed from `version`. | -| [`minor`](#minor) | Minor version number parsed from `version`. | -| [`version_num`](#version-num) | Numeric version encoded as `major * 100 + minor`. | -| [`is_luajit`](#is-luajit) | True when running under LuaJIT. | -| [`is_windows`](#is-windows) | True when running on a Windows host. | -| [`is_lua51`](#is-lua51) | True only on Lua 5.1 runtimes. | -| [`is_lua52`](#is-lua52) | True only on Lua 5.2 runtimes. | -| [`is_lua53`](#is-lua53) | True only on Lua 5.3 runtimes. | -| [`is_lua54`](#is-lua54) | True only on Lua 5.4 runtimes. | +| Field | Description | +| --------------------------- | ------------------------------------------------- | +| [`is_lua51`](#is-lua51) | True only on Lua 5.1 runtimes. | +| [`is_lua52`](#is-lua52) | True only on Lua 5.2 runtimes. | +| [`is_lua53`](#is-lua53) | True only on Lua 5.3 runtimes. | +| [`is_lua54`](#is-lua54) | True only on Lua 5.4 runtimes. | +| [`is_lua55`](#is-lua55) | True only on Lua 5.5 runtimes. | +| [`is_luajit`](#is-luajit) | True when running under LuaJIT. | +| [`is_windows`](#is-windows) | True when running on a Windows host. | +| [`major`](#major) | Major version number parsed from `version`. | +| [`minor`](#minor) | Minor version number parsed from `version`. | +| [`version`](#version) | Numeric version encoded as `major * 100 + minor`. | -### `version` + -Version string reported by the runtime. +### `is_lua51` (`boolean`) + +True only on Lua 5.1 runtimes. ```lua -print(runtime.version) --> "Lua 5.x" +print(runtime.is_lua51) --> true | false ``` -### `major` + -Major version number parsed from `version`. +### `is_lua52` (`boolean`) + +True only on Lua 5.2 runtimes. ```lua -print(runtime.major) --> 5 +print(runtime.is_lua52) --> true | false ``` -### `minor` + -Minor version number parsed from `version`. +### `is_lua53` (`boolean`) + +True only on Lua 5.3 runtimes. ```lua -print(runtime.minor) --> 1 | 2 | 3 | 4 +print(runtime.is_lua53) --> true | false ``` -### `version_num` + -Numeric version encoded as `major * 100 + minor`. +### `is_lua54` (`boolean`) + +True only on Lua 5.4 runtimes. ```lua -print(runtime.version_num) --> 501 | 502 | 503 | 504 +print(runtime.is_lua54) --> true | false ``` -### `is_luajit` + -True when running under LuaJIT. +### `is_lua55` (`boolean`) + +True only on Lua 5.5 runtimes. ```lua -print(runtime.is_luajit) --> true | false +print(runtime.is_lua55) --> true | false ``` -### `is_windows` + -True when running on a Windows host. +### `is_luajit` (`boolean`) + +True when running under LuaJIT. ```lua -print(runtime.is_windows) --> true | false +print(runtime.is_luajit) --> true | false ``` -### `is_lua51` + -True only on Lua 5.1 runtimes. +### `is_windows` (`boolean`) + +True when running on a Windows host. ```lua -print(runtime.is_lua51) --> true | false +print(runtime.is_windows) --> true | false ``` -### `is_lua52` + -True only on Lua 5.2 runtimes. +### `major` (`5`) + +Major version number parsed from `version`. ```lua -print(runtime.is_lua52) --> true | false +print(runtime.major) --> 5 ``` -### `is_lua53` + -True only on Lua 5.3 runtimes. +### `minor` (`1|2|3|4|5`) + +Minor version number parsed from `version`. ```lua -print(runtime.is_lua53) --> true | false +print(runtime.minor) --> 1 | 2 | 3 | 4 | 5 ``` -### `is_lua54` + -True only on Lua 5.4 runtimes. +### `version` (`501|502|503|504|505`) + +Numeric version encoded as `major * 100 + minor`. ```lua -print(runtime.is_lua54) --> true | false +print(runtime.version) --> 501 | 502 | 503 | 504 | 505 ``` diff --git a/docs/src/modules/set.md b/docs/src/modules/set.md index cad87fb..6265d8b 100644 --- a/docs/src/modules/set.md +++ b/docs/src/modules/set.md @@ -29,50 +29,51 @@ print(s:contains("a")) --> true | [`symmetric_difference_update(set)`](#fn-symmetric-difference-update) | Update the set with elements not shared by both (in place). | | [`update(set)`](#fn-update) | Add all elements from another set (in place). | -**Copying**: - -| Function | Description | -| ----------------------------------------------------- | --------------------------------------------------- | -| [`copy()`](#fn-copy) | Return a shallow copy of the set. | -| [`difference(t)`](#fn-difference) | Return elements in this set but not in another. | -| [`intersection(t)`](#fn-intersection) | Return elements common to both sets. | -| [`remove(v)`](#fn-remove) | Remove an element if present, do nothing otherwise. | -| [`symmetric_difference(t)`](#fn-symmetric-difference) | Return elements not shared by both sets. | -| [`union(t)`](#fn-union) | Return a new set with all elements from both. | - **Predicates**: | Function | Description | | ----------------------------------- | ---------------------------------------------------------------- | -| [`isdisjoint(set)`](#fn-isdisjoint) | Return true if sets have no elements in common. | | [`equals(t)`](#fn-equals) | Return true when both sets contain exactly the same members. | +| [`isdisjoint(set)`](#fn-isdisjoint) | Return true if sets have no elements in common. | | [`isempty()`](#fn-isempty) | Return true if the set has no elements. | | [`issubset(t)`](#fn-issubset) | Return true if all elements of this set are also in another set. | | [`issuperset(t)`](#fn-issuperset) | Return true if this set contains all elements of another set. | -**Query**: +**Queries**: | Function | Description | | ----------------------------- | ----------------------------------------- | | [`contains(v)`](#fn-contains) | Return true if the set contains `v`. | | [`len()`](#fn-len) | Return the number of elements in the set. | -**Transform**: +**Set Operations**: + +| Function | Description | +| ----------------------------------------------------- | --------------------------------------------------- | +| [`copy()`](#fn-copy) | Return a shallow copy of the set. | +| [`difference(t)`](#fn-difference) | Return elements in this set but not in another. | +| [`intersection(t)`](#fn-intersection) | Return elements common to both sets. | +| [`remove(v)`](#fn-remove) | Remove an element if present, do nothing otherwise. | +| [`symmetric_difference(t)`](#fn-symmetric-difference) | Return elements not shared by both sets. | +| [`union(t)`](#fn-union) | Return a new set with all elements from both. | + +**Transforms**: -| Function | Description | -| --------------------------------- | --------------------------------------- | -| [`map(fn)`](#fn-map) | Return a new set by mapping each value. | -| [`values()`](#fn-values) | Return a list of all values in the set. | -| [`tostring()`](#fn-tostring) | Render the set as a string. | -| [`join(sep?, quoted?)`](#fn-join) | Join set values into a string. | +| Function | Description | +| --------------------------------- | ------------------------------------------------------- | +| [`join(sep?, quoted?)`](#fn-join) | Join set values into a string. | +| [`map(fn)`](#fn-map) | Return a new set by mapping each value. | +| [`mirror()`](#fn-mirror) | Mirror values into a new table as both keys and values. | +| [`tostring()`](#fn-tostring) | Render the set as a string. | +| [`values()`](#fn-values) | Return a list of all values in the set. | **Metamethods**: | Function | Description | | ------------------------------ | -------------------------------------------------------------------------- | | [`__add(t)`](#fn-add) | Return the union of two sets using `+`. | -| [`__bor(t)`](#fn-bor) | Return the union of two sets using `\|`. | | [`__band(t)`](#fn-band) | Return the intersection of two sets using `&`. | +| [`__bor(t)`](#fn-bor) | Return the union of two sets using `\|`. | | [`__bxor(t)`](#fn-bxor) | Return elements present in exactly one set using `^`. | | [`__eq(t)`](#fn-eq) | Return true if both sets contain exactly the same members using `==`. | | [`__le(t)`](#fn-le) | Return true if the left set is a subset of the right set using `<=`. | @@ -82,7 +83,7 @@ print(s:contains("a")) --> true ### Mutation -In-place operations that mutate the current set. + #### `add(v)` @@ -216,98 +217,101 @@ Add all elements from another set (in place). s = Set({ "a" }):update(Set({ "b" })) --> s contains "a", "b" ``` -### Copying +### Predicates -Non-mutating set operations that return new set instances. + -#### `copy()` +#### `equals(t)` -Return a shallow copy of the set. +Return true when both sets contain exactly the same members. + +**Parameters**: + +- `t` (`mods.Set|mods.List|table`): Other set/list. **Return**: -- `set` (`mods.Set`): New set. +- `isEqual` (`boolean`): True when both sets contain the same members. **Example**: ```lua -c = Set({ "a" }):copy() --> c is a new set with "a" +a = Set({ "a", "b" }) +b = Set({ "b", "a" }) +ok = a:equals(b) --> true ``` - +> [!NOTE] +> +> `equals` is also available as the `__eq` (`==`) operator. `a:equals(b)` is +> equivalent to `a == b`. -#### `difference(t)` + -Return elements in this set but not in another. +#### `isdisjoint(set)` + +Return true if sets have no elements in common. **Parameters**: -- `t` (`mods.Set|mods.List|table`): Other set/list. +- `set` (`T|mods.List`): Other set/list. **Return**: -- `set` (`mods.Set`): New set. +- `isDisjoint` (`boolean`): True when sets have no elements in common. **Example**: ```lua -d = Set({ "a", "b" }):difference(Set({ "b" })) --> d contains "a" +ok = Set({ "a" }):isdisjoint(Set({ "b" })) --> true ``` -> [!NOTE] -> -> `difference` is also available as the `__sub` (`-`) operator. -> `a:difference(b)` is equivalent to `a - b`. - - - -#### `intersection(t)` - -Return elements common to both sets. + -**Parameters**: +#### `isempty()` -- `t` (`mods.Set|mods.List|table`): Other set/list. +Return true if the set has no elements. **Return**: -- `set` (`mods.Set`): New set. +- `isEmpty` (`boolean`): True when the set has no elements. **Example**: ```lua -i = Set({ "a", "b" }):intersection(Set({ "b", "c" })) --> i contains "b" +empty = Set({}):isempty() --> true ``` -> [!NOTE] -> -> `intersection` is also available as `__band` (`&`) on Lua 5.3+. - - + -#### `remove(v)` +#### `issubset(t)` -Remove an element if present, do nothing otherwise. +Return true if all elements of this set are also in another set. **Parameters**: -- `v` (`any`): Value to remove. +- `t` (`mods.Set|mods.List|table`): Other set/list. **Return**: -- `self` (`T`): Current set. +- `isSubset` (`boolean`): True when every element of `self` exists in `set`. **Example**: ```lua -s = Set({ "a", "b" }):remove("b") --> s contains "a" +ok = Set({ "a" }):issubset(Set({ "a", "b" })) --> true ``` - +> [!NOTE] +> +> `issubset` is also available as the `__le` (`<=`) operator. `a:issubset(b)` is +> equivalent to `a <= b`. -#### `symmetric_difference(t)` + -Return elements not shared by both sets. +#### `issuperset(t)` + +Return true if this set contains all elements of another set. **Parameters**: @@ -315,71 +319,76 @@ Return elements not shared by both sets. **Return**: -- `set` (`mods.Set`): New set. +- `isSuperset` (`boolean`): True when `self` contains every element of `set`. **Example**: ```lua -d = Set({ "a", "b" }):symmetric_difference(Set({ "b", "c" })) ---> d contains "a", "c" +ok = Set({ "a", "b" }):issuperset(Set({ "a" })) --> true ``` -> [!NOTE] -> -> `symmetric_difference` is also available as `__bxor` (`^`) on Lua 5.3+. +### Queries - + -#### `union(t)` +#### `contains(v)` -Return a new set with all elements from both. +Return true if the set contains `v`. **Parameters**: -- `t` (`mods.Set|mods.List|table`): Other set/list. +- `v` (`any`): Value to check. **Return**: -- `set` (`mods.Set`): New set. +- `isPresent` (`boolean`): True when `v` is present in the set. **Example**: ```lua -s = Set({ "a" }):union(Set({ "b" })) --> s contains "a", "b" +ok = Set({ "a", "b" }):contains("a") --> true +ok = Set({ "a", "b" }):contains("z") --> false ``` -> [!NOTE] -> -> `union` is available as `__add` (`+`) and `__bor` (`|`) on Lua 5.3+. -> `a:union(b)` is equivalent to `a + b` and `a | b`. + -### Predicates +#### `len()` -Boolean checks about set relationships and emptiness. +Return the number of elements in the set. -#### `isdisjoint(set)` +**Return**: -Return true if sets have no elements in common. +- `count` (`integer`): Element count. -**Parameters**: +**Example**: -- `set` (`T|mods.List`): Other set/list. +```lua +n = Set({ "a", "b" }):len() --> 2 +``` + +### Set Operations + + + +#### `copy()` + +Return a shallow copy of the set. **Return**: -- `isDisjoint` (`boolean`): True when sets have no elements in common. +- `set` (`mods.Set`): New set. **Example**: ```lua -ok = Set({ "a" }):isdisjoint(Set({ "b" })) --> true +c = Set({ "a" }):copy() --> c is a new set with "a" ``` - + -#### `equals(t)` +#### `difference(t)` -Return true when both sets contain exactly the same members. +Return elements in this set but not in another. **Parameters**: @@ -387,67 +396,68 @@ Return true when both sets contain exactly the same members. **Return**: -- `isEqual` (`boolean`): True when both sets contain the same members. +- `set` (`mods.Set`): New set. **Example**: ```lua -a = Set({ "a", "b" }) -b = Set({ "b", "a" }) -ok = a:equals(b) --> true +d = Set({ "a", "b" }):difference(Set({ "b" })) --> d contains "a" ``` > [!NOTE] > -> `equals` is also available as the `__eq` (`==`) operator. `a:equals(b)` is -> equivalent to `a == b`. +> `difference` is also available as the `__sub` (`-`) operator. +> `a:difference(b)` is equivalent to `a - b`. - + -#### `isempty()` +#### `intersection(t)` -Return true if the set has no elements. +Return elements common to both sets. + +**Parameters**: + +- `t` (`mods.Set|mods.List|table`): Other set/list. **Return**: -- `isEmpty` (`boolean`): True when the set has no elements. +- `set` (`mods.Set`): New set. **Example**: ```lua -empty = Set({}):isempty() --> true +i = Set({ "a", "b" }):intersection(Set({ "b", "c" })) --> i contains "b" ``` - +> [!NOTE] +> +> `intersection` is also available as `__band` (`&`) on Lua 5.3+. -#### `issubset(t)` + -Return true if all elements of this set are also in another set. +#### `remove(v)` + +Remove an element if present, do nothing otherwise. **Parameters**: -- `t` (`mods.Set|mods.List|table`): Other set/list. +- `v` (`any`): Value to remove. **Return**: -- `isSubset` (`boolean`): True when every element of `self` exists in `set`. +- `self` (`T`): Current set. **Example**: ```lua -ok = Set({ "a" }):issubset(Set({ "a", "b" })) --> true +s = Set({ "a", "b" }):remove("b") --> s contains "a" ``` -> [!NOTE] -> -> `issubset` is also available as the `__le` (`<=`) operator. `a:issubset(b)` is -> equivalent to `a <= b`. - - + -#### `issuperset(t)` +#### `symmetric_difference(t)` -Return true if this set contains all elements of another set. +Return elements not shared by both sets. **Parameters**: @@ -455,56 +465,73 @@ Return true if this set contains all elements of another set. **Return**: -- `isSuperset` (`boolean`): True when `self` contains every element of `set`. +- `set` (`mods.Set`): New set. **Example**: ```lua -ok = Set({ "a", "b" }):issuperset(Set({ "a" })) --> true +d = Set({ "a", "b" }):symmetric_difference(Set({ "b", "c" })) +--> d contains "a", "c" ``` -### Query +> [!NOTE] +> +> `symmetric_difference` is also available as `__bxor` (`^`) on Lua 5.3+. -Read-only queries for membership and size. + -#### `contains(v)` +#### `union(t)` -Return true if the set contains `v`. +Return a new set with all elements from both. **Parameters**: -- `v` (`any`): Value to check. +- `t` (`mods.Set|mods.List|table`): Other set/list. **Return**: -- `isPresent` (`boolean`): True when `v` is present in the set. +- `set` (`mods.Set`): New set. **Example**: ```lua -ok = Set({ "a", "b" }):contains("a") --> true -ok = Set({ "a", "b" }):contains("z") --> false +s = Set({ "a" }):union(Set({ "b" })) --> s contains "a", "b" ``` - +> [!NOTE] +> +> `union` is available as `__add` (`+`) and `__bor` (`|`) on Lua 5.3+. +> `a:union(b)` is equivalent to `a + b` and `a | b`. -#### `len()` +### Transforms -Return the number of elements in the set. + + +#### `join(sep?, quoted?)` + +Join set values into a string. + +**Parameters**: + +- `sep?` (`string`): Optional separator value (defaults to `""`). +- `quoted?` (`boolean`): Optional boolean flag (defaults to `false`). **Return**: -- `count` (`integer`): Element count. +- `joined` (`string`): Joined string. **Example**: ```lua -n = Set({ "a", "b" }):len() --> 2 +s = Set({ "b", "a" }):join(", ") --> "a, b" +s = Set({ "b", "a" }):join(", ", true) --> '"a", "b"' ``` -### Transform +> [!NOTE] +> +> Join order is not guaranteed. -Value-to-value transformations and projection helpers. + #### `map(fn)` @@ -524,20 +551,20 @@ Return a new set by mapping each value. s = Set({ 1, 2 }):map(function(v) return v * 10 end) --> s contains 10, 20 ``` - + -#### `values()` +#### `mirror()` -Return a list of all values in the set. +Mirror values into a new table as both keys and values. **Return**: -- `values` (`mods.List`): List of set values. +- `mirroredValues` (`table`): Table mapping each value to itself. **Example**: ```lua -values = Set({ "a", "b" }):values() --> { "a", "b" } +mirrored = Set({ "a", "b" }):mirror() --> { a = "a", b = "b" } ``` @@ -556,32 +583,22 @@ Render the set as a string. s = Set({ "b", "a", 1 }):tostring() --> '{ 1, "a", "b" }' ``` - - -#### `join(sep?, quoted?)` - -Join set values into a string. + -**Parameters**: +#### `values()` -- `sep?` (`string`): Optional separator value (defaults to `""`). -- `quoted?` (`boolean`): Optional boolean flag (defaults to `false`). +Return a list of all values in the set. **Return**: -- `joined` (`string`): Joined string. +- `values` (`mods.List`): List of set values. **Example**: ```lua -s = Set({ "b", "a" }):join(", ") --> "a, b" -s = Set({ "b", "a" }):join(", ", true) --> '"a", "b"' +values = Set({ "a", "b" }):values() --> { "a", "b" } ``` -> [!NOTE] -> -> Join order is not guaranteed. - ### Metamethods @@ -610,11 +627,11 @@ u = a + b --> { a = true, b = true, c = true } > > `__add` is the operator form of `:union(set)`. - + -#### `__bor(t)` +#### `__band(t)` -Return the union of two sets using `|`. +Return the intersection of two sets using `&`. **Parameters**: @@ -629,18 +646,18 @@ Return the union of two sets using `|`. ```lua a = Set({ "a", "b" }) b = Set({ "b", "c" }) -u = a | b --> { a = true, b = true, c = true } +i = a & b --> { b = true } ``` > [!NOTE] > -> `__bor` is the operator form of `:union(set)` on Lua 5.3+. +> `__band` is the operator form of `:intersection(set)` on Lua 5.3+. - + -#### `__band(t)` +#### `__bor(t)` -Return the intersection of two sets using `&`. +Return the union of two sets using `|`. **Parameters**: @@ -655,12 +672,12 @@ Return the intersection of two sets using `&`. ```lua a = Set({ "a", "b" }) b = Set({ "b", "c" }) -i = a & b --> { b = true } +u = a | b --> { a = true, b = true, c = true } ``` > [!NOTE] > -> `__band` is the operator form of `:intersection(set)` on Lua 5.3+. +> `__bor` is the operator form of `:union(set)` on Lua 5.3+. diff --git a/docs/src/modules/str.md b/docs/src/modules/str.md index fa3074c..4f1a88d 100644 --- a/docs/src/modules/str.md +++ b/docs/src/modules/str.md @@ -17,6 +17,17 @@ print(str.capitalize("hello world")) --> "Hello world" ## Functions +**Casing & Transform**: + +| Function | Description | +| -------------------------------------------------------- | --------------------------------------------------------- | +| [`startswith(s, prefix, start?, stop?)`](#fn-startswith) | Return true if string starts with prefix. | +| [`swapcase(s)`](#fn-swapcase) | Return a copy with case of alphabetic characters swapped. | +| [`title(s)`](#fn-title) | Return titlecased copy. | +| [`translate(s, table_map)`](#fn-translate) | Translate characters using a mapping table. | +| [`upper(s)`](#fn-upper) | Return uppercased copy. | +| [`zfill(s, width)`](#fn-zfill) | Pad numeric string on the left with zeros. | + **Formatting**: | Function | Description | @@ -29,6 +40,17 @@ print(str.capitalize("hello world")) --> "Hello world" | [`find(s, sub, start?, stop?)`](#fn-find) | Return lowest index of substring or nil if not found. | | [`format_map(s, mapping)`](#fn-format-map) | Format string with mapping (key-based) replacement. | +**Layout**: + +| Function | Description | +| ----------------------------------------- | ------------------------------------------------------------------- | +| [`join(sep, ls)`](#fn-join) | Join an array-like table of strings using this string as separator. | +| [`ljust(s, width, fillchar?)`](#fn-ljust) | Left-justify string in a field of given width. | +| [`lower(s)`](#fn-lower) | Return lowercased copy. | +| [`lstrip(s, chars?)`](#fn-lstrip) | Remove leading characters (default: whitespace). | +| [`rstrip(s, chars?)`](#fn-rstrip) | Remove trailing characters (default: whitespace). | +| [`strip(s, chars?)`](#fn-strip) | Remove leading and trailing characters (default: whitespace). | + **Predicates**: | Function | Description | @@ -37,26 +59,13 @@ print(str.capitalize("hello world")) --> "Hello world" | [`isalpha(s)`](#fn-isalpha) | Return true if all characters are alphabetic and string is non-empty. | | [`isascii(s)`](#fn-isascii) | Return true if all characters are ASCII. | | [`isdecimal(s)`](#fn-isdecimal) | Return true if all characters are decimal characters and string is non-empty. | -| [`isdigit(s)`](#fn-isdigit) | Return true if all characters are digits and string is non-empty. | | [`isidentifier(s)`](#fn-isidentifier) | Return true if string is a valid identifier and not a reserved keyword. | | [`islower(s)`](#fn-islower) | Return true if all cased characters are lowercase and there is at least one cased character. | -| [`isnumeric(s)`](#fn-isnumeric) | Return true if all characters are numeric and string is non-empty. | | [`isprintable(s)`](#fn-isprintable) | Return true if all characters are printable. | | [`isspace(s)`](#fn-isspace) | Return true if all characters are whitespace and string is non-empty. | | [`istitle(s)`](#fn-istitle) | Return true if string is titlecased. | | [`isupper(s)`](#fn-isupper) | Return true if all cased characters are uppercase and there is at least one cased character. | -**Layout**: - -| Function | Description | -| ----------------------------------------- | ------------------------------------------------------------------- | -| [`join(sep, ls)`](#fn-join) | Join an array-like table of strings using this string as separator. | -| [`ljust(s, width, fillchar?)`](#fn-ljust) | Left-justify string in a field of given width. | -| [`lower(s)`](#fn-lower) | Return lowercased copy. | -| [`lstrip(s, chars?)`](#fn-lstrip) | Remove leading characters (default: whitespace). | -| [`rstrip(s, chars?)`](#fn-rstrip) | Remove trailing characters (default: whitespace). | -| [`strip(s, chars?)`](#fn-strip) | Remove leading and trailing characters (default: whitespace). | - **Split & Replace**: | Function | Description | @@ -73,16 +82,137 @@ print(str.capitalize("hello world")) --> "Hello world" | [`split(s, sep?, maxsplit?)`](#fn-split) | Split by separator (or whitespace) up to maxsplit. | | [`splitlines(s, keepends?)`](#fn-splitlines) | Split on line boundaries. | -**Casing & Transform**: +### Casing & Transform -| Function | Description | -| -------------------------------------------------------- | --------------------------------------------------------- | -| [`swapcase(s)`](#fn-swapcase) | Return a copy with case of alphabetic characters swapped. | -| [`startswith(s, prefix, start?, stop?)`](#fn-startswith) | Return true if string starts with prefix. | -| [`title(s)`](#fn-title) | Return titlecased copy. | -| [`translate(s, table_map)`](#fn-translate) | Translate characters using a mapping table. | -| [`upper(s)`](#fn-upper) | Return uppercased copy. | -| [`zfill(s, width)`](#fn-zfill) | Pad numeric string on the left with zeros. | + + +#### `startswith(s, prefix, start?, stop?)` + +Return true if string starts with prefix. + +**Parameters**: + +- `s` (`string`): Input string. +- `prefix` (`string|string[]`): Prefix string. +- `start?` (`integer`): Optional start index (defaults to `1`). +- `stop?` (`integer`): Optional exclusive end index (defaults to `#s + 1`). + +**Return**: + +- `hasPrefix` (`boolean`): True when `s` starts with `prefix`. + +**Example**: + +```lua +ok = startswith("hello.lua", "he") --> true +``` + +> [!NOTE] +> +> If prefix is a list, returns `true` when any prefix matches. + + + +#### `swapcase(s)` + +Return a copy with case of alphabetic characters swapped. + +**Parameters**: + +- `s` (`string`): Input string. + +**Return**: + +- `swappedCase` (`string`): String with alphabetic case swapped. + +**Example**: + +```lua +s = swapcase("AbC") --> "aBc" +``` + + + +#### `title(s)` + +Return titlecased copy. + +**Parameters**: + +- `s` (`string`): Input string. + +**Return**: + +- `titlecased` (`string`): Titlecased string. + +**Example**: + +```lua +s = title("hello world") --> "Hello World" +``` + + + +#### `translate(s, table_map)` + +Translate characters using a mapping table. + +**Parameters**: + +- `s` (`string`): Input string. +- `table_map` (`table`): Character translation map. + +**Return**: + +- `translated` (`string`): Translated string. + +**Example**: + +```lua +map = { [string.byte("a")] = "b", ["c"] = false } +s = translate("abc", map) --> "bb" +``` + + + +#### `upper(s)` + +Return uppercased copy. + +**Parameters**: + +- `s` (`string`): Input string. + +**Return**: + +- `uppercased` (`string`): Uppercased string. + +**Example**: + +```lua +s = upper("Hello") --> "HELLO" +``` + + + +#### `zfill(s, width)` + +Pad numeric string on the left with zeros. + +**Parameters**: + +- `s` (`string`): Input string. +- `width` (`integer`): Target width. + +**Return**: + +- `zeroFilled` (`string`): Zero-padded string. + +**Example**: + +```lua +s = zfill("42", 5) --> "00042" +``` ### Formatting @@ -250,95 +380,56 @@ s = format_map("hi {name}", { name = "bob" }) --> "hi bob" > `format_map` is a lightweight `{key}` replacement helper. For richer > templating, use [`mods.template`](/modules/template). -### Predicates - - - -#### `isalnum(s)` - -Return true if all characters are alphanumeric and string is non-empty. - -**Parameters**: - -- `s` (`string`): Input string. - -**Return**: - -- `isAlnum` (`boolean`): True when `s` is non-empty and all characters are - alphanumeric. - -**Example**: - -```lua -ok = isalnum("abc123") --> true -``` - -> [!NOTE] -> -> Lua letters are ASCII by default, so non-ASCII letters are not alphanumeric. -> -> ```lua -> isalnum("á1") --> false -> ``` +### Layout - + -#### `isalpha(s)` +#### `join(sep, ls)` -Return true if all characters are alphabetic and string is non-empty. +Join an array-like table of strings using this string as separator. **Parameters**: -- `s` (`string`): Input string. +- `sep` (`string`): Separator value. +- `ls` (`string[]`): Table value. **Return**: -- `isAlpha` (`boolean`): True when `s` is non-empty and all characters are - alphabetic. +- `joined` (`string`): Joined string. **Example**: ```lua -ok = isalpha("abc") --> true +s = join(",", { "a", "b", "c" }) --> "a,b,c" ``` -> [!NOTE] -> -> Lua letters are ASCII by default, so non-ASCII letters are not alphabetic. -> -> ```lua -> isalpha("á") --> false -> ``` - - + -#### `isascii(s)` +#### `ljust(s, width, fillchar?)` -Return true if all characters are ASCII. +Left-justify string in a field of given width. **Parameters**: - `s` (`string`): Input string. +- `width` (`integer`): Target width. +- `fillchar?` (`string`): Optional fill character. **Return**: -- `isAscii` (`boolean`): True when all bytes in `s` are ASCII. +- `leftJustified` (`string`): Left-justified string. **Example**: ```lua -ok = isascii("hello") --> true +s = ljust("hi", 5, ".") --> "hi..." ``` -> [!NOTE] -> -> The empty string returns `true`. - - + -#### `isdecimal(s)` +#### `lower(s)` -Return true if all characters are decimal characters and string is non-empty. +Return lowercased copy. **Parameters**: @@ -346,107 +437,84 @@ Return true if all characters are decimal characters and string is non-empty. **Return**: -- `isDecimal` (`boolean`): True when `s` is non-empty and all characters are - decimal digits. +- `lowercased` (`string`): Lowercased string. **Example**: ```lua -ok = isdecimal("123") --> true +s = lower("HeLLo") --> "hello" ``` - + -#### `isdigit(s)` +#### `lstrip(s, chars?)` -Return true if all characters are digits and string is non-empty. +Remove leading characters (default: whitespace). **Parameters**: - `s` (`string`): Input string. +- `chars?` (`string`): Optional character set. **Return**: -- `isDigit` (`boolean`): True when `s` is non-empty and all characters are - digits. +- `leadingStripped` (`string`): String with leading characters removed. **Example**: ```lua -ok = isdigit("123") --> true +s = lstrip(" hello") --> "hello" ``` - + -#### `isidentifier(s)` +#### `rstrip(s, chars?)` -Return true if string is a valid identifier and not a reserved keyword. +Remove trailing characters (default: whitespace). **Parameters**: - `s` (`string`): Input string. +- `chars?` (`string`): Optional character set. **Return**: -- `isIdentifier` (`boolean`): True when `s` is a valid identifier and not a - keyword. +- `trailingStripped` (`string`): String with trailing characters removed. **Example**: ```lua -ok = isidentifier("foo_bar") --> true -ok = isidentifier("2var") --> false -ok = isidentifier("end") --> false (keyword) +s = rstrip("hello ") --> "hello" ``` - + -#### `islower(s)` +#### `strip(s, chars?)` -Return true if all cased characters are lowercase and there is at least one -cased character. +Remove leading and trailing characters (default: whitespace). **Parameters**: - `s` (`string`): Input string. +- `chars?` (`string`): Optional character set. **Return**: -- `isLower` (`boolean`): True when `s` has at least one cased character and all - are lowercase. +- `stripped` (`string`): String with leading and trailing characters removed. **Example**: ```lua -ok = islower("hello") --> true +s = strip(" hello ") --> "hello" ``` - - -#### `isnumeric(s)` - -Return true if all characters are numeric and string is non-empty. - -**Parameters**: - -- `s` (`string`): Input string. - -**Return**: - -- `isNumeric` (`boolean`): True when `s` is non-empty and all characters are - numeric. - -**Example**: - -```lua -ok = isnumeric("123") --> true -``` +### Predicates - + -#### `isprintable(s)` +#### `isalnum(s)` -Return true if all characters are printable. +Return true if all characters are alphanumeric and string is non-empty. **Parameters**: @@ -454,23 +522,28 @@ Return true if all characters are printable. **Return**: -- `isPrintable` (`boolean`): True when all bytes in `s` are printable ASCII. +- `isAlnum` (`boolean`): True when `s` is non-empty and all characters are + alphanumeric. **Example**: ```lua -ok = isprintable("abc!") --> true +ok = isalnum("abc123") --> true ``` > [!NOTE] > -> The empty string returns `true`. +> Lua letters are ASCII by default, so non-ASCII letters are not alphanumeric. +> +> ```lua +> isalnum("á1") --> false +> ``` - + -#### `isspace(s)` +#### `isalpha(s)` -Return true if all characters are whitespace and string is non-empty. +Return true if all characters are alphabetic and string is non-empty. **Parameters**: @@ -478,20 +551,28 @@ Return true if all characters are whitespace and string is non-empty. **Return**: -- `isSpace` (`boolean`): True when `s` is non-empty and all characters are - whitespace. +- `isAlpha` (`boolean`): True when `s` is non-empty and all characters are + alphabetic. **Example**: ```lua -ok = isspace(" \t") --> true +ok = isalpha("abc") --> true ``` - +> [!NOTE] +> +> Lua letters are ASCII by default, so non-ASCII letters are not alphabetic. +> +> ```lua +> isalpha("á") --> false +> ``` -#### `istitle(s)` + -Return true if string is titlecased. +#### `isascii(s)` + +Return true if all characters are ASCII. **Parameters**: @@ -499,20 +580,23 @@ Return true if string is titlecased. **Return**: -- `isTitle` (`boolean`): True when `s` is titlecased. +- `isAscii` (`boolean`): True when all bytes in `s` are ASCII. **Example**: ```lua -ok = istitle("Hello World") --> true +ok = isascii("hello") --> true ``` - +> [!NOTE] +> +> The empty string returns `true`. -#### `isupper(s)` + -Return true if all cased characters are uppercase and there is at least one -cased character. +#### `isdecimal(s)` + +Return true if all characters are decimal characters and string is non-empty. **Parameters**: @@ -520,65 +604,65 @@ cased character. **Return**: -- `isUpper` (`boolean`): True when `s` has at least one cased character and all - are uppercase. +- `isDecimal` (`boolean`): True when `s` is non-empty and all characters are + decimal digits. **Example**: ```lua -ok = isupper("HELLO") --> true +ok = isdecimal("123") --> true ``` -### Layout - - + -#### `join(sep, ls)` +#### `isidentifier(s)` -Join an array-like table of strings using this string as separator. +Return true if string is a valid identifier and not a reserved keyword. **Parameters**: -- `sep` (`string`): Separator value. -- `ls` (`string[]`): Table value. +- `s` (`string`): Input string. **Return**: -- `joined` (`string`): Joined string. +- `isIdentifier` (`boolean`): True when `s` is a valid identifier and not a + keyword. **Example**: ```lua -s = join(",", { "a", "b", "c" }) --> "a,b,c" +ok = isidentifier("foo_bar") --> true +ok = isidentifier("2var") --> false +ok = isidentifier("end") --> false (keyword) ``` - + -#### `ljust(s, width, fillchar?)` +#### `islower(s)` -Left-justify string in a field of given width. +Return true if all cased characters are lowercase and there is at least one +cased character. **Parameters**: - `s` (`string`): Input string. -- `width` (`integer`): Target width. -- `fillchar?` (`string`): Optional fill character. **Return**: -- `leftJustified` (`string`): Left-justified string. +- `isLower` (`boolean`): True when `s` has at least one cased character and all + are lowercase. **Example**: ```lua -s = ljust("hi", 5, ".") --> "hi..." +ok = islower("hello") --> true ``` - + -#### `lower(s)` +#### `isprintable(s)` -Return lowercased copy. +Return true if all characters are printable. **Parameters**: @@ -586,75 +670,79 @@ Return lowercased copy. **Return**: -- `lowercased` (`string`): Lowercased string. +- `isPrintable` (`boolean`): True when all bytes in `s` are printable ASCII. **Example**: ```lua -s = lower("HeLLo") --> "hello" +ok = isprintable("abc!") --> true ``` - +> [!NOTE] +> +> The empty string returns `true`. -#### `lstrip(s, chars?)` + -Remove leading characters (default: whitespace). +#### `isspace(s)` + +Return true if all characters are whitespace and string is non-empty. **Parameters**: - `s` (`string`): Input string. -- `chars?` (`string`): Optional character set. **Return**: -- `leadingStripped` (`string`): String with leading characters removed. +- `isSpace` (`boolean`): True when `s` is non-empty and all characters are + whitespace. **Example**: ```lua -s = lstrip(" hello") --> "hello" +ok = isspace(" \t") --> true ``` - + -#### `rstrip(s, chars?)` +#### `istitle(s)` -Remove trailing characters (default: whitespace). +Return true if string is titlecased. **Parameters**: - `s` (`string`): Input string. -- `chars?` (`string`): Optional character set. **Return**: -- `trailingStripped` (`string`): String with trailing characters removed. +- `isTitle` (`boolean`): True when `s` is titlecased. **Example**: ```lua -s = rstrip("hello ") --> "hello" +ok = istitle("Hello World") --> true ``` - + -#### `strip(s, chars?)` +#### `isupper(s)` -Remove leading and trailing characters (default: whitespace). +Return true if all cased characters are uppercase and there is at least one +cased character. **Parameters**: - `s` (`string`): Input string. -- `chars?` (`string`): Optional character set. **Return**: -- `stripped` (`string`): String with leading and trailing characters removed. +- `isUpper` (`boolean`): True when `s` has at least one cased character and all + are uppercase. **Example**: ```lua -s = strip(" hello ") --> "hello" +ok = isupper("HELLO") --> true ``` ### Split & Replace @@ -902,135 +990,3 @@ Split on line boundaries. ```lua lines = splitlines("a\nb\r\nc") --> { "a", "b", "c" } ``` - -### Casing & Transform - - - -#### `swapcase(s)` - -Return a copy with case of alphabetic characters swapped. - -**Parameters**: - -- `s` (`string`): Input string. - -**Return**: - -- `swappedCase` (`string`): String with alphabetic case swapped. - -**Example**: - -```lua -s = swapcase("AbC") --> "aBc" -``` - - - -#### `startswith(s, prefix, start?, stop?)` - -Return true if string starts with prefix. - -**Parameters**: - -- `s` (`string`): Input string. -- `prefix` (`string|string[]`): Prefix string. -- `start?` (`integer`): Optional start index (defaults to `1`). -- `stop?` (`integer`): Optional exclusive end index (defaults to `#s + 1`). - -**Return**: - -- `hasPrefix` (`boolean`): True when `s` starts with `prefix`. - -**Example**: - -```lua -ok = startswith("hello.lua", "he") --> true -``` - -> [!NOTE] -> -> If prefix is a list, returns `true` when any prefix matches. - - - -#### `title(s)` - -Return titlecased copy. - -**Parameters**: - -- `s` (`string`): Input string. - -**Return**: - -- `titlecased` (`string`): Titlecased string. - -**Example**: - -```lua -s = title("hello world") --> "Hello World" -``` - - - -#### `translate(s, table_map)` - -Translate characters using a mapping table. - -**Parameters**: - -- `s` (`string`): Input string. -- `table_map` (`table`): Character translation map. - -**Return**: - -- `translated` (`string`): Translated string. - -**Example**: - -```lua -map = { [string.byte("a")] = "b", ["c"] = false } -s = translate("abc", map) --> "bb" -``` - - - -#### `upper(s)` - -Return uppercased copy. - -**Parameters**: - -- `s` (`string`): Input string. - -**Return**: - -- `uppercased` (`string`): Uppercased string. - -**Example**: - -```lua -s = upper("Hello") --> "HELLO" -``` - - - -#### `zfill(s, width)` - -Pad numeric string on the left with zeros. - -**Parameters**: - -- `s` (`string`): Input string. -- `width` (`integer`): Target width. - -**Return**: - -- `zeroFilled` (`string`): Zero-padded string. - -**Example**: - -```lua -s = zfill("42", 5) --> "00042" -``` diff --git a/docs/src/modules/stringcase.md b/docs/src/modules/stringcase.md index 68aa650..e0b7790 100644 --- a/docs/src/modules/stringcase.md +++ b/docs/src/modules/stringcase.md @@ -23,29 +23,29 @@ print(stringcase.snake("FooBar")) --> "foo_bar" | [`lower(s)`](#fn-lower) | Convert string to all lowercase. | | [`upper(s)`](#fn-upper) | Convert string to all uppercase. | +**Letter Case**: + +| Function | Description | +| ----------------------------- | ------------------------------------------------------------------------- | +| [`capital(s)`](#fn-capital) | Capitalize the first letter and lowercase the rest. | +| [`sentence(s)`](#fn-sentence) | Convert string to sentence case (first letter uppercase, rest unchanged). | +| [`swap(s)`](#fn-swap) | Swap case of each letter. | + **Word Case**: | Function | Description | | --------------------------------- | --------------------------------------------------------------------- | -| [`snake(s)`](#fn-snake) | Convert string to snake_case. | -| [`camel(s)`](#fn-camel) | Convert string to camelCase. | -| [`replace(s, sep?)`](#fn-replace) | Normalize to snake_case, then replace underscores with a separator. | | [`acronym(s)`](#fn-acronym) | Get acronym of words in string (first letters only). | -| [`title(s)`](#fn-title) | Convert string to Title Case (first letter of each word capitalized). | +| [`camel(s)`](#fn-camel) | Convert string to camelCase. | | [`constant(s)`](#fn-constant) | Convert string to CONSTANT_CASE (uppercase snake_case). | -| [`pascal(s)`](#fn-pascal) | Convert string to PascalCase. | -| [`kebab(s)`](#fn-kebab) | Convert string to kebab-case. | | [`dot(s)`](#fn-dot) | Convert string to dot.case. | -| [`space(s)`](#fn-space) | Convert string to space case (spaces between words). | +| [`kebab(s)`](#fn-kebab) | Convert string to kebab-case. | +| [`pascal(s)`](#fn-pascal) | Convert string to PascalCase. | | [`path(s)`](#fn-path) | Convert string to path/case (slashes between words). | - -**Letter Case**: - -| Function | Description | -| ----------------------------- | ------------------------------------------------------------------------- | -| [`swap(s)`](#fn-swap) | Swap case of each letter. | -| [`capital(s)`](#fn-capital) | Capitalize the first letter and lowercase the rest. | -| [`sentence(s)`](#fn-sentence) | Convert string to sentence case (first letter uppercase, rest unchanged). | +| [`replace(s, sep?)`](#fn-replace) | Normalize to snake_case, then replace underscores with a separator. | +| [`snake(s)`](#fn-snake) | Convert string to snake_case. | +| [`space(s)`](#fn-space) | Convert string to space case (spaces between words). | +| [`title(s)`](#fn-title) | Convert string to Title Case (first letter of each word capitalized). | ### Basic @@ -91,13 +91,13 @@ upper("foo_bar-baz") --> "FOO_BAR-BAZ" upper("FooBar baz") --> "FOOBAR BAZ" ``` -### Word Case +### Letter Case - + -#### `snake(s)` +#### `capital(s)` -Convert string to snake_case. +Capitalize the first letter and lowercase the rest. **Parameters**: @@ -105,20 +105,20 @@ Convert string to snake_case. **Return**: -- `snakeCased` (`string`): Snake-cased string. +- `capitalized` (`string`): Capitalized string. **Example**: ```lua -snake("foo_bar-baz") --> "foo_bar_baz" -snake("FooBar baz") --> "foo_bar_baz" +capital("foo_bar-baz") --> "Foo_bar-baz" +capital("FooBar baz") --> "Foobar baz" ``` - + -#### `camel(s)` +#### `sentence(s)` -Convert string to camelCase. +Convert string to sentence case (first letter uppercase, rest unchanged). **Parameters**: @@ -126,37 +126,38 @@ Convert string to camelCase. **Return**: -- `camelCased` (`string`): Camel-cased string. +- `sentenceCased` (`string`): Sentence-cased string. **Example**: ```lua -camel("foo_bar-baz") --> "fooBarBaz" -camel("FooBar baz") --> "fooBarBaz" +sentence("foo_bar-baz") --> "Foo_bar-baz" +sentence("FooBar baz") --> "FooBar baz" ``` - + -#### `replace(s, sep?)` +#### `swap(s)` -Normalize to snake_case, then replace underscores with a separator. +Swap case of each letter. **Parameters**: - `s` (`string`): Input string. -- `sep?` (`string`): Optional separator value (defaults to `""`). **Return**: -- `replaced` (`string`): String with underscores replaced by `sep`. +- `swapCased` (`string`): Swap-cased string. **Example**: ```lua -replace("foo_bar-baz", "-") --> "foo-bar-baz" -replace("FooBar baz", "-") --> "foo-bar-baz" +swap("foo_bar-baz") --> "FOO_BAR-BAZ" +swap("FooBar baz") --> "fOObAR BAZ" ``` +### Word Case + #### `acronym(s)` @@ -178,11 +179,11 @@ acronym("foo_bar-baz") --> "FBB" acronym("FooBar baz") --> "FBB" ``` - + -#### `title(s)` +#### `camel(s)` -Convert string to Title Case (first letter of each word capitalized). +Convert string to camelCase. **Parameters**: @@ -190,13 +191,13 @@ Convert string to Title Case (first letter of each word capitalized). **Return**: -- `titleCased` (`string`): Title-cased string. +- `camelCased` (`string`): Camel-cased string. **Example**: ```lua -title("foo_bar-baz") --> "Foo Bar Baz" -title("FooBar baz") --> "Foo Bar Baz" +camel("foo_bar-baz") --> "fooBarBaz" +camel("FooBar baz") --> "fooBarBaz" ``` @@ -220,11 +221,11 @@ constant("foo_bar-baz") --> "FOO_BAR_BAZ" constant("FooBar baz") --> "FOO_BAR_BAZ" ``` - + -#### `pascal(s)` +#### `dot(s)` -Convert string to PascalCase. +Convert string to dot.case. **Parameters**: @@ -232,13 +233,13 @@ Convert string to PascalCase. **Return**: -- `pascalCased` (`string`): Pascal-cased string. +- `dotCased` (`string`): Dot-cased string. **Example**: ```lua -pascal("foo_bar-baz") --> "FooBarBaz" -pascal("FooBar baz") --> "FooBarBaz" +dot("foo_bar-baz") --> "foo.bar.baz" +dot("FooBar baz") --> "foo.bar.baz" ``` @@ -262,11 +263,11 @@ kebab("foo_bar-baz") --> "foo-bar-baz" kebab("FooBar baz") --> "foo-bar-baz" ``` - + -#### `dot(s)` +#### `pascal(s)` -Convert string to dot.case. +Convert string to PascalCase. **Parameters**: @@ -274,20 +275,20 @@ Convert string to dot.case. **Return**: -- `dotCased` (`string`): Dot-cased string. +- `pascalCased` (`string`): Pascal-cased string. **Example**: ```lua -dot("foo_bar-baz") --> "foo.bar.baz" -dot("FooBar baz") --> "foo.bar.baz" +pascal("foo_bar-baz") --> "FooBarBaz" +pascal("FooBar baz") --> "FooBarBaz" ``` - + -#### `space(s)` +#### `path(s)` -Convert string to space case (spaces between words). +Convert string to path/case (slashes between words). **Parameters**: @@ -295,43 +296,42 @@ Convert string to space case (spaces between words). **Return**: -- `spaceCased` (`string`): Space-cased string. +- `pathCased` (`string`): Path-cased string. **Example**: ```lua -space("foo_bar-baz") --> "foo bar baz" -space("FooBar baz") --> "foo bar baz" +path("foo_bar-baz") --> "foo/bar/baz" +path("FooBar baz") --> "foo/bar/baz" ``` - + -#### `path(s)` +#### `replace(s, sep?)` -Convert string to path/case (slashes between words). +Normalize to snake_case, then replace underscores with a separator. **Parameters**: - `s` (`string`): Input string. +- `sep?` (`string`): Optional separator value (defaults to `""`). **Return**: -- `pathCased` (`string`): Path-cased string. +- `replaced` (`string`): String with underscores replaced by `sep`. **Example**: ```lua -path("foo_bar-baz") --> "foo/bar/baz" -path("FooBar baz") --> "foo/bar/baz" +replace("foo_bar-baz", "-") --> "foo-bar-baz" +replace("FooBar baz", "-") --> "foo-bar-baz" ``` -### Letter Case - - + -#### `swap(s)` +#### `snake(s)` -Swap case of each letter. +Convert string to snake_case. **Parameters**: @@ -339,20 +339,20 @@ Swap case of each letter. **Return**: -- `swapCased` (`string`): Swap-cased string. +- `snakeCased` (`string`): Snake-cased string. **Example**: ```lua -swap("foo_bar-baz") --> "FOO_BAR-BAZ" -swap("FooBar baz") --> "fOObAR BAZ" +snake("foo_bar-baz") --> "foo_bar_baz" +snake("FooBar baz") --> "foo_bar_baz" ``` - + -#### `capital(s)` +#### `space(s)` -Capitalize the first letter and lowercase the rest. +Convert string to space case (spaces between words). **Parameters**: @@ -360,20 +360,20 @@ Capitalize the first letter and lowercase the rest. **Return**: -- `capitalized` (`string`): Capitalized string. +- `spaceCased` (`string`): Space-cased string. **Example**: ```lua -capital("foo_bar-baz") --> "Foo_bar-baz" -capital("FooBar baz") --> "Foobar baz" +space("foo_bar-baz") --> "foo bar baz" +space("FooBar baz") --> "foo bar baz" ``` - + -#### `sentence(s)` +#### `title(s)` -Convert string to sentence case (first letter uppercase, rest unchanged). +Convert string to Title Case (first letter of each word capitalized). **Parameters**: @@ -381,11 +381,11 @@ Convert string to sentence case (first letter uppercase, rest unchanged). **Return**: -- `sentenceCased` (`string`): Sentence-cased string. +- `titleCased` (`string`): Title-cased string. **Example**: ```lua -sentence("foo_bar-baz") --> "Foo_bar-baz" -sentence("FooBar baz") --> "FooBar baz" +title("foo_bar-baz") --> "Foo Bar Baz" +title("FooBar baz") --> "Foo Bar Baz" ``` diff --git a/docs/src/modules/tbl.md b/docs/src/modules/tbl.md index 4db7bbe..b70dcdd 100644 --- a/docs/src/modules/tbl.md +++ b/docs/src/modules/tbl.md @@ -17,29 +17,37 @@ print(tbl.count({ a = 1, b = 2 })) --> 2 ## Functions -**Basics**: +**Copies**: + +| Function | Description | +| ----------------------------- | ----------------------------------- | +| [`copy(t)`](#fn-copy) | Create a shallow copy of the table. | +| [`deepcopy(v)`](#fn-deepcopy) | Create a deep copy of a value. | + +**Core Utilities**: | Function | Description | | ----------------------- | --------------------------------------- | | [`clear(t)`](#fn-clear) | Remove all entries from the table. | | [`count(t)`](#fn-count) | Return the number of keys in the table. | -**Copying**: +**Iterators**: -| Function | Description | -| ----------------------------- | ----------------------------------- | -| [`copy(t)`](#fn-copy) | Create a shallow copy of the table. | -| [`deepcopy(v)`](#fn-deepcopy) | Create a deep copy of a value. | +| Function | Description | +| ------------------------------- | -------------------------------------------- | +| [`foreach(t, fn)`](#fn-foreach) | Call a function for each value in the table. | +| [`spairs(t)`](#fn-spairs) | Iterate key-value pairs in sorted key order. | -**Query**: +**Queries**: -| Function | Description | -| --------------------------------- | ---------------------------------------------------------------- | -| [`filter(t, pred)`](#fn-filter) | Filter entries by a value predicate. | -| [`find(t, v)`](#fn-find) | Find the first key whose value equals the given value. | -| [`same(a, b)`](#fn-same) | Return `true` if two tables have the same keys and equal values. | -| [`find_if(t, pred)`](#fn-find-if) | Find first value and key matching predicate. | -| [`get(t, ...)`](#fn-get) | Safely get nested value by keys. | +| Function | Description | +| ------------------------------------ | ---------------------------------------------------------------- | +| [`deep_equal(a, b)`](#fn-deep-equal) | Return `true` if two tables are deeply equal. | +| [`filter(t, pred)`](#fn-filter) | Filter entries by a value predicate. | +| [`find(t, v)`](#fn-find) | Find the first key whose value equals the given value. | +| [`find_if(t, pred)`](#fn-find-if) | Find first value and key matching predicate. | +| [`get(t, ...)`](#fn-get) | Safely get nested value by keys. | +| [`is_same(a, b)`](#fn-is-same) | Return `true` if two tables have the same keys and equal values. | **Transforms**: @@ -53,16 +61,57 @@ print(tbl.count({ a = 1, b = 2 })) --> 2 | [`update(t1, t2)`](#fn-update) | Merge entries from `t2` into `t1` and return `t1`. | | [`values(t)`](#fn-values) | Return a list of all values in the table. | -**Iteration**: +### Copies -| Function | Description | -| ------------------------------- | -------------------------------------------- | -| [`foreach(t, fn)`](#fn-foreach) | Call a function for each value in the table. | -| [`spairs(t)`](#fn-spairs) | Iterate key-value pairs in sorted key order. | + + +#### `copy(t)` + +Create a shallow copy of the table. -### Basics +**Parameters**: -Core table utilities for clearing and counting. +- `t` (`T`): Source table. + +**Return**: + +- `copy` (`T`): Shallow-copied table. + +**Example**: + +```lua +t = copy({ a = 1, b = 2 }) --> { a = 1, b = 2 } +``` + + + +#### `deepcopy(v)` + +Create a deep copy of a value. + +**Parameters**: + +- `v` (`T`): Input value. + +**Return**: + +- `copiedValue` (`T`): Deep-copied value. + +**Example**: + +```lua +t = deepcopy({ a = { b = 1 } }) --> { a = { b = 1 } } +n = deepcopy(42) --> 42 +``` + +> [!NOTE] +> +> If `v` is a table, all nested tables are copied recursively; other types are +> returned as-is. + +### Core Utilities + + #### `clear(t)` @@ -103,57 +152,80 @@ Return the number of keys in the table. n = count({ a = 1, b = 2 }) --> 2 ``` -### Copying +### Iterators -Shallow and deep copy helpers. + -#### `copy(t)` +#### `foreach(t, fn)` -Create a shallow copy of the table. +Call a function for each value in the table. **Parameters**: -- `t` (`T`): Source table. +- `t` (`table`): Input table. +- `fn` (`fun(value:V, key:K)`): Function invoked for each entry. **Return**: -- `copy` (`T`): Shallow-copied table. +- `none` (`nil`) **Example**: ```lua -t = copy({ a = 1, b = 2 }) --> { a = 1, b = 2 } +foreach({ a = 1, b = 2 }, function(v, k) + print(k, v) +end) ``` - + -#### `deepcopy(v)` +#### `spairs(t)` -Create a deep copy of a value. +Iterate key-value pairs in sorted key order. **Parameters**: -- `v` (`T`): Input value. +- `t` (`T`): Input table. **Return**: -- `copiedValue` (`T`): Deep-copied value. +- `iterator` (`fun(table: table, index?: K):(K, V)`): Sorted pairs + iterator. +- **value** (`T`) **Example**: ```lua -t = deepcopy({ a = { b = 1 } }) --> { a = { b = 1 } } -n = deepcopy(42) --> 42 +for k, v in spairs({ b = 2, a = 1 }) do + print(k, v) +end ``` -> [!NOTE] -> -> If `v` is a table, all nested tables are copied recursively; other types are -> returned as-is. +### Queries + + -### Query +#### `deep_equal(a, b)` -Read-only lookup and selection helpers. +Return `true` if two tables are deeply equal. + +**Parameters**: + +- `a` (`table`): Left table. +- `b` (`table`): Right table. + +**Return**: + +- `isDeepEqual` (`boolean`): True when both tables are recursively equal. + +**Example**: + +```lua +ok = deep_equal({ a = { b = 1 } }, { a = { b = 1 } }) --> true +ok = deep_equal({ a = { b = 1 } }, { a = { b = 2 } }) --> false +``` + + #### `filter(t, pred)` @@ -197,28 +269,6 @@ Find the first key whose value equals the given value. key = find({ a = 1, b = 2, c = 2 }, 2) --> "b" or "c" ``` - - -#### `same(a, b)` - -Return `true` if two tables have the same keys and equal values. - -**Parameters**: - -- `a` (`table`): Left table. -- `b` (`table`): Right table. - -**Return**: - -- `isSame` (`boolean`): True when both tables have the same keys and values. - -**Example**: - -```lua -ok = same({ a = 1, b = 2 }, { b = 2, a = 1 }) --> true -ok = same({ a = {} }, { a = {} }) --> false -``` - #### `find_if(t, pred)` @@ -270,9 +320,31 @@ v2 = get(t) --> { a = { b = { c = 1 } } } > > If no keys are provided, returns the input table. + + +#### `is_same(a, b)` + +Return `true` if two tables have the same keys and equal values. + +**Parameters**: + +- `a` (`table`): Left table. +- `b` (`table`): Right table. + +**Return**: + +- `isSame` (`boolean`): True when both tables have the same keys and values. + +**Example**: + +```lua +ok = is_same({ a = 1, b = 2 }, { b = 2, a = 1 }) --> true +ok = is_same({ a = {} }, { a = {} }) --> false +``` + ### Transforms -Table transformation and conversion utilities. + #### `invert(t)` @@ -423,52 +495,3 @@ Return a list of all values in the table. ```lua vals = values({ a = 1, b = 2 }) --> { 1, 2 } ``` - -### Iteration - -Iterators and ordered traversal helpers. - -#### `foreach(t, fn)` - -Call a function for each value in the table. - -**Parameters**: - -- `t` (`table`): Input table. -- `fn` (`fun(value:V, key:K)`): Function invoked for each entry. - -**Return**: - -- `none` (`nil`) - -**Example**: - -```lua -foreach({ a = 1, b = 2 }, function(v, k) - print(k, v) -end) -``` - - - -#### `spairs(t)` - -Iterate key-value pairs in sorted key order. - -**Parameters**: - -- `t` (`T`): Input table. - -**Return**: - -- `iterator` (`fun(table: table, index?: K):(K, V)`): Sorted pairs - iterator. -- **value** (`T`) - -**Example**: - -```lua -for k, v in spairs({ b = 2, a = 1 }) do - print(k, v) -end -``` diff --git a/docs/src/modules/utils.md b/docs/src/modules/utils.md index e7fa93e..aefed80 100644 --- a/docs/src/modules/utils.md +++ b/docs/src/modules/utils.md @@ -16,40 +16,57 @@ print(utils.quote('hello "world"')) --> 'hello "world"' ## Functions -| Function | Description | -| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| [`quote(v)`](#fn-quote) | Smart-quote a string for readable Lua-like output. | -| [`keypath(...)`](#fn-keypath) | Format a key chain as a Lua-like table access path. | -| [`args_repr(v)`](#fn-args-repr) | Format a list-like table as a comma-separated argument string. | -| [`assert_arg(argn, v, validator?, optional?, msg?)`](#fn-assert-arg) | Assert argument value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | -| [`validate(name, v, validator?, optional?, msg?)`](#fn-validate) | Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | -| [`validate(path, v, validator?, optional?, msg?)`](#fn-validate) | Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | -| [`lazy_module(name, err?)`](#fn-lazy-module) | Return a lazy proxy for a module. | +**Formatting**: - +| Function | Description | +| ------------------------------- | -------------------------------------------------------------- | +| [`args_repr(v)`](#fn-args-repr) | Format a list-like table as a comma-separated argument string. | +| [`keypath(...)`](#fn-keypath) | Format a key chain as a Lua-like table access path. | +| [`quote(v)`](#fn-quote) | Smart-quote a string for readable Lua-like output. | -### `quote(v)` +**Lazy Loading**: -Smart-quote a string for readable Lua-like output. +| Function | Description | +| -------------------------------------------- | --------------------------------- | +| [`lazy_module(name, err?)`](#fn-lazy-module) | Return a lazy proxy for a module. | + +**Validation**: + +| Function | Description | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| [`assert_arg(argn, v, validator?, optional?, lv?)`](#fn-assert-arg) | Assert argument value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | +| [`validate(name, v, validator?, optional?, msg?)`](#fn-validate) | Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | +| [`validate(path, v, validator?, optional?, msg?)`](#fn-validate) | Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. | + +### Formatting + + + +#### `args_repr(v)` + +Format a list-like table as a comma-separated argument string. **Parameters**: -- `v` (`string`): String to quote. +- `v` (`any`): Value to format. `nil` returns an empty string. **Return**: -- `out` (`string`): Quoted string. +- `out` (`string`): Argument list string. **Example**: ```lua -print(utils.quote('He said "hi"')) -- 'He said "hi"' -print(utils.quote('say "hi" and \\'bye\\'')) -- "say \"hi\" and 'bye'" +utils.args_repr({ "a", 1, true }) --> '"a", 1, true' ``` +> [!NOTE] +> +> Requires [`inspect`](https://github.com/kikito/inspect.lua) + -### `keypath(...)` +#### `keypath(...)` Format a key chain as a Lua-like table access path. @@ -70,33 +87,66 @@ p3 = utils.keypath("ctx", "invalid-key") --> 'ctx["invalid-key"]' p4 = utils.keypath() --> "" ``` - + -### `args_repr(v)` +#### `quote(v)` -Format a list-like table as a comma-separated argument string. +Smart-quote a string for readable Lua-like output. **Parameters**: -- `v` (`table|any`): Value to format. +- `v` (`string`): String to quote. **Return**: -- `out` (`string`): Argument list string. +- `out` (`string`): Quoted string. **Example**: ```lua -utils.args_repr({ "a", 1, true }) --> '"a", 1, true' +print(utils.quote('He said "hi"')) -- 'He said "hi"' +print(utils.quote('say "hi" and \\'bye\\'')) -- "say \"hi\" and 'bye'" +``` + +### Lazy Loading + + + +#### `lazy_module(name, err?)` + +Return a lazy proxy for a module. + +The proxy rewrites its metamethods after first access while keeping the proxy +table itself free of cached fields. + +**Parameters**: + +- `name` (`string`): Module name passed to `require`. +- `err?` (`string`): Optional error message raised when loading fails. + +**Return**: + +- `module` (`{}`): Lazy proxy for the loaded module. + +**Example**: + +```lua +local fs = utils.lazy_module("mods.fs") +print(fs.exists("README.md")) + +local repr = utils.lazy_module("mods.repr") +print(repr({ a = 1 })) ``` > [!NOTE] > -> Requires [`inspect`](https://github.com/kikito/inspect.lua) +> Supports both table-returning modules and function-returning modules. + +### Validation -### `assert_arg(argn, v, validator?, optional?, msg?)` +#### `assert_arg(argn, v, validator?, optional?, lv?)` Assert argument value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. @@ -107,8 +157,7 @@ error on failure. - `v` (`T`): Value to check. - `validator?` (`modsValidatorName`): Validator name (defaults to `"truthy"`). - `optional?` (`boolean`): Skip errors when `v` is `nil` (defaults to `false`). -- `msg?` (`string`): Optional override template passed to - [`mods.validate`](/modules/validate). +- `lv?` (`integer`): Error level passed to `error` (defaults to `3`). **Return**: @@ -132,7 +181,7 @@ utils.assert_arg(3, "x", "number", false, "need {{expected}}, got {{got}}") -### `validate(name, v, validator?, optional?, msg?)` +#### `validate(name, v, validator?, optional?, msg?)` Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. @@ -161,7 +210,7 @@ utils.validate("count", "x", "number") -### `validate(path, v, validator?, optional?, msg?)` +#### `validate(path, v, validator?, optional?, msg?)` Validate a value using [`mods.validate`](/modules/validate) and raise a Lua error on failure. @@ -191,35 +240,3 @@ utils.validate({ "ctx", "users", 1, "name" }, 123, "string") > > On failure, `path` is rendered with > [`mods.utils.keypath`](/modules/utils#fn-keypath). - - - -### `lazy_module(name, err?)` - -Return a lazy proxy for a module. - -The proxy rewrites its metamethods after first access while keeping the proxy -table itself free of cached fields. - -**Parameters**: - -- `name` (`string`): Module name passed to `require`. -- `err?` (`string`): Optional error message raised when loading fails. - -**Return**: - -- `module` (`{}`): Lazy proxy for the loaded module. - -**Example**: - -```lua -local fs = utils.lazy_module("mods.fs") -print(fs.exists("README.md")) - -local repr = utils.lazy_module("mods.repr") -print(repr({ a = 1 })) -``` - -> [!NOTE] -> -> Supports both table-returning modules and function-returning modules. diff --git a/docs/src/modules/validate.md b/docs/src/modules/validate.md index f904c2d..196af83 100644 --- a/docs/src/modules/validate.md +++ b/docs/src/modules/validate.md @@ -26,6 +26,12 @@ validate(1) --> true, nil validate(1, "nil") --> false, "nil expected, got number" ``` +> [!IMPORTANT] +> +> Path checks require **LuaFileSystem** +> ([`lfs`](https://github.com/lunarmodules/luafilesystem)) and raise an error if +> it is not installed. + ## Validator Names Validator names are case-insensitive for field access. @@ -57,6 +63,26 @@ validate.string(123, "want {{expected}}, got {{got}}") ## Functions +**Path Checks**: + +| Function | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------- | +| [`block(v, msg?)`](#fn-block) | Returns `true` when `v` is a block device path. Otherwise returns `false` and an error message. | +| [`char(v, msg?)`](#fn-char) | Returns `true` when `v` is a char device path. Otherwise returns `false` and an error message. | +| [`device(v, msg?)`](#fn-device) | Returns `true` when `v` is a block or char device path. Otherwise returns `false` and an error message. | +| [`dir(v, msg?)`](#fn-dir) | Returns `true` when `v` is a directory path. Otherwise returns `false` and an error message. | +| [`fifo(v, msg?)`](#fn-fifo) | Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error message. | +| [`file(v, msg?)`](#fn-file) | Returns `true` when `v` is a file path. Otherwise returns `false` and an error message. | +| [`link(v, msg?)`](#fn-link) | Returns `true` when `v` is a symlink path. Otherwise returns `false` and an error message. | +| [`path(v, msg?)`](#fn-path) | Returns `true` when `v` is a valid filesystem path. Otherwise returns `false` and an error message. | +| [`socket(v, msg?)`](#fn-socket) | Returns `true` when `v` is a socket path. Otherwise returns `false` and an error message. | + +**Registration**: + +| Function | Description | +| ------------------------------------------------------ | -------------------------------------------------- | +| [`register(name, validator, template?)`](#fn-register) | Register or override a validator function by name. | + **Type Checks**: | Function | Description | @@ -74,41 +100,21 @@ validate.string(123, "want {{expected}}, got {{got}}") | Function | Description | | ----------------------------------- | ------------------------------------------------------------------------------------------- | +| [`callable(v, msg?)`](#fn-callable) | Returns `true` when `v` is callable. Otherwise returns `false` and an error message. | | [`false(v, msg?)`](#fn-false) | Returns `true` when `v` is exactly `false`. Otherwise returns `false` and an error message. | -| [`true(v, msg?)`](#fn-true) | Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an error message. | | [`falsy(v, msg?)`](#fn-falsy) | Returns `true` when `v` is falsy. Otherwise returns `false` and an error message. | -| [`callable(v, msg?)`](#fn-callable) | Returns `true` when `v` is callable. Otherwise returns `false` and an error message. | | [`integer(v, msg?)`](#fn-integer) | Returns `true` when `v` is an integer. Otherwise returns `false` and an error message. | +| [`true(v, msg?)`](#fn-true) | Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an error message. | | [`truthy(v, msg?)`](#fn-truthy) | Returns `true` when `v` is truthy. Otherwise returns `false` and an error message. | -**Path Checks**: - -| Function | Description | -| ------------------------------- | ------------------------------------------------------------------------------------------------------- | -| [`path(v, msg?)`](#fn-path) | Returns `true` when `v` is a valid filesystem path. Otherwise returns `false` and an error message. | -| [`block(v, msg?)`](#fn-block) | Returns `true` when `v` is a block device path. Otherwise returns `false` and an error message. | -| [`char(v, msg?)`](#fn-char) | Returns `true` when `v` is a char device path. Otherwise returns `false` and an error message. | -| [`device(v, msg?)`](#fn-device) | Returns `true` when `v` is a block or char device path. Otherwise returns `false` and an error message. | -| [`dir(v, msg?)`](#fn-dir) | Returns `true` when `v` is a directory path. Otherwise returns `false` and an error message. | -| [`fifo(v, msg?)`](#fn-fifo) | Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error message. | -| [`file(v, msg?)`](#fn-file) | Returns `true` when `v` is a file path. Otherwise returns `false` and an error message. | -| [`link(v, msg?)`](#fn-link) | Returns `true` when `v` is a symlink path. Otherwise returns `false` and an error message. | -| [`socket(v, msg?)`](#fn-socket) | Returns `true` when `v` is a socket path. Otherwise returns `false` and an error message. | - -**Validator API**: - -| Function | Description | -| ------------------------------------------------------ | -------------------------------------------------- | -| [`register(name, validator, template?)`](#fn-register) | Register or override a validator function by name. | - -### Type Checks +### Path Checks -Basic Lua type validators (and their negated variants). + -#### `boolean(v, msg?)` +#### `block(v, msg?)` -Returns `true` when `v` is a boolean. Otherwise returns `false` and an error -message. +Returns `true` when `v` is a block device path. Otherwise returns `false` and an +error message. **Parameters**: @@ -123,16 +129,15 @@ message. **Example**: ```lua -ok, err = validate.boolean(true) --> true, nil -ok, err = validate.boolean(1) --> false, "boolean expected, got number" +ok, err = validate.block(".") ``` - + -#### `function(v, msg?)` +#### `char(v, msg?)` -Returns `true` when `v` is a function. Otherwise returns `false` and an error -message. +Returns `true` when `v` is a char device path. Otherwise returns `false` and an +error message. **Parameters**: @@ -147,17 +152,15 @@ message. **Example**: ```lua -ok, err = validate.Function(function() end) --> true, nil -ok, err = validate.Function(1) ---> false, "function expected, got number" +ok, err = validate.char(".") ``` - + -#### `nil(v, msg?)` +#### `device(v, msg?)` -Returns `true` when `v` is `nil`. Otherwise returns `false` and an error -message. +Returns `true` when `v` is a block or char device path. Otherwise returns +`false` and an error message. **Parameters**: @@ -172,16 +175,15 @@ message. **Example**: ```lua -ok, err = validate.Nil(nil) --> true, nil -ok, err = validate.Nil(0) --> false, "nil expected, got number" +ok, err = validate.device(".") ``` - + -#### `number(v, msg?)` +#### `dir(v, msg?)` -Returns `true` when `v` is a number. Otherwise returns `false` and an error -message. +Returns `true` when `v` is a directory path. Otherwise returns `false` and an +error message. **Parameters**: @@ -196,15 +198,14 @@ message. **Example**: ```lua -ok, err = validate.number(42) --> true, nil -ok, err = validate.number("x") --> false, "number expected, got string" +ok, err = validate.dir(".") ``` - + -#### `string(v, msg?)` +#### `fifo(v, msg?)` -Returns `true` when `v` is a string. Otherwise returns `false` and an error +Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error message. **Parameters**: @@ -220,15 +221,14 @@ message. **Example**: ```lua -ok, err = validate.string("hello") --> true, nil -ok, err = validate.string(1) --> false, "string expected, got number" +ok, err = validate.fifo(".") ``` - + -#### `table(v, msg?)` +#### `file(v, msg?)` -Returns `true` when `v` is a table. Otherwise returns `false` and an error +Returns `true` when `v` is a file path. Otherwise returns `false` and an error message. **Parameters**: @@ -244,16 +244,15 @@ message. **Example**: ```lua -ok, err = validate.table({}) --> true, nil -ok, err = validate.table(1) --> false, "table expected, got number" +ok, err = validate.file(".") ``` - + -#### `thread(v, msg?)` +#### `link(v, msg?)` -Returns `true` when `v` is a thread. Otherwise returns `false` and an error -message. +Returns `true` when `v` is a symlink path. Otherwise returns `false` and an +error message. **Parameters**: @@ -268,17 +267,15 @@ message. **Example**: ```lua -co = coroutine.create(function() end) -ok, err = validate.thread(co) --> true, nil -ok, err = validate.thread(1) --> false, "thread expected, got number" +ok, err = validate.link(".") ``` - + -#### `userdata(v, msg?)` +#### `path(v, msg?)` -Returns `true` when `v` is a userdata value. Otherwise returns `false` and an -error message. +Returns `true` when `v` is a valid filesystem path. Otherwise returns `false` +and an error message. **Parameters**: @@ -293,19 +290,15 @@ error message. **Example**: ```lua -ok, err = validate.userdata(io.stdout) --> true, nil -ok, err = validate.userdata(1) --> false, "userdata expected, got number" +ok, err = validate.path("README.md") ``` -### Value Checks - -Value-state validators (exact true/false, truthy/falsy, callable, integer). - + -#### `false(v, msg?)` +#### `socket(v, msg?)` -Returns `true` when `v` is exactly `false`. Otherwise returns `false` and an -error message. +Returns `true` when `v` is a socket path. Otherwise returns `false` and an error +message. **Parameters**: @@ -320,39 +313,53 @@ error message. **Example**: ```lua -ok, err = validate.False(false) --> true, nil -ok, err = validate.False(true) --> false, "false value expected, got true" +ok, err = validate.socket(".") ``` - +### Registration -#### `true(v, msg?)` + -Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an -error message. +#### `register(name, validator, template?)` + +Register or override a validator function by name. **Parameters**: -- `v` (`any`): Value to validate. -- `msg?` (`string`): Optional override template. +- `name` (`string`): Validator name. +- `validator` (`fun(v:any):(ok:boolean)`): Validator function. +- `template?` (`string`): Optional default message template. **Return**: -- `isValid` (`boolean`): Whether the check succeeds. -- `err` (`string?`): Error message when the check fails. +- `none` (`nil`) **Example**: ```lua -ok, err = validate.True(true) --> true, nil -ok, err = validate.True(false) --> false, "true value expected, got false" +validate.register("odd", function(v) + return type(v) == "number" and v % 2 == 1 +end, "{{value}} does not satisfy {{expected}}") + +ok, err = validate.odd(3) --> true, nil +ok, err = validate.odd("x") --> false, '"x" does not satisfy odd' +ok, err = validate(2, "odd") --> false, "2 does not satisfy odd" ``` - +> [!NOTE] +> +> - If `template` is provided, it becomes the default message template for that +> validator. +> - If `template` is omitted, failures use: +> `{{expected}} expected, got {{got}}`. -#### `falsy(v, msg?)` +### Type Checks -Returns `true` when `v` is falsy. Otherwise returns `false` and an error + + +#### `boolean(v, msg?)` + +Returns `true` when `v` is a boolean. Otherwise returns `false` and an error message. **Parameters**: @@ -368,15 +375,15 @@ message. **Example**: ```lua -ok, err = validate.falsy(false) --> true, nil -ok, err = validate.falsy(1) --> false, "falsy value expected, got 1" +ok, err = validate.boolean(true) --> true, nil +ok, err = validate.boolean(1) --> false, "boolean expected, got number" ``` - + -#### `callable(v, msg?)` +#### `function(v, msg?)` -Returns `true` when `v` is callable. Otherwise returns `false` and an error +Returns `true` when `v` is a function. Otherwise returns `false` and an error message. **Parameters**: @@ -392,15 +399,16 @@ message. **Example**: ```lua -ok, err = validate.callable(type) --> true, nil -ok, err = validate.callable(1) --> false, "callable value expected, got 1" +ok, err = validate.Function(function() end) --> true, nil +ok, err = validate.Function(1) +--> false, "function expected, got number" ``` - + -#### `integer(v, msg?)` +#### `nil(v, msg?)` -Returns `true` when `v` is an integer. Otherwise returns `false` and an error +Returns `true` when `v` is `nil`. Otherwise returns `false` and an error message. **Parameters**: @@ -416,15 +424,15 @@ message. **Example**: ```lua -ok, err = validate.integer(1) --> true, nil -ok, err = validate.integer(1.5) --> false, "integer value expected, got 1.5" +ok, err = validate.Nil(nil) --> true, nil +ok, err = validate.Nil(0) --> false, "nil expected, got number" ``` - + -#### `truthy(v, msg?)` +#### `number(v, msg?)` -Returns `true` when `v` is truthy. Otherwise returns `false` and an error +Returns `true` when `v` is a number. Otherwise returns `false` and an error message. **Parameters**: @@ -440,24 +448,16 @@ message. **Example**: ```lua -ok, err = validate.truthy(1) --> true, nil -ok, err = validate.truthy(false) --> false, "truthy value expected, got false" +ok, err = validate.number(42) --> true, nil +ok, err = validate.number("x") --> false, "number expected, got string" ``` -### Path Checks - -Filesystem path-kind validators backed by LuaFileSystem (`lfs`). - -> [!IMPORTANT] -> -> Path checks require **LuaFileSystem** -> ([`lfs`](https://github.com/lunarmodules/luafilesystem)) and raise an error if -> it is not installed. + -#### `path(v, msg?)` +#### `string(v, msg?)` -Returns `true` when `v` is a valid filesystem path. Otherwise returns `false` -and an error message. +Returns `true` when `v` is a string. Otherwise returns `false` and an error +message. **Parameters**: @@ -472,15 +472,16 @@ and an error message. **Example**: ```lua -ok, err = validate.path("README.md") +ok, err = validate.string("hello") --> true, nil +ok, err = validate.string(1) --> false, "string expected, got number" ``` - + -#### `block(v, msg?)` +#### `table(v, msg?)` -Returns `true` when `v` is a block device path. Otherwise returns `false` and an -error message. +Returns `true` when `v` is a table. Otherwise returns `false` and an error +message. **Parameters**: @@ -495,15 +496,16 @@ error message. **Example**: ```lua -ok, err = validate.block(".") +ok, err = validate.table({}) --> true, nil +ok, err = validate.table(1) --> false, "table expected, got number" ``` - + -#### `char(v, msg?)` +#### `thread(v, msg?)` -Returns `true` when `v` is a char device path. Otherwise returns `false` and an -error message. +Returns `true` when `v` is a thread. Otherwise returns `false` and an error +message. **Parameters**: @@ -518,15 +520,17 @@ error message. **Example**: ```lua -ok, err = validate.char(".") +co = coroutine.create(function() end) +ok, err = validate.thread(co) --> true, nil +ok, err = validate.thread(1) --> false, "thread expected, got number" ``` - + -#### `device(v, msg?)` +#### `userdata(v, msg?)` -Returns `true` when `v` is a block or char device path. Otherwise returns -`false` and an error message. +Returns `true` when `v` is a userdata value. Otherwise returns `false` and an +error message. **Parameters**: @@ -541,15 +545,18 @@ Returns `true` when `v` is a block or char device path. Otherwise returns **Example**: ```lua -ok, err = validate.device(".") +ok, err = validate.userdata(io.stdout) --> true, nil +ok, err = validate.userdata(1) --> false, "userdata expected, got number" ``` - +### Value Checks -#### `dir(v, msg?)` + -Returns `true` when `v` is a directory path. Otherwise returns `false` and an -error message. +#### `callable(v, msg?)` + +Returns `true` when `v` is callable. Otherwise returns `false` and an error +message. **Parameters**: @@ -564,15 +571,16 @@ error message. **Example**: ```lua -ok, err = validate.dir(".") +ok, err = validate.callable(type) --> true, nil +ok, err = validate.callable(1) --> false, "callable value expected, got 1" ``` - + -#### `fifo(v, msg?)` +#### `false(v, msg?)` -Returns `true` when `v` is a FIFO path. Otherwise returns `false` and an error -message. +Returns `true` when `v` is exactly `false`. Otherwise returns `false` and an +error message. **Parameters**: @@ -587,14 +595,15 @@ message. **Example**: ```lua -ok, err = validate.fifo(".") +ok, err = validate.False(false) --> true, nil +ok, err = validate.False(true) --> false, "false value expected, got true" ``` - + -#### `file(v, msg?)` +#### `falsy(v, msg?)` -Returns `true` when `v` is a file path. Otherwise returns `false` and an error +Returns `true` when `v` is falsy. Otherwise returns `false` and an error message. **Parameters**: @@ -610,15 +619,16 @@ message. **Example**: ```lua -ok, err = validate.file(".") +ok, err = validate.falsy(false) --> true, nil +ok, err = validate.falsy(1) --> false, "falsy value expected, got 1" ``` - + -#### `link(v, msg?)` +#### `integer(v, msg?)` -Returns `true` when `v` is a symlink path. Otherwise returns `false` and an -error message. +Returns `true` when `v` is an integer. Otherwise returns `false` and an error +message. **Parameters**: @@ -633,15 +643,16 @@ error message. **Example**: ```lua -ok, err = validate.link(".") +ok, err = validate.integer(1) --> true, nil +ok, err = validate.integer(1.5) --> false, "integer value expected, got 1.5" ``` - + -#### `socket(v, msg?)` +#### `true(v, msg?)` -Returns `true` when `v` is a socket path. Otherwise returns `false` and an error -message. +Returns `true` when `v` is exactly `true`. Otherwise returns `false` and an +error message. **Parameters**: @@ -656,49 +667,39 @@ message. **Example**: ```lua -ok, err = validate.socket(".") +ok, err = validate.True(true) --> true, nil +ok, err = validate.True(false) --> false, "true value expected, got false" ``` -### Validator API - - + -#### `register(name, validator, template?)` +#### `truthy(v, msg?)` -Register or override a validator function by name. +Returns `true` when `v` is truthy. Otherwise returns `false` and an error +message. **Parameters**: -- `name` (`string`): Validator name. -- `validator` (`fun(v:any):(ok:boolean)`): Validator function. -- `template?` (`string`): Optional default message template. +- `v` (`any`): Value to validate. +- `msg?` (`string`): Optional override template. **Return**: -- `none` (`nil`) +- `isValid` (`boolean`): Whether the check succeeds. +- `err` (`string?`): Error message when the check fails. **Example**: ```lua -validate.register("odd", function(v) - return type(v) == "number" and v % 2 == 1 -end, "{{value}} does not satisfy {{expected}}") - -ok, err = validate.odd(3) --> true, nil -ok, err = validate.odd("x") --> false, '"x" does not satisfy odd' -ok, err = validate(2, "odd") --> false, "2 does not satisfy odd" +ok, err = validate.truthy(1) --> true, nil +ok, err = validate.truthy(false) --> false, "truthy value expected, got false" ``` -> [!NOTE] -> -> - If `template` is provided, it becomes the default message template for that -> validator. -> - If `template` is omitted, failures use: -> `{{expected}} expected, got {{got}}`. - ## Fields -### `messages` + + +### `messages` (`modsValidatorMessages`) Custom error-message templates for validator failures.