///|
pub(open) trait DynCast: IsValue {
  from_value(Value) -> Self raise
}

///|
suberror DynCastFailed String?

///|
pub fn[T] fail_cast(target? : String) -> T raise DynCastFailed {
  raise DynCastFailed(target)
}

///|
#callsite(autofill(loc))
pub fn[T : DynCast] dyn_cast(x : &IsValue, loc~ : SourceLoc) -> T raise {
  DynCast::from_value(x.as_value()) catch {
    DynCastFailed(target) => raise InvalidCast((loc, x.as_value(), target))
    e => raise e
  }
}

///|
pub impl DynCast for Value with from_value(value) {
  value
}

///|
pub impl DynCast for Double with from_value(value) {
  value.to_number() catch {
    InvalidCast((_, _, target)) => fail_cast(target?)
  }
}

///|
pub impl DynCast for Int with from_value(value) {
  value.to_number().to_int() catch {
    InvalidCast((_, _, target)) => fail_cast(target?)
  }
}

///|
pub impl DynCast for Bool with from_value(value) {
  value.to_boolean() catch {
    InvalidCast((_, _, target)) => fail_cast(target?)
  }
}

///|
pub impl DynCast for String with from_value(value) {
  value.to_string() catch {
    InvalidCast((_, _, target)) => fail_cast(target?)
  }
}

///|
pub impl[T : DynCast] DynCast for Array[T] with from_value(value) {
  value.to_array() catch {
    InvalidCast((_, _, target)) => fail_cast(target?)
    e => raise e
  }
}