184 lines
4.6 KiB
Plaintext
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,
|
||
|
|
}
|