///|
/// Types that have a length, used by `nonempty`, `min_len`, `max_len`, and `len`.
pub(open) trait Length {
  fn length(Self) -> Int
}

///|
pub impl Length for String with fn length(self) {
  self.length()
}

///|
pub impl[T] Length for Array[T] with fn length(self) {
  self.length()
}

///|
/// Reject empty strings and arrays.
pub fn[T : Length] Typed::nonempty(self : Typed[T]) -> Typed[T] raise Invalid {
  expect_nonempty(self.path, Length::length(self.val))
  self
}

///|
/// Require a minimum length for strings and arrays.
pub fn[T : Length] Typed::min_len(
  self : Typed[T],
  n : Int,
) -> Typed[T] raise Invalid {
  expect_min_len(self.path, Length::length(self.val), n)
  self
}

///|
/// Require a maximum length for strings and arrays.
pub fn[T : Length] Typed::max_len(
  self : Typed[T],
  n : Int,
) -> Typed[T] raise Invalid {
  expect_max_len(self.path, Length::length(self.val), n)
  self
}

///|
/// Require an exact length for strings and arrays.
pub fn[T : Length] Typed::len(
  self : Typed[T],
  n : Int,
) -> Typed[T] raise Invalid {
  if Length::length(self.val) == n {
    self
  } else {
    err(self.path, "length must be \{n}")
  }
}

///|
fn expect_nonempty(path : Path, len : Int) -> Unit raise Invalid {
  if len <= 0 {
    err(path, "must not be empty")
  }
}

///|
fn expect_min_len(path : Path, len : Int, n : Int) -> Unit raise Invalid {
  if len < n {
    err(path, "length must be at least \{n}")
  }
}

///|
fn expect_max_len(path : Path, len : Int, n : Int) -> Unit raise Invalid {
  if len > n {
    err(path, "length must be at most \{n}")
  }
}