///|
/// Apply a transformation function to the validated JSON value.
///
/// The schema is first validated normally. If validation succeeds, `f` is
/// called with the validated JSON. The transform can modify the value or
/// return an error.
///
/// Rules chained after `.transform()` are applied to the transformed value.
///
/// # Example
/// ```mbt nocheck
/// let s = string().transform(fn(json) {
///   match json {
///     String(s) => Ok(Json::string(s + "!"))
///     _ => Err("expected string")
///   }
/// })
/// ```
pub fn Schema::transform(
  self : Schema,
  f : (Json) -> Result[Json, String],
) -> Schema {
  {
    id: new_schema_id(),
    schema_type: TransformType(self, TransformClosure::{
      f,
      kind: CustomTransform,
    }),
    rules: [],
    description: self.description,
    required_error: self.required_error,
    invalid_type_error: self.invalid_type_error,
    name: self.name,
    brand: self.brand,
  }
}

///|
pub fn Schema::parse_transform(
  self : Schema,
  inner : Schema,
  closure : TransformClosure,
  json : Json,
  path_stack : Array[String],
) -> RawSchemaResult {
  parse_inner(
    { ..self, id: new_schema_id(), schema_type: TransformType(inner, closure), },
    json,
    path_stack,
  )
}