9 releases

Uses new Rust 2024

new 0.2.0 Nov 6, 2025
0.1.1 Nov 2, 2025
0.0.25 Apr 29, 2025

#294 in Text processing

46 downloads per month
Used in 3 crates (2 directly)

MIT license

140KB
2.5K SLoC

phlow logo

PHS – Phlow Script

PHS (Phlow Script) é um formato de script leve para Phlow, baseado em Rhai. Permite lógica dinâmica, manipulação de dados e integração profunda com módulos Phlow, tudo em arquivos .phs.

✨ Visão Geral

PHS traz o poder do scripting embutido para workflows YAML, permitindo lógica dinâmica, manipulação de variáveis, funções, arrays, objetos e integração com módulos Rust customizados.

Scripts .phs podem ser importados em flows Phlow via !import, e têm acesso global aos módulos declarados no YAML.

📑 Sumário

🔌 Injeção de Módulos via YAML

Todos os módulos declarados em modules: no YAML ficam disponíveis globalmente no script .phs.

🧪 Exemplo

main.phlow

main: cli
modules:
  - module: cli
  - module: log
steps:
  - return: !import script.phs

script.phs

log("warn", `Hello, ${main.name}`);
// or
log({
  level: "warn",
  message: `Hello, ${main.name}`
});

📁 Extensões de Arquivo

Phlow carrega scripts .phs via !import e executa com engine Rhai estendida.

� Módulos Suportados

Qualquer módulo com bindings de scripting pode ser usado: log, cli, http_server, etc.

🧠 Variáveis

Declare variáveis com let:

let nome = main.name;
let saudacao = "Olá";
let mensagem = `${saudacao}, ${nome}!`;
log("info", mensagem);

Reatribuição:

let cont = 1;
cont = cont + 1;

Funções podem retornar valores:

let status = "warn";
let msg = "Algo aconteceu";
log(status, msg);

🧱 Arrays e Objetos (Maps)

Arrays:

let frutas = ["maçã", "banana", "laranja"];
frutas.push("uva");

Objetos:

let usuario = #{ nome: main.name, idade: 30 };
usuario.idade = 31;
usuario.status = "online";

Nesting:

let config = #{ tags: ["dev"], options: #{ retries: 3 } };

🧭 Condicionais

if main.name == "Philippe" {
  log("info", "Bem-vindo!");
} else if main.name == "Alice" {
  log("info", "Oi Alice!");
} else {
  log("info", "Olá, visitante!");
}

🔁 Loops

for fruta in frutas {
  log("info", `Fruta: ${fruta}`);
}
for i in 0..5 {
  log("debug", `Índice: ${i}`);
}

🧩 Funções

Defina funções com fn:

fn saudacao(nome) {
  log("info", `Olá, ${nome}!");
}
saudacao("Philippe");

Funções podem retornar valores:

fn dobro(n) { return n * 2; }
let resultado = dobro(5);

🧬 Sintaxe e Recursos

Tipos suportados: bool, string, int, float, array, null, map, fn Operadores: +, -, *, /, %, ==, !=, <, >, <=, >=, &&, ||, ! Escopo global: main, modules, funções utilitárias

Expressões & Ternário

let msg = when main.name == "" ? "Anônimo" : `Olá, ${main.name}`;

Funções de String

  • search(pattern) — regex
  • starts_with(prefix) — prefixo
  • replace(target, replacement) — substituição
  • slice(start, end) / slice(start) — substring
  • capitalize() — primeira letra maiúscula
  • to_snake_case(), to_camel_case(), to_kebab_case() — conversão de case
  • to_url_encode() — codificação URL
  • to_base64() — codificação Base64
  • base64_to_utf8() — decodificação Base64
  • url_decode() — decodificação URL

Conversão de Tipos

let numero = "42".to_int();
let flag = "true".to_bool();

Manipulação de Maps & Arrays

let chaves = usuario.keys();
if frutas.contains("banana") { log("info", "Achou!"); }

Debug

log("debug", `Debug: ${data}`);

Acesso aninhado YAML

config:
  retries: 3
  labels:
    - core
    - beta
let retry = main.config.retries;
let tag = main.config.labels[0];

Notas Futuras

  • break / continue — não suportado
  • match — planejado
  • try/catch — TBD

Para mais detalhes, consulte a documentação oficial do Phlow e do Rhai.

  • Standard encoding: Uses the standard Base64 alphabet
  • Automatic padding: Adds = characters when needed
  • UTF-8 support: Handles special characters correctly
  • Binary safe: Works with any byte sequence

base64_to_utf8() - Base64 Decoding

Decode Base64 strings back to UTF-8 text:

"SGVsbG8gV29ybGQ=".base64_to_utf8();        // "Hello World"
"dXNlckBleGFtcGxlLmNvbQ==".base64_to_utf8(); // "user@example.com"
"Y2Fmw6k=".base64_to_utf8();                // "café"
"MTIzNDU=".base64_to_utf8();                // "12345"
"".base64_to_utf8();                        // ""
"invalid_base64!@#".base64_to_utf8();       // "" (empty on error)

Features:

  • Standard decoding: Uses the standard Base64 alphabet
  • UTF-8 validation: Returns empty string if result is not valid UTF-8
  • Error handling: Returns empty string for invalid Base64 input
  • Safe operation: Never crashes on malformed input

🔓 url_decode() - URL Decoding

Decode URL-encoded strings back to UTF-8 text:

"Hello+World".url_decode();              // "Hello World"
"user%40example.com".url_decode();       // "user@example.com"
"caf%C3%A9+%26+ma%C3%A7%C3%A3".url_decode(); // "café & maçã"
"abc-123_test.file~".url_decode();       // "abc-123_test.file~" (unchanged)
"Ol%C3%A1+mundo%21".url_decode();        // "Olá mundo!"
"%ZZ".url_decode();                      // "%ZZ" (invalid hex preserved)
"test%".url_decode();                    // "test%" (incomplete sequence preserved)

Features:

  • RFC 3986 compliant: Handles standard URL encoding rules
  • Plus to space: Converts + characters to spaces
  • UTF-8 support: Properly decodes multi-byte UTF-8 sequences
  • Error tolerance: Preserves malformed sequences rather than failing
  • Safe operation: Returns empty string only for UTF-8 validation errors

�📋 parse() - JSON Parser

Parse JSON strings into native Rhai types:

"\"hello world\"".parse();    // "hello world" (string)
"42".parse();                 // 42 (integer)
"3.14".parse();               // 3.14 (float)
"true".parse();               // true (boolean)
"false".parse();              // false (boolean)
"null".parse();               // () (unit/null)

// Objects are converted to Rhai Maps
let obj = "{\"name\":\"João\",\"age\":30}".parse();
obj.name;                     // "João"
obj.age;                      // 30

// Arrays are converted to Rhai Arrays
let arr = "[1, 2, 3, \"test\"]".parse();
arr[0];                       // 1
arr[3];                       // "test"
arr.len();                    // 4

// Nested structures work too
let nested = "{\"user\":{\"name\":\"Maria\",\"roles\":[\"admin\",\"user\"]}}".parse();
nested.user.name;             // "Maria"
nested.user.roles[0];         // "admin"

Features:

  • Type conversion: Automatically converts to appropriate Rhai types
  • Primitive support: Handles strings, numbers, booleans, and null
  • Native structures: Objects become Maps, arrays become Arrays
  • Nested support: Handles complex nested JSON structures
  • Direct access: Use dot notation and indexing on parsed objects/arrays
  • Error handling: Returns null (unit) for invalid JSON
  • Safe parsing: Never crashes on malformed input

📖 Additional String Methodsehavior scripting using .phs files while deeply integrating with the Phlow runtime and module system.

✨ Overview

PHS (Phlow Script) brings the power of embedded scripting to YAML-based workflows. It's designed to let you inject dynamic logic through readable scripts, while preserving Phlow's declarative style.

You can inject modules directly into your PHS context via the modules section of your .yaml configuration. Each module declared becomes globally accessible in the .phs script, making it easy to mix scripting with orchestrated steps.

📑 Summary

🔌 Module Injection via YAML

All modules declared in the YAML under modules: are automatically available inside your .phs script. For example, when you load the log module, its functions can be used directly in the script.

🧪 Example

main.phlow

main: cli
name: Example Cli
version: 1.0.0
description: Example CLI module
author: Your Name

modules: 
  - module: cli
    version: latest
    with:
      additional_args: false
      args: 
        - name: name
          description: Name of the user
          index: 1
          type: string
          required: false
  - module: log
    version: latest

steps:
  - return: !import script.phs

script.phs

log("warn", `Hello, ${main.name}`);

💡Output

If the user runs:

phlow run main.phlow --name Philippe

The script will log:

[warn] Hello, Philippe

📁 File Extensions

Phlow automatically loads .phs scripts when referenced in the flow via !import. These scripts are parsed and executed using the internal Rhai engine extended with Phlow modules.

🔐 Modules Supported in PHS

Any module that exposes scripting bindings can be used. Example modules:

  • log
  • cli
  • http_server
  • (and any custom Rust module registered with bindings)

🧠 Variables in PHS

You can declare and use variables in .phs scripts using the let keyword. These variables help you store temporary values, compose strings, perform calculations, or reuse values throughout your script.

🔤 Declaring Variables

let name = main.name;
let greeting = "Hello";
let message = `${greeting}, ${name}!`;

log("info", message);

✍️ Reassigning Values

Variables can be reassigned at any point:

let count = 1;
count = count + 1;

🔄 Using Function Results

You can assign the result of a function to a variable:

let status = "warn";
let msg = "Something happened";

log(status, msg);

🧱 Arrays and objects (maps)

PHS allows you to work with arrays and objects (maps) natively. These are useful when handling lists of items, grouping values, or building dynamic data structures.

📚 Arrays

You can create arrays using square brackets []:

let fruits = ["apple", "banana", "orange"];
log("info", `First fruit: ${fruits[0]}`);
➕ Adding Items

fruits.push("grape");

🔄 Looping Through Arrays

for fruit in fruits {
  log("debug", `Fruit: ${fruit}`);
}

🧳 Objects (Maps)

You can define key-value objects using curly braces {}:

let user = #{
  name: main.name,
  age: 30,
  active: true
};

log("info", `User: ${user.name} (age: ${user.age})`);
🔧 Updating Properties

user.age = 31;
user.status = "online";

📦 Nesting

Objects and arrays can be nested:

let config = #{
  tags: ["dev", "backend"],
  options: #{
    retries: 3,
    timeout: 1000
  }
};

log("debug", `Retries: ${config.options.retries}`);

🧭 Conditionals in PHS

PHS supports conditional logic using if, else if, and else blocks. These let you define dynamic behaviors based on data or user input.

✅ Basic If

if main.name == "Philippe" {
  log("info", "Welcome back, boss!");
}

🔁 If...Else

if main.name == "Alice" {
  log("info", "Hi Alice!");
} else {
  log("info", "Hello, guest!");
}

🔀 Else If

if main.name == "Bob" {
  log("info", "Hello Bob!");
} else if main.name == "Charlie" {
  log("info", "Hey Charlie!");
} else {
  log("info", "Who are you?");
}

🔗 Nested Conditions

if main.name != "" {
  if main.name.len > 5 {
    log("debug", "That's a long name.");
  } else {
    log("debug", "Short and sweet.");
  }
}

Conditionals are a great way to adapt the behavior of your script based on CLI arguments, environment values, or runtime results.

🔁 Loops in PHS

PHS supports looping structures to help you iterate over arrays or repeat actions multiple times. The most common loop you'll use is the for loop.

📚 Looping Through an Array

let fruits = ["apple", "banana", "orange"];

for fruit in fruits {
  log("info", `Fruit: ${fruit}`);
}

🔢 Looping with a Range

You can loop through a range of numbers:

for i in 0..5 {
  log("debug", `Index: ${i}`);
}

This prints numbers from 0 to 4.

🔄 Nested Loops

Loops can be nested for handling multi-dimensional data:

let matrix = [
  [1, 2],
  [3, 4]
];

for row in matrix {
  for value in row {
    log("debug", `Value: ${value}`);
  }
}

🛑 Breaking a Loop (not supported yet)

Currently, there's no support for break or continue in .phs. Keep your loops simple and controlled with conditions when needed.

Loops are powerful for automating repetitive tasks or handling collections of data. Combine them with conditionals and functions to build expressive scripts.

🧩 Functions in PHS

You can define your own functions in .phs to reuse logic, organize your code, and make scripts cleaner and more modular.

🛠 Defining a Function

Use the fn keyword:

fn greet(name) {
  log("info", `Hello, ${name}!`);
}

▶️ Calling a Function

Once defined, just call it like this:

greet("Philippe");

This will log:

[info] Hello, Philippe!

↩️ Returning Values

Functions can return values using return:

fn double(n) {
  return n * 2;
}

let result = double(5);
log("debug", `Result: ${result}`);

🧠 Functions with Logic

You can include conditionals, loops, and other functions inside your custom function:

fn log_fruits(fruits) {
  for fruit in fruits {
    log("info", `Fruit: ${fruit}`);
  }
}

let list = ["apple", "banana", "orange"];
log_fruits(list);

⚠️ Scope

Variables declared inside a function are local to that function unless returned or passed back explicitly.

🧬 PHS Syntax and Language Features

This guide expands on PHS (Phlow Script)'s syntax, types, and scripting features.

📐 Data Types in PHS

PHS supports common primitive types, plus arrays and maps (objects):

Type Example
bool true, false
string "hello", `hi ${name}`
int 42
float 3.14 (if enabled)
array [1, 2, 3]
null null
map { key: "value" }
fn fn name(x) { ... }

➕ Operators

Operator Description Example
+ Add / Concatenate 2 + 3, "a" + "b"
- Subtract 10 - 4
* Multiply 5 * 6
/ Divide 9 / 3
% Modulo 10 % 3
== Equals x == y
!= Not equal x != y
<, >, <=, >= Comparisons x >= 10
&& Logical AND x && y
` `
! Logical NOT !x

🌐 Global Scope

  • main – the full YAML input
  • Declared modules – globally exposed
  • Utility functions like log(...)

🧪 Expressions & Statements

let upper = main.name.to_uppercase().trim();

🔀 Ternary Expressions

PHS supports ternary expressions using the when keyword for conditional logic:

let msg = when main.name == "" ? "Anonymous" : `Hello, ${main.name}`;
let status = when age >= 18 ? "adult" : "minor";
let value = when condition ? true_value : false_value;

🔤 String Functions

PHS includes several custom string manipulation functions in addition to Rhai's built-in string methods:

🔍 search(pattern) - Regex Pattern Matching

Search for regex patterns in strings, returns true if found:

let text = "Hello World";
let hasHello = text.search("Hello");        // true
let startsWithH = text.search("^H");        // true (regex: starts with H)
let endsWithD = text.search("d$");          // true (regex: ends with d)
let hasNumbers = text.search("[0-9]");      // false

🎯 starts_with(prefix) - Prefix Checking

Check if a string starts with a specific prefix:

let auth = "Bearer abc123";
let hasBearer = auth.starts_with("Bearer");     // true
let hasSpace = auth.starts_with("Bearer ");     // true
let isBasic = auth.starts_with("Basic");        // false

let email = "user@example.com";
let isUserEmail = email.starts_with("user");   // true
let hasAt = email.starts_with("@");             // false

let empty = "";
let anyString = "test".starts_with("");         // true (empty prefix always matches)

Features:

  • Case-sensitive: Distinguishes between uppercase and lowercase
  • Exact matching: No regex patterns, just literal string comparison
  • Fast operation: More efficient than regex for simple prefix checks
  • Empty prefix: Always returns true for empty string prefix
  • Safe operation: Never fails, returns false for invalid cases

When to use starts_with() vs search():

  • Use starts_with("prefix") for simple prefix checking (faster)
  • Use search("^prefix") for regex-based prefix checking with patterns

🔄 replace(target, replacement) - String Replacement

⚠️ Important: Unlike native Rhai replace, this function returns the modified string instead of changing the variable in place:

let text = "Hello World";
let newText = text.replace("World", "Universe");  // Returns "Hello Universe"
// text is still "Hello World" - original unchanged

✂️ slice(start, end) / slice(start) - Substring Extraction

Extract a portion of a string with bounds checking. Supports two variants:

Two parameters - slice(start, end):

let text = "Hello World";
let part = text.slice(0, 5);     // "Hello"
let middle = text.slice(6, 11);  // "World"
let safe = text.slice(0, 100);   // "Hello World" (auto-bounds)

One parameter - slice(start):

let text = "abcdef";
let fromIndex = text.slice(3);    // "def" (from index 3 to end)
let lastTwo = text.slice(-2);     // "ef" (last 2 characters)
let fromStart = text.slice(0);    // "abcdef" (entire string)
  • Positive index: Takes from that position to the end
  • Negative index: Takes the last N characters

🎩 capitalize() - First Letter Uppercase

Capitalize the first character of a string:

let name = "joão";
let capitalized = name.capitalize();  // "João"
let empty = "".capitalize();         // ""

🐍 Case Conversion Functions

Convert between different naming conventions. These functions automatically detect the current format:

// to_snake_case() - Convert to snake_case
"meuTextoExemplo".to_snake_case();    // "meu_texto_exemplo"
"MeuTextoExemplo".to_snake_case();    // "meu_texto_exemplo"  
"meu-texto-exemplo".to_snake_case();  // "meu_texto_exemplo"
"Meu texto exemplo".to_snake_case();  // "meu_texto_exemplo"

// to_camel_case() - Convert to camelCase
"meu_texto_exemplo".to_camel_case();  // "meuTextoExemplo"
"meu-texto-exemplo".to_camel_case();  // "meuTextoExemplo"
"Meu texto exemplo".to_camel_case();  // "meuTextoExemplo"

// to_kebab_case() - Convert to kebab-case
"meuTextoExemplo".to_kebab_case();    // "meu-texto-exemplo"
"meu_texto_exemplo".to_kebab_case();  // "meu-texto-exemplo"
"Meu texto exemplo".to_kebab_case();  // "meu-texto-exemplo"

to_url_encode() - URL Encoding

Encode strings for safe use in URLs following RFC 3986:

"Hello World".to_url_encode();        // "Hello+World"
"user@example.com".to_url_encode();   // "user%40example.com"
"café & maçã".to_url_encode();        // "caf%C3%A9+%26+ma%C3%A7%C3%A3"
"abc-123_test.file~".to_url_encode(); // "abc-123_test.file~" (unchanged)

Encoding rules:

  • Safe characters: Letters, numbers, -, _, ., ~ remain unchanged
  • Spaces: Converted to +
  • Other characters: Encoded as %XX (hexadecimal)
  • UTF-8: Full support for multi-byte characters

🔐 to_base64() - Base64 Encoding

Encode strings to Base64 format (RFC 4648):

"Hello World".to_base64();      // "SGVsbG8gV29ybGQ="
"user@example.com".to_base64(); // "dXNlckBleGFtcGxlLmNvbQ=="
"café".to_base64();             // "Y2Fmw6k="
"12345".to_base64();            // "MTIzNDU="
"".to_base64();                 // ""

Features:

  • Standard encoding: Uses the standard Base64 alphabet
  • Automatic padding: Adds = characters when needed
  • UTF-8 support: Handles special characters correctly
  • Binary safe: Works with any byte sequence

�📖 Additional String Methods

For more string manipulation functions, refer to Rhai Language Reference.

🔎 Type Conversion Helpers

let number = "42".to_int();
let flag = "true".to_bool();

🛠 Working with Maps & Arrays

let keys = user.keys();
let vals = user.values();
if fruits.contains("banana") {
  log("info", "Found it!");
}

🧯 Error Handling

Structured try/catch is not supported.

🪛 Debugging Tools

log("debug", `Debugging var: ${data}`);

🧬 Nested Access in YAML

config:
  retries: 3
  labels:
    - core
    - beta
let retry = main.config.retries;
let tag = main.config.labels[0];

📍Future Support Notes

  • break / continuenot supported yet
  • match / pattern matching → planned
  • try/catchTBD

📚 Catálogo de Funções do Engine (com exemplos)

Esta seção lista as funções utilitárias disponíveis globalmente em scripts .phs via o engine do PHS. Todas podem ser usadas diretamente nas expressões do seu script.

🕒 Datas e Tempo

  • today() -> int — Timestamp (segundos) do início do dia UTC.
    • Ex.: let t0 = today();
  • now() -> int — Timestamp atual (segundos).
    • Ex.: let ts = now();
  • format(ts, fmt) -> string — Formata um timestamp com strftime.
    • Ex.: format(1692362096, "%d/%m/%Y %H:%M:%S") // "18/08/2023 12:34:56"
  • diff(ts1, ts2) -> int — Diferença em segundos: ts1 - ts2.
    • Ex.: diff(now(), today())
  • add_days(ts, n) -> int
    • Ex.: add_days(now(), 2)
  • add_hours(ts, n) -> int, add_minutes(ts, n) -> int, add_seconds(ts, n) -> int
    • Ex.: add_minutes(1000, 2) // 1120
  • sub_minutes(ts, n) -> int, sub_seconds(ts, n) -> int
    • Ex.: sub_seconds(1000, 10) // 990
  • weekday(ts) -> int — 0=Dom, 1=Seg, ...
    • Ex.: weekday(1692362096) // 5 (sexta)
  • year(ts), month(ts), day(ts), hour(ts), minute(ts), second(ts)
    • Ex.: year(1692362096) // 2023
  • from_iso(iso) -> int — Converte ISO-8601 para timestamp.
    • Ex.: from_iso("2023-08-18T12:34:56Z") // 1692362096
  • to_iso(ts) -> string — Converte timestamp para ISO-8601.
    • Ex.: to_iso(1692362096) // "2023-08-18T12:34:56+00:00"

🔤 Strings

  • search(pattern: regex) -> bool (método de string)
    • Ex.: "meu texto".search("^meu") // true
  • starts_with(prefix) -> bool (método de string)
    • Ex.: "Bearer abc".starts_with("Bearer ") // true
  • contains(substr) -> bool (método de string)
    • Ex.: "The quick brown fox".contains("brown") // true
  • replace(target, replacement) -> string (retorna nova string)
    • Ex.: "abc abc".replace("abc", "x") // "x x"
  • slice(start, end?) -> string (método de string)
    • Ex.: "abcdef".slice(1,4) // "bcd", "abcdef".slice(-2) // "ef"
  • capitalize() -> string
    • Ex.: "joão".capitalize() // "João"
  • to_snake_case(), to_camel_case(), to_kebab_case()
    • Ex.: "MeuTexto".to_snake_case() // "meu_texto"
  • to_url_encode() -> string
    • Ex.: "Hello World".to_url_encode() // "Hello+World"
  • to_base64() -> string
    • Ex.: "café".to_base64() // "Y2Fmw6k="
  • base64_to_utf8() -> string
    • Ex.: "SGVsbG8gV29ybGQ=".base64_to_utf8() // "Hello World"
  • url_decode() -> string
    • Ex.: "user%40example.com".url_decode() // "user@example.com"

SQL-like Matching

  • like(pattern) -> bool
    • Usa curingas % (qualquer sequência) e _ (um caractere). O padrão deve casar a string inteira.
    • Ex.: "hello world".like("h%o w%ld") // true
  • ilike(pattern) -> bool
    • Igual ao like, mas case-insensitive.
    • Ex.: "hello world".ilike("H%O W%LD") // true
  • not_like(pattern) -> bool
    • Negação do like.
    • Ex.: "hello world".not_like("H%X W%LD") // true
  • not_ilike(pattern) -> bool
    • Negação do ilike.
    • Ex.: "hello world".not_ilike("H%X W%LD") // true

🧩 Objetos (Maps) e Arrays

  • merge(mapA, mapB) -> map — Junta mapas; mapB sobrescreve chaves.
    • Ex.: merge(#{a:1,b:2}, #{b:3,c:4}) // #{a:1,b:3,c:4}
  • contains_key(key) -> bool (método de map)
    • Ex.: #{a:1}.contains_key("a") // true
  • some(|item| expr) -> bool (método de array)
    • Ex.: [1,2,3].some(|x| x > 2) // true
    • Ex.: [#{a:1}, #{b:2}].some(|obj| obj.contains_key("b")) // true

Observação: Funções internas __spread_object(arrDeMaps) e __spread_array(arrDeArrays) existem para suporte a sintaxe de spread e não são recomendadas para uso direto em scripts.

🔄 Conversão e JSON

  • parse(jsonStr) -> any — Converte JSON em tipos Rhai (map, array, string, número, bool, null).
    • Ex.: let obj = "{\"name\":\"João\"}".parse(); obj.name // "João"
    • Ex.: let arr = "[1,2,\"x\"]".parse(); arr[2] // "x"
  • to_json(valor) -> string — Converte qualquer valor Rhai em JSON string.
    • Ex.: #{name: "João", age: 30}.to_json()

🆔 UUIDs

  • uuid("v4"), uuid("v6"), uuid("v7") — Gera UUIDs sem entrada.
    • Ex.: let id = uuid("v7");
  • uuid("v3", seed), uuid("v5", seed) — Gera UUID determinístico baseado em seed.
    • Ex.: uuid("v5", "example.com")

✅ Predicados de Tipo e Validação

  • is_null(x), is_not_null(x)
  • is_empty(x) e também "str".is_empty()
  • is_array(x), is_object(x), is_string(x)
  • is_number(x), is_boolean(x)
  • is_datetime(x) — valida string ISO-8601
  • is_float(x), is_int(x)

🧪 Sintaxe Condicional Custom

  • when <cond> then <expr> else <expr>
    • Ex.: when 2 + 2 == 4 then "certo" else "errado"
  • it <cond> ? <expr> : <expr>
    • Ex.: it "abc".search("b") ? "encontrou" : "não"

Se notar alguma função ausente aqui, abra uma issue ou PR — essa seção é gerada a partir das utilidades registradas no engine em phs/src/functions.rs.

Dependencies

~17MB
~290K SLoC