///|
/// Chains or wraps an error with an optional message
#inline
#callsite(autofill(loc))
#as_free_fn
pub fn[T] Cause::by(
  err : Error,
  msg? : String = "",
  loc~ : SourceLoc,
) -> T raise Cause {
  match err {
    Cause(_) as cause => raise Causes(msg~, source=cause, loc~, err=Failure)
    Causes(_) as cause => raise Causes(msg~, source=cause, loc~, err=Failure)
    _ => raise Cause(msg~, source=err, loc~)
  }
}

///|
priv suberror Failure {
  Failure
} derive(Debug)

///|
/// Returns an `Iter` that walks the error chain
#cfg(any(target="wasm", target="js"))
pub fn Cause::errors(self : Cause) -> Iter[Cause] {
  let mut cur : Cause? = Some(self)
  Iter::new(fn() {
    match cur {
      None => None
      Some(c) =>
        match c {
          Cause(..) => {
            cur = None
            Some(c)
          }
          Causes(msg~, err~, source~, loc~) => {
            cur = Some(source)
            Some(Cause(msg~, source=err, loc~))
          }
        }
    }
  })
}

///|
/// Returns an `Iter` that walks the error chain
#cfg(any(target="wasm-gc", target="native"))
pub fn Cause::errors(self : Cause) -> Iter[Cause] {
  let mut cur : Cause? = Some(self)
  Iter::new(fn() {
    match cur {
      None => None
      Some(Cause(_) as c) => {
        cur = None
        Some(c)
      }
      Some(Causes(msg~, err~, source~, loc~)) => {
        cur = Some(source)
        Some(Cause(msg~, source=err, loc~))
      }
    }
  })
}

///|
/// Call the function and wrap any error it raises
#inline
#callsite(autofill(loc))
pub fn[T] passby(
  f : () -> T raise Error,
  msg? : String = "",
  loc~ : SourceLoc,
) -> T raise Cause {
  f() catch {
    e => by(e, msg~, loc~)
  }
}