diff --git a/main.go b/main.go
index e8b0b10..a189cf0 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ import (
_ "embed"
"errors"
"os"
+ "path/filepath"
"sort"
"sync"
"sync/atomic"
@@ -64,6 +65,20 @@ Features:
CreateExampleConfig()
return
}
+ metaFlag, _ := cmd.Flags().GetBool("meta")
+ if metaFlag {
+ cwd, err := os.Getwd()
+ if err != nil {
+ mainLogger.Error("Failed to get current directory: %v", err)
+ os.Exit(1)
+ }
+ metaPath := filepath.Join(cwd, "meta.lua")
+ if err := processor.GenerateMetaFile(metaPath); err != nil {
+ mainLogger.Error("Failed to generate meta.lua: %v", err)
+ os.Exit(1)
+ }
+ return
+ }
if len(args) == 0 {
cmd.Usage()
return
@@ -81,6 +96,7 @@ Features:
rootCmd.Flags().Bool("json", false, "Enable JSON mode for processing JSON files")
rootCmd.Flags().BoolP("conv", "c", false, "Convert YAML files to TOML format")
rootCmd.Flags().BoolP("example", "e", false, "Generate example_cook.toml and exit")
+ rootCmd.Flags().BoolP("meta", "m", false, "Generate meta.lua file for LuaLS autocomplete and exit")
// Set up examples in the help text
rootCmd.SetUsageTemplate(`Usage:{{if .Runnable}}
diff --git a/processor/meta.go b/processor/meta.go
new file mode 100644
index 0000000..bbf5042
--- /dev/null
+++ b/processor/meta.go
@@ -0,0 +1,29 @@
+package processor
+
+import (
+ _ "embed"
+ "fmt"
+ "os"
+
+ logger "git.site.quack-lab.dev/dave/cylogger"
+)
+
+//go:embed meta.lua
+var metaFileContent string
+
+var metaLogger = logger.Default.WithPrefix("meta")
+
+// GenerateMetaFile generates meta.lua with function signatures for LuaLS autocomplete
+func GenerateMetaFile(outputPath string) error {
+ metaLogger.Info("Generating meta.lua file for LuaLS autocomplete")
+
+ // Write the embedded meta file
+ err := os.WriteFile(outputPath, []byte(metaFileContent), 0644)
+ if err != nil {
+ metaLogger.Error("Failed to write meta.lua: %v", err)
+ return fmt.Errorf("failed to write meta.lua: %w", err)
+ }
+
+ metaLogger.Info("Successfully generated meta.lua at %q", outputPath)
+ return nil
+}
diff --git a/processor/meta.lua b/processor/meta.lua
new file mode 100644
index 0000000..c68b5ef
--- /dev/null
+++ b/processor/meta.lua
@@ -0,0 +1,245 @@
+---@meta
+
+---@class ParserOptions
+---@field delimiter string? The field delimiter (default: ",").
+---@field hasheader boolean? If true, first non-comment row is treated as headers (default: false).
+---@field hascomments boolean? If true, lines starting with '#' are skipped (default: false).
+
+---@class XMLElement
+---@field _tag string The XML tag name
+---@field _attr {[string]: string}? XML attributes as key-value pairs
+---@field _text string? Text content of the element
+---@field _children XMLElement[]? Child elements
+
+---@class JSONNode
+---@field [string] string | number | boolean | nil | JSONNode | JSONArray JSON object fields
+---@alias JSONArray (string | number | boolean | nil | JSONNode)[]
+
+---@class CSVRow
+---@field [integer] string Numeric indices for field access
+---@field Headers string[]? Header row if hasheader was true
+
+--- Returns the minimum of two numbers
+---@param a number First number
+---@param b number Second number
+---@return number #Minimum value
+function min(a, b) end
+
+--- Returns the maximum of two numbers
+---@param a number First number
+---@param b number Second number
+---@return number #Maximum value
+function max(a, b) end
+
+--- Rounds a number to n decimal places
+---@param x number Number to round
+---@param n number? Number of decimal places (default: 0)
+---@return number #Rounded number
+function round(x, n) end
+
+--- Returns the floor of a number
+---@param x number Number to floor
+---@return number #Floored number
+function floor(x) end
+
+--- Returns the ceiling of a number
+---@param x number Number to ceil
+---@return number #Ceiled number
+function ceil(x) end
+
+--- Converts string to uppercase
+---@param s string String to convert
+---@return string #Uppercase string
+function upper(s) end
+
+--- Converts string to lowercase
+---@param s string String to convert
+---@return string #Lowercase string
+function lower(s) end
+
+--- Formats a string using Lua string.format
+---@param s string Format string
+---@param ... any Values to format
+---@return string #Formatted string
+function format(s, ...) end
+
+--- Removes leading and trailing whitespace from string
+---@param s string String to trim
+---@return string #Trimmed string
+function trim(s) end
+
+--- Splits a string by separator
+---@param inputstr string String to split
+---@param sep string? Separator pattern (default: whitespace)
+---@return string[] #Array of string parts
+function strsplit(inputstr, sep) end
+
+--- Prints table structure recursively
+---@param table {[any]: any} Table to dump
+---@param depth number? Current depth (default: 0)
+function dump(table, depth) end
+
+--- Validates options against a set of valid option keys.
+---@param options ParserOptions? The options table to validate
+function areOptionsValid(options) end
+
+--- Parses CSV text into rows and fields using a minimal RFC 4180 state machine.
+--- Requirements/assumptions:
+--- Input is a single string containing the entire CSV content.
+--- Field separators are specified by delimiter option (default: comma).
+--- Newlines between rows may be "\n" or "\r\n". "\r\n" is treated as one line break.
+--- Fields may be quoted with double quotes (").
+--- Inside quoted fields, doubled quotes ("") represent a literal quote character.
+--- No backslash escaping is supported (not part of RFC 4180).
+--- Newlines inside quoted fields are preserved as part of the field.
+--- Leading/trailing spaces are preserved; no trimming is performed.
+--- Empty fields and empty rows are preserved.
+--- The final row is emitted even if the text does not end with a newline.
+--- Lines starting with '#' (after optional leading whitespace) are treated as comments and skipped if hascomments is true.
+---@param csv string The CSV text to parse.
+---@param options ParserOptions? Options for the parser
+---@return CSVRow[] #A table (array) of rows; each row is a table with numeric indices and optionally header-named keys.
+function fromCSV(csv, options) end
+
+--- Converts a table of rows back to CSV text format (RFC 4180 compliant).
+--- Requirements:
+--- Input is a table (array) of rows, where each row is a table (array) of field values.
+--- Field values are converted to strings using tostring().
+--- Fields are quoted if they contain the delimiter, newlines, or double quotes.
+--- Double quotes inside quoted fields are doubled ("").
+--- Fields are joined with the specified delimiter; rows are joined with newlines.
+--- If includeHeaders is true and rows have a Headers field, headers are included as the first row.
+---@param rows CSVRow[] Array of rows, where each row is an array of field values.
+---@param options ParserOptions? Options for the parser
+---@return string #CSV-formatted text
+function toCSV(rows, options) end
+
+--- Converts string to number, returns 0 if invalid
+---@param str string String to convert
+---@return number #Numeric value or 0
+function num(str) end
+
+--- Converts number to string
+---@param num number Number to convert
+---@return string #String representation
+function str(num) end
+
+--- Checks if string is numeric
+---@param str string String to check
+---@return boolean #True if string is numeric
+function is_number(str) end
+
+--- Checks if table is a sequential array (1-indexed with no gaps)
+---@param t {[integer]: any} Table to check
+---@return boolean #True if table is an array
+function isArray(t) end
+
+--- Find all elements with a specific tag name (recursive search)
+---@param root XMLElement The root XML element (with _tag, _attr, _children fields)
+---@param tagName string The tag name to search for
+---@return XMLElement[] #Array of matching elements
+function findElements(root, tagName) end
+
+--- Visit all elements recursively and call a function on each
+---@param root XMLElement The root XML element
+---@param callback fun(element: XMLElement, depth: number, path: string) Function to call with each element
+function visitElements(root, callback) end
+
+--- Get numeric value from XML element attribute
+---@param element XMLElement XML element with _attr field
+---@param attrName string Attribute name
+---@return number? #The numeric value or nil if not found/not numeric
+function getNumAttr(element, attrName) end
+
+--- Set numeric value to XML element attribute
+---@param element XMLElement XML element with _attr field
+---@param attrName string Attribute name
+---@param value number Numeric value to set
+function setNumAttr(element, attrName, value) end
+
+--- Modify numeric attribute by applying a function
+---@param element XMLElement XML element
+---@param attrName string Attribute name
+---@param func fun(currentValue: number): number Function that takes current value and returns new value
+---@return boolean #True if modification was made
+function modifyNumAttr(element, attrName, func) end
+
+--- Find all elements matching a predicate function
+---@param root XMLElement The root XML element
+---@param predicate fun(element: XMLElement): boolean Function that takes element and returns true/false
+---@return XMLElement[] #Array of matching elements
+function filterElements(root, predicate) end
+
+--- Get text content of an element
+---@param element XMLElement XML element
+---@return string? #The text content or nil
+function getText(element) end
+
+--- Set text content of an element
+---@param element XMLElement XML element
+---@param text string Text content to set
+function setText(element, text) end
+
+--- Check if element has an attribute
+---@param element XMLElement XML element
+---@param attrName string Attribute name
+---@return boolean #True if attribute exists
+function hasAttr(element, attrName) end
+
+--- Get attribute value as string
+---@param element XMLElement XML element
+---@param attrName string Attribute name
+---@return string? #The attribute value or nil
+function getAttr(element, attrName) end
+
+--- Set attribute value
+---@param element XMLElement XML element
+---@param attrName string Attribute name
+---@param value string | number | boolean Value to set (will be converted to string)
+function setAttr(element, attrName, value) end
+
+--- Find first element with a specific tag name (searches direct children only)
+---@param parent XMLElement The parent XML element
+---@param tagName string The tag name to search for
+---@return XMLElement? #The first matching element or nil
+function findFirstElement(parent, tagName) end
+
+--- Add a child element to a parent
+---@param parent XMLElement The parent XML element
+---@param child XMLElement The child element to add
+function addChild(parent, child) end
+
+--- Remove all children with a specific tag name
+---@param parent XMLElement The parent XML element
+---@param tagName string The tag name to remove
+---@return number #Count of removed children
+function removeChildren(parent, tagName) end
+
+--- Get all direct children with a specific tag name
+---@param parent XMLElement The parent XML element
+---@param tagName string The tag name to search for
+---@return XMLElement[] #Array of matching children
+function getChildren(parent, tagName) end
+
+--- Count children with a specific tag name
+---@param parent XMLElement The parent XML element
+---@param tagName string The tag name to count
+---@return number #Count of matching children
+function countChildren(parent, tagName) end
+
+--- Recursively visit all values in a JSON structure
+---@param data JSONNode | JSONArray JSON data (nested tables)
+---@param callback fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): nil Function called with (value, key, parent)
+function visitJSON(data, callback) end
+
+--- Find all values in JSON matching a predicate
+---@param data JSONNode | JSONArray JSON data
+---@param predicate fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): boolean Function that takes (value, key, parent) and returns true/false
+---@return (string | number | boolean | nil | JSONNode | JSONArray)[] #Array of matching values
+function findInJSON(data, predicate) end
+
+--- Modify all numeric values in JSON matching a condition
+---@param data JSONNode | JSONArray JSON data
+---@param predicate fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): boolean Function that takes (value, key, parent) and returns true/false
+---@param modifier fun(currentValue: number): number Function that takes current value and returns new value
+function modifyJSONNumbers(data, predicate, modifier) end