provisioning/schemas/lib/validation.ncl
Jesús Pérez 44648e3206
chore: complete nickel migration and consolidate legacy configs
- Remove KCL ecosystem (~220 files deleted)
- Migrate all infrastructure to Nickel schema system
- Consolidate documentation: legacy docs → provisioning/docs/src/
- Add CI/CD workflows (.github/) and Rust build config (.cargo/)
- Update core system for Nickel schema parsing
- Update README.md and CHANGES.md for v5.0.0 release
- Fix pre-commit hooks: end-of-file, trailing-whitespace
- Breaking changes: KCL workspaces require migration
- Migration bridge available in docs/src/development/
2026-01-08 09:55:37 +00:00

184 lines
4.6 KiB
Plaintext

# | Reusable Validation Library for Nickel
# | Common validation contracts and helper functions
# | Author: JesusPerezLorenzo
# | Date: 2025-12-15
# | Status: Production Ready
# ============================================================
# Common Validation Contracts
# ============================================================
# IPv4 address validation (e.g., "192.168.1.1")
let IpV4Contract = {
label = "ValidIPv4",
predicate = fun ip =>
std.string.is_match ip "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
}
# CIDR notation validation (e.g., "192.168.1.0/24")
let CidrContract = {
label = "ValidCIDR",
predicate = fun cidr =>
std.string.is_match cidr "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$"
}
# Port range validation (1-65535)
let PortContract = {
label = "ValidPort",
predicate = fun p =>
p > 0 && p < 65536
}
# Semantic versioning validation (e.g., "1.2.3")
let SemverContract = {
label = "ValidSemver",
predicate = fun v =>
std.string.is_match v "^\\d+\\.\\d+\\.\\d+$"
}
# Domain name validation (e.g., "example.com")
let DomainContract = {
label = "ValidDomain",
predicate = fun d =>
std.string.is_match d "^[a-z0-9]([a-z0-9-\\.]{0,253}[a-z0-9])?$"
}
# OCI tag validation (e.g., "latest", "v1.0.0", "sha256-abc123")
let OciTagContract = {
label = "ValidOCITag",
predicate = fun tag =>
std.string.is_match tag "^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$"
}
# ISO 8601 timestamp validation (e.g., "2025-12-15T10:30:00Z")
let Iso8601Contract = {
label = "ValidISO8601",
predicate = fun ts =>
std.string.is_match ts "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$"
}
# Filesystem path validation (simple: non-empty, no double slashes)
let PathContract = {
label = "ValidPath",
predicate = fun path =>
std.string.length path > 0 && !std.string.contains path "//"
}
# ============================================================
# Helper Functions for Common Validations
# ============================================================
# Validate minimum string length
let min_length = fun min_val =>
{
label = "MinLength%{std.to_string min_val}",
predicate = fun s =>
std.string.length s >= min_val
}
# Validate maximum string length
let max_length = fun max_val =>
{
label = "MaxLength%{std.to_string max_val}",
predicate = fun s =>
std.string.length s <= max_val
}
# Validate numeric range
let range = fun min_val max_val =>
{
label = "Range[%{std.to_string min_val}-%{std.to_string max_val}]",
predicate = fun n =>
n >= min_val && n <= max_val
}
# Validate enum (value must be in list)
let enum = fun values =>
{
label = "Enum[%{std.string.join "," values}]",
predicate = fun v =>
std.array.elem v values
}
# Validate non-empty string
let non_empty_string = {
label = "NonEmptyString",
predicate = fun s =>
std.string.length s > 0
}
# Validate non-negative number
let non_negative = {
label = "NonNegative",
predicate = fun n =>
n >= 0
}
# Validate positive number
let positive = {
label = "Positive",
predicate = fun n =>
n > 0
}
# Validate boolean value
let boolean_value = {
label = "Boolean",
predicate = fun b =>
b == true || b == false
}
# ============================================================
# Helper Functions for Custom Validation
# ============================================================
# Validate all items in array satisfy predicate
let all_items = fun predicate =>
fun items =>
std.array.all predicate items
# Validate at least one item in array satisfies predicate
let any_items = fun predicate =>
fun items =>
std.array.any predicate items
# Validate record has all required keys
let has_keys = fun required_keys =>
fun record =>
std.array.all
(fun key =>
std.record.has_field record key
)
required_keys
# ============================================================
# Exports
# ============================================================
{
# Core validation contracts
IpV4Contract = IpV4Contract,
CidrContract = CidrContract,
PortContract = PortContract,
SemverContract = SemverContract,
DomainContract = DomainContract,
OciTagContract = OciTagContract,
Iso8601Contract = Iso8601Contract,
PathContract = PathContract,
# Helper functions
min_length = min_length,
max_length = max_length,
range = range,
enum = enum,
non_empty_string = non_empty_string,
non_negative = non_negative,
positive = positive,
boolean_value = boolean_value,
# Custom validators
all_items = all_items,
any_items = any_items,
has_keys = has_keys,
}