| Crates.io | skillet |
| lib.rs | skillet |
| version | 0.5.2 |
| created_at | 2025-08-24 01:55:40.04677+00 |
| updated_at | 2025-09-23 17:30:25.836593+00 |
| description | Skillet: micro expression language (arithmetic, logical, functions, arrays, conditionals, excel formulas) made in Rust bin cli and server |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1808012 |
| size | 713,666 |

Skillet is a high-performance, embeddable expression engine written in Rust, inspired by Excel formulas with Ruby-style method chaining. It parses expressions into an AST and evaluates them with an optimized runtime.
β¨ New Features:
null.to_s(), "123".to_i(), [1,2,3].to_bool() - available on all typesobj&.property&.method() - prevents null reference errorsSUBSTITUTE(text, substr, replacement), SUBSTITUTEM(text, substr, replacement) and Excel-style REPLACE(old_text, start_num, num_chars, new_text)DIG(json, ['path','to','key'], default) and method form json.dig([...], default)&.) and conversion methods handle null gracefullySupported Types: Numbers, strings, booleans, nulls, arrays, JSON objects, dates, currency Operations: Arithmetic, logical, comparisons, method chaining, array operations, lambdas Extensions: JavaScript plugins, Rust custom functions, HTTP/TCP server modes
π Full Documentation | π API Reference
cargo build
cargo test
Traditional Excel-style formulas:
cargo run --bin sk -- "SUM(1, 2, 3, 4, 5)" # 15
cargo run --bin sk -- "IF(10 > 5, \"Yes\", \"No\")" # "Yes"
cargo run --bin sk -- "AVERAGE([85, 92, 78, 90])" # 86.25
β¨ New: Null-safe operations with conversion methods:
cargo run --bin sk -- "null.to_s().length()" # 0 (no error!)
cargo run --bin sk -- "\"123\".to_i() + 10" # 133
cargo run --bin sk -- "[null, \"hello\"].map(:x.to_s())" # ["", "hello"]
β¨ New: Safe navigation operator:
cargo run --bin sk -- ":data := {\"name\": null}; :data&.name&.length()" # null (no error!)
β¨ New: String helpers and JSON dig:
# SUBSTITUTE replaces all occurrences of a substring
cargo run --bin sk -- "SUBSTITUTE('foo bar foo', 'foo', 'baz')" # "baz bar baz"
# SUBSTITUTEM: same as SUBSTITUTE (replace multiple occurrences)
cargo run --bin sk -- "SUBSTITUTEM('a-a-a', '-', '_')" # "a_a_a"
# REPLACE: positional, 1-based start
cargo run --bin sk -- "REPLACE('abcdef', 3, 2, 'XY')" # "abXYef"
# DIG: navigate JSON safely with a path (arrays supported)
cargo run --bin sk -- ":obj := {\"user\": {\"posts\": [{\"title\": \"First\"}]}}; DIG(:obj, ['user','posts',0,'title'])" # "First"
# Method form
cargo run --bin sk -- ":obj := {\"user\": {\"name\": \"Jane\"}}; :obj.dig(['user','name'])" # "Jane"
Advanced array operations:
cargo run --bin sk -- "[30,60,80,100].filter(:x > 50).map(:x * 0.9).sum()" # 216
Notes:
= is optional (supported for Excel-style familiarity)Add to your Cargo project (from crates.io):
[dependencies]
skillet = "0.5.2"
Or with cargo-edit:
cargo add skillet@0.4.1
Skillet includes production-ready HTTP and TCP servers for high-performance expression evaluation.
sk_http_server)Run the HTTP server for REST API access:
# Basic usage
cargo run --bin sk_http_server 5074
# Production deployment
cargo run --bin sk_http_server 5074 --host 0.0.0.0 --token your_secret_token
# Background daemon
cargo run --bin sk_http_server 5074 -d --host 0.0.0.0 --token secret123 --admin-token admin456
Parameters:
<port> - Port to bind (required)-H, --host <addr> - Bind address (default: 127.0.0.1)-d, --daemon - Run as background daemon--token <value> - Require token for eval requests--admin-token <value> - Require admin token for JS function management--pid-file <file> - PID file for daemon mode--log-file <file> - Log file for daemon modeHTTP Endpoints:
GET /health - Health checkGET / - API documentationPOST /eval - Evaluate expressions (JSON body)GET /eval?expr=... - Evaluate expressions (query params)POST /js/functions - Upload JavaScript functions (admin)GET /js/functions - List JavaScript functionsDELETE /js/functions/{name} - Delete JavaScript function (admin)Example API calls:
# Basic evaluation
curl -X POST http://localhost:5074/eval \
-H "Content-Type: application/json" \
-d '{"expression": "2 + 3 * 4"}'
# With variables and null safety
curl -X POST http://localhost:5074/eval \
-H "Content-Type: application/json" \
-d '{
"expression": ":data.filter(:x.value.to_s().length() > 0)",
"arguments": {
"data": [{"value": null}, {"value": "hello"}, {"value": ""}]
},
"include_variables": true
}'
# GET request with query params
curl "http://localhost:5074/eval?expr=SUM(1,2,3,4,5)"
# With authentication
curl -X POST http://localhost:5074/eval \
-H "Authorization: Bearer your_secret_token" \
-H "Content-Type: application/json" \
-d '{"expression": "null.to_s().length()"}'
sk_server)High-performance TCP server for custom protocol access:
# Basic usage
cargo run --bin sk_server 8080
# With worker threads
cargo run --bin sk_server 8080 16
# Production daemon
cargo run --bin sk_server 8080 8 -d --host 0.0.0.0 --token secret123
Parameters:
<port> - Port to bind (required)[num_threads] - Worker threads (optional)-H, --host <addr> - Bind address (default: 127.0.0.1)-d, --daemon - Run as background daemon--token <value> - Require authentication token--pid-file <file> - PID file for daemon mode--log-file <file> - Log file for daemon modeProtocol: JSON-based TCP protocol for maximum performance
Evaluate expressions:
use skillet::{evaluate, evaluate_with, Value};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Numeric
let v = evaluate("= 2 + 3 * 4")?; // -> Value::Number(14.0)
// Variables
let mut vars = HashMap::new();
vars.insert("sales".to_string(), Value::Number(5000.0));
let v = evaluate_with("=SUM(:sales, 1000)", &vars)?; // -> 6000.0
// Strings and chaining
let v = evaluate("= ' john '.trim().upper()")?; // -> "JOHN"
// Arrays + F/M/R
let v = evaluate("= [30,60,80,100].filter(:x > 50).map(:x * 0.9).sum()")?; // -> 216.0
// Named lambda parameters
let v = evaluate("= FILTER([1,2,3,4], :n % 2 == 0, 'n')")?; // -> [2,4]
// Type casting
let v = evaluate("= '42'::Integer")?; // -> 42
Ok(())
}
TRUE/FALSE), strings ('...' or "..."), NULL+ - * / % ^, > < >= <= == !=, AND/OR/NOT (also &&/||/!), ternary ? ::name (provided via evaluate_with map)SUM, AVG/AVERAGE, MIN, MAX, ROUND, CEIL, FLOOR, ABS, SQRT, POWARRAY, FIRST, LAST, CONTAINS, UNIQUE, SORT, REVERSE, JOIN, FLATTENCONCAT, UPPER, LOWER, TRIM, LENGTH, SPLIT, SUBSTITUTE, REPLACEDIG(json, path_array, [default])ISBLANKFILTER(array, expr, [param]), MAP(array, expr, [param]), REDUCE(array, expr, initial, [valParam], [accParam])SUMIF(array, expr), AVGIF(array, expr), COUNTIF(array, expr). and predicates ?
.abs() .round(n) .floor() .ceil(); predicates .positive? .negative? .zero? .even? .odd? .numeric?.length() .size() .first() .last() .sum() .avg() .min() .max() .sort() .unique() .reverse() .compact() .flatten().upper() .lower() .trim() .reverse()[1, 2, 3]; indexing arr[i] (negatives allowed); slicing arr[a:b]...expr inside arg listsexpr::Integer|Float|String|Boolean|Array|Currency|DateTime|Json= 2 + 3 * 4 β 14= :score >= 90 ? 'A' : 'B'= [1,2,3,4].map(:v * 10, 'v') β [10,20,30,40]= [1,2,3].reduce(:a + :v, 0, 'v', 'a') β 6= PRODUCT(2, 3, 4) β 24= SUMIF([1,-2,3,-4], :x > 0) β 4 (lambda-style)= SUMIF([10,20,30,40], ">25") β 70= FLATTEN([1,[2,[3]],4]) β [1,2,3,4]Value in src/types.rs.If you want the binaries such as sk, sk_server and sk_client installed system-wide:
cargo install skillet
Skillet includes a high-performance evaluation server that keeps the interpreter warm and eliminates per-process overhead.
sk_server 8080 (binds to 127.0.0.1:8080)sk_server 8080 -d (writes PID to skillet-server.pid in CWD)kill $(cat skillet-server.pid)sk_server 8080 --host 0.0.0.0 (listen on all interfaces)sk_server 8080 --host 0.0.0.0 --token <secret> (or set SKILLET_AUTH_TOKEN)Client and benchmarks:
sk_client localhost:8080 '=2+3*4'sk_client localhost:8080 '=SUM(:a,:b)' a=10 b=5sk_client localhost:8080 '=:user.name' --json '{"user":{"name":"Alice"}}'sk_client localhost:8080 --benchmark '=2+3*4' 10000sk_client localhost:8080 '=2+3*4' --token <secret> (or set SKILLET_SERVER_TOKEN)Scripts:
bash scripts/benchmark_server.sh [port] [iterations] [threads]take a look at the Server Usage Guide for more details about how to use it and consume in different langauages
SUM, PRODUCT/MULTIPLY, AVG/AVERAGE, MIN, MAX, ROUND, CEIL, CEILING, FLOOR, ABS, SQRT, POW/POWER, MOD, INTAND, OR, NOT, XOR, IF, IFSLENGTH, CONCAT, UPPER, LOWER, TRIM, SUBSTRING, SPLIT, REPLACE, REVERSE, ISBLANK, ISNUMBER, ISTEXTARRAY, FLATTEN, FIRST, LAST, CONTAINS, IN, COUNT, UNIQUE, SORT, REVERSE, JOINNOW, DATE, TIME, YEAR, MONTH, DAY, DATEADD, DATEDIFFPMT, DB, FV, IPMTMEDIAN, MODE.SNGL (MODESNGL, MODE_SNGL), STDEV.P (STDEVP, STDEV_P), VAR.P (VARP, VAR_P), PERCENTILE.INC (PERCENTILEINC, PERCENTILE_INC), QUARTILE.INC (QUARTILEINC, QUARTILE_INC)FILTER(array, expr, [param]), MAP(array, expr, [param]), REDUCE(array, expr, initial, [valParam], [accParam]), SUMIF(array, expr_or_criteria [, sum_array]), AVGIF(array, expr), COUNTIF(array, expr)parse(input: &str) -> Result<Expr, Error>: parse into ASTevaluate(input: &str) -> Result<Value, Error>: evaluate without variablesevaluate_with(input: &str, vars: &HashMap<String, Value>) -> Result<Value, Error>evaluate_with_json(input: &str, json_vars: &str) -> Result<Value, Error>evaluate_with_custom(input: &str, vars: &HashMap<String, Value>) -> Result<Value, Error>evaluate_with_json_custom(input: &str, json_vars: &str) -> Result<Value, Error>register_function(Box<dyn CustomFunction>) -> Result<(), Error>unregister_function(name: &str) -> boollist_custom_functions() -> Vec<String>Value enum: Number(f64) | Array(Vec<Value>) | Boolean(bool) | String(String) | Null | Currency(f64) | DateTime(i64) | Json(String)
Error with message and optional position
Run the test suite:
cargo test
MIT OR Apache-2.0