import gleam/dict.{type Dict} import gleam/dynamic import gleam/option.{type Option} import pedantic/codec import pedantic/decode import pedantic/encode import pedantic/json import pedantic/report // Re-export core types pub type Codec(t) = codec.Codec(t) pub type Decoder(t) = decode.Decoder(t) pub type Encoder(t) = encode.Encoder(t) pub type Report = report.Report pub type Error = report.Error pub type Warning = report.Warning pub type Path = report.Path pub type PathSegment = report.PathSegment pub type ErrorKind = report.ErrorKind pub type WarningKind = report.WarningKind pub type TypeGraph = report.TypeGraph pub type TypeField = report.TypeField pub type ValidationIssue = report.ValidationIssue pub type ValidationReport = report.ValidationReport pub type ValidationWarning = report.ValidationWarning pub type IssueKind = report.IssueKind // Re-export primitive codecs (Coerced & Exact) pub const coerced_int = codec.coerced_int pub const exact_int = codec.exact_int pub const coerced_float = codec.coerced_float pub const exact_float = codec.exact_float pub const coerced_bool = codec.coerced_bool pub const exact_bool = codec.exact_bool pub const coerced_string = codec.coerced_string pub const exact_string = codec.exact_string // Default aliases (default to lax coercion) pub const int = codec.int pub const string = codec.string pub const bool = codec.bool pub const float = codec.float // Re-export combinator functions pub fn list(element_codec: Codec(a)) -> Codec(List(a)) { codec.list(element_codec) } pub fn optional(inner_codec: Codec(a)) -> Codec(Option(a)) { codec.optional(inner_codec) } pub fn dict(value_codec: Codec(a)) -> Codec(Dict(String, a)) { codec.dict(value_codec) } // Re-export object builder functions pub fn object(name: String, constructor: constructor) { codec.object(name, constructor) } pub fn field( builder: codec.ObjectBuilder(fn(field_type) -> next, value), name: String, field_codec: Codec(field_type), get: fn(value) -> field_type, ) { codec.field(builder, name, field_codec, get) } pub const key_required = codec.key_required pub const key = codec.key pub fn key_optional( builder: codec.ObjectBuilder(fn(Option(field_type)) -> next, value), name: String, field_codec: Codec(field_type), get: fn(value) -> Option(field_type), ) { codec.key_optional(builder, name, field_codec, get) } pub fn key_with_default( builder: codec.ObjectBuilder(fn(field_type) -> next, value), name: String, field_codec: Codec(field_type), default_val: field_type, get: fn(value) -> field_type, ) { codec.key_with_default(builder, name, field_codec, default_val, get) } pub fn build(builder: codec.ObjectBuilder(value, value)) -> Codec(value) { codec.build(builder) } pub fn default(codec: Codec(Option(a)), default_val: a) -> Codec(a) { codec.default(codec, default_val) } pub const default_to = codec.default_to pub const unwrap_or = codec.unwrap_or /// Creates a Codec by custom pairing a standalone Decoder and Encoder. pub fn custom(decoder: Decoder(t), encoder: Encoder(t)) -> Codec(t) { codec.custom(decoder, encoder) } // Re-export validations pub fn refine( codec: Codec(t), rule: fn(t) -> Result(t, ErrorKind), ) -> Codec(t) { codec.refine(codec, rule) } pub fn min(codec: Codec(Int), minimum: Int) -> Codec(Int) { codec.min(codec, minimum) } pub fn max(codec: Codec(Int), maximum: Int) -> Codec(Int) { codec.max(codec, maximum) } pub fn min_length(codec: Codec(String), minimum: Int) -> Codec(String) { codec.min_length(codec, minimum) } pub fn max_length(codec: Codec(String), maximum: Int) -> Codec(String) { codec.max_length(codec, maximum) } // Zod-aligned fluent validations pub fn trim(codec: Codec(String)) -> Codec(String) { codec.trim(codec) } pub fn lowercase(codec: Codec(String)) -> Codec(String) { codec.lowercase(codec) } pub fn uppercase(codec: Codec(String)) -> Codec(String) { codec.uppercase(codec) } pub fn collapse_whitespace(codec: Codec(String)) -> Codec(String) { codec.collapse_whitespace(codec) } pub fn replace( codec: Codec(String), pattern: String, replacement: String, ) -> Codec(String) { codec.replace(codec, pattern, replacement) } pub fn remove_chars(codec: Codec(String), characters: String) -> Codec(String) { codec.remove_chars(codec, characters) } pub fn allow_chars(codec: Codec(String), characters: String) -> Codec(String) { codec.allow_chars(codec, characters) } pub fn strip_control_chars(codec: Codec(String)) -> Codec(String) { codec.strip_control_chars(codec) } pub fn normalize_email(codec: Codec(String)) -> Codec(String) { codec.normalize_email(codec) } pub fn normalize_uuid(codec: Codec(String)) -> Codec(String) { codec.normalize_uuid(codec) } pub fn default_if_blank( codec: Codec(String), default_value: String, ) -> Codec(String) { codec.default_if_blank(codec, default_value) } pub fn empty_as_none(codec: Codec(String)) -> Codec(Option(String)) { codec.empty_as_none(codec) } pub fn digits_only(codec: Codec(String)) -> Codec(String) { codec.digits_only(codec) } pub fn letters_only(codec: Codec(String)) -> Codec(String) { codec.letters_only(codec) } pub fn remove_whitespace(codec: Codec(String)) -> Codec(String) { codec.remove_whitespace(codec) } pub fn normalize_line_endings(codec: Codec(String)) -> Codec(String) { codec.normalize_line_endings(codec) } pub fn strip_non_ascii(codec: Codec(String)) -> Codec(String) { codec.strip_non_ascii(codec) } pub fn positive(codec: Codec(Int)) -> Codec(Int) { codec.positive(codec) } pub fn negative(codec: Codec(Int)) -> Codec(Int) { codec.negative(codec) } pub fn non_negative(codec: Codec(Int)) -> Codec(Int) { codec.non_negative(codec) } pub fn non_positive(codec: Codec(Int)) -> Codec(Int) { codec.non_positive(codec) } pub fn length(codec: Codec(String), len: Int) -> Codec(String) { codec.length(codec, len) } pub fn email(codec: Codec(String), msg: String) -> Codec(String) { codec.email(codec, msg) } pub fn uuid(codec: Codec(String), msg: String) -> Codec(String) { codec.uuid(codec, msg) } pub fn check( codec: Codec(t), predicate: fn(t) -> Bool, msg: String, ) -> Codec(t) { codec.check(codec, predicate, msg) } pub fn assert_that( codec: Codec(t), predicate: fn(t) -> Bool, error: ErrorKind, ) -> Codec(t) { codec.assert_that(codec, predicate, error) } pub fn with_error_formatter( codec: Codec(t), formatter: fn(dynamic.Dynamic, IssueKind) -> String, ) -> Codec(t) { codec.with_error_formatter(codec, formatter) } pub fn with_error_message(codec: Codec(t), msg: String) -> Codec(t) { codec.with_error_message(codec, msg) } pub fn with_template(codec: Codec(t), template: String) -> Codec(t) { codec.with_template(codec, template) } pub fn min_size(codec: Codec(List(a)), minimum: Int) -> Codec(List(a)) { codec.min_size(codec, minimum) } pub fn max_size(codec: Codec(List(a)), maximum: Int) -> Codec(List(a)) { codec.max_size(codec, maximum) } pub fn non_empty_list(element_codec: Codec(a)) -> Codec(List(a)) { codec.non_empty_list(element_codec) } // Re-export metadata builders pub fn title(codec: Codec(t), name: String) -> Codec(t) { codec.title(codec, name) } pub fn describe(codec: Codec(t), desc: String) -> Codec(t) { codec.describe(codec, desc) } pub fn example(codec: Codec(t), ex: dynamic.Dynamic) -> Codec(t) { codec.example(codec, ex) } // Re-export JSON runners pub fn decode_json(codec: Codec(t), json_string: String) -> Result(t, Report) { json.decode_json(codec, json_string) } pub fn encode_json(codec: Codec(t), val: t) -> String { json.encode_json(codec, val) } // Zod-aligned entrypoints pub fn safe_parse(codec: Codec(t), val: dynamic.Dynamic) -> Result(t, Report) { codec.decoder.function(val) } pub fn safe_parse_json( codec: Codec(t), json_string: String, ) -> Result(t, Report) { json.decode_json(codec, json_string) } pub fn any_of(decoders: List(Decoder(t))) -> Decoder(t) { decode.any_of(decoders) } pub fn map(decoder: Decoder(a), transform_fn: fn(a) -> b) -> Decoder(b) { decode.map(decoder, transform_fn) }