# Reusable Validation Library for Nickel { 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]?)$" }, 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}$" }, PortContract = { label = "ValidPort", predicate = fun p => p > 0 && p < 65536 }, SemverContract = { label = "ValidSemver", predicate = fun v => std.string.is_match v "^\\d+\\.\\d+\\.\\d+$" }, DomainContract = { label = "ValidDomain", predicate = fun d => std.string.is_match d "^[a-z0-9]([a-z0-9-\\.]{0,253}[a-z0-9])?$" }, OciTagContract = { label = "ValidOCITag", predicate = fun tag => std.string.is_match tag "^[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}$" }, 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$" }, PathContract = { label = "ValidPath", predicate = fun path => std.string.length path > 0 && !std.string.contains path "//" }, min_length = fun min_val => { label = "MinLength%{std.to_string min_val}", predicate = fun s => std.string.length s >= min_val, }, max_length = fun max_val => { label = "MaxLength%{std.to_string max_val}", predicate = fun s => std.string.length s <= max_val, }, 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, }, enum = fun values => { label = "Enum[%{std.string.join "," values}]", predicate = fun v => std.array.elem v values, }, non_empty_string = { label = "NonEmptyString", predicate = fun s => std.string.length s > 0 }, non_negative = { label = "NonNegative", predicate = fun n => n >= 0 }, positive = { label = "Positive", predicate = fun n => n > 0 }, boolean_value = { label = "Boolean", predicate = fun b => b == true || b == false }, all_items = fun pred => fun items => std.array.all pred items, any_items = fun pred => fun items => std.array.any pred items, has_keys = fun required_keys => fun record => std.array.all (fun key => std.record.has_field record key) required_keys, IpRef = std.contract.custom ( fun _label => fun value => if value == "" || std.string.is_match "^(\\d{1,3}\\.){3}\\d{1,3}$" value || std.string.contains ":" value || std.string.contains "fip" value then 'Ok value else 'Error { message = "expected empty, an IPv4 address, an IPv6 address (contains ':'), or a FIP name (contains 'fip'); got '%{value}'" } ), }