// Embedder-facing value-inspection helpers. These mirror starlark-go's
// package-level Equal/Compare/Len/Iterate/AsFloat/AsString/NumberToInt and form
// the public value-level API. They return String errors because value-level
// operations carry no source position; richer EvalError context lives in @eval.
///|
/// Hashes `v` with an explicit recursion-depth cap. Pass the received `depth`
/// to `hash_value_depth` inside a `CustomValue::with_hash_depth` callback so
/// nested `Value` fields are hashed without restarting the counter.
///
/// Parameters:
///
/// - `v` : The value to hash.
/// - `depth` : Remaining recursion budget; use `hash_limit` as the initial
/// value.
///
/// Returns `Ok(hash)` on success, or `Err` for unhashable types or when
/// `depth` is exhausted.
pub fn hash_value_depth(v : Value, depth : Int) -> Result[UInt, String] {
v.hash_depth(depth)
}
///|
/// Structural equality with the default recursion-depth cap (`compare_limit`)
/// to guard against cycles.
///
/// Parameters:
///
/// - `a` : The left-hand value.
/// - `b` : The right-hand value.
///
/// Returns `Ok(true)` if `a == b`, `Ok(false)` if not, or `Err` when the
/// recursion-depth cap is exceeded (cyclic structure).
pub fn equal(a : Value, b : Value) -> Result[Bool, String] {
starlark_equals_depth(a, b, compare_limit)
}
///|
/// Structural equality with an explicit recursion-depth cap.
///
/// Parameters:
///
/// - `a` : The left-hand value.
/// - `b` : The right-hand value.
/// - `depth` : Remaining recursion budget; use `compare_limit` as the initial
/// value.
///
/// Returns `Ok(true)` if `a == b`, `Ok(false)` if not, or `Err` when `depth`
/// is exhausted (cyclic structure).
pub fn equal_depth(a : Value, b : Value, depth : Int) -> Result[Bool, String] {
starlark_equals_depth(a, b, depth)
}
///|
/// Applies a comparison operator (`"=="`, `"!="`, `"<"`, `"<="`, `">"`,
/// `">="`) to `a` and `b` with an explicit recursion-depth cap.
///
/// Parameters:
///
/// - `op` : The comparison operator string.
/// - `a` : The left-hand value.
/// - `b` : The right-hand value.
/// - `depth` : Remaining recursion budget; use `compare_limit` as the initial
/// value.
///
/// Returns `Ok(result)` on success, or `Err` on type mismatch, depth
/// exhaustion, or unknown operator.
pub fn compare_depth(
op : String,
a : Value,
b : Value,
depth : Int,
) -> Result[Bool, String] {
match op {
"==" => starlark_equals_depth(a, b, depth)
"!=" => starlark_equals_depth(a, b, depth).map(fn(v) { !v })
"<" | "<=" | ">" | ">=" =>
match compare_values_depth(a, b, depth) {
Ok(c) =>
Ok(
match op {
"<" => c < 0
"<=" => c <= 0
">" => c > 0
_ => c >= 0
},
)
Err(e) => Err(e)
}
_ => Err("unknown comparison operator: \{op}")
}
}
///|
/// Returns the sequence length of `v` (list, tuple, string, bytes, range), or
/// `-1` if `v` has no length.
///
/// Parameters:
///
/// - `v` : The value whose length to retrieve.
///
/// Returns the non-negative length, or `-1` if `v` does not support `len`.
pub fn len_of(v : Value) -> Int64 {
match length_of(v) {
Ok(n) => n
Err(_) => -1L
}
}
///|
/// Extracts a `Double` from an `Int` or `Float` value.
///
/// Parameters:
///
/// - `v` : The value to extract a float from.
///
/// Returns `(value, true)` on success, or `(0.0, false)` if `v` is neither a
/// numeric type.
pub fn as_float(v : Value) -> (Double, Bool) {
match v {
Float(f) => (f, true)
Int(n) => (@numeric.bigint_to_double(n), true)
_ => (0.0, false)
}
}
///|
/// Extracts the raw `String` from a Starlark `String` value.
///
/// Parameters:
///
/// - `v` : The value to extract a string from.
///
/// Returns `(string, true)` on success, or `("", false)` if `v` is not a
/// string.
pub fn as_string(v : Value) -> (String, Bool) {
match v {
String(s) => (s.raw(), true)
_ => ("", false)
}
}
///|
/// Converts an `Int` or `Float` value to `Int64`.
///
/// Parameters:
///
/// - `v` : The numeric value to convert.
///
/// Returns `Some(n)` on success, or `None` if `v` is not numeric, is NaN or
/// infinite, or is outside the signed 64-bit range.
pub fn number_to_int(v : Value) -> Int64? {
match v {
Int(n) =>
if n.compare_int64(@int64.MAX_VALUE) > 0 ||
n.compare_int64(@int64.MIN_VALUE) < 0 {
None
} else {
Some(n.to_int64())
}
Float(f) => if f.is_nan() || f.is_inf() { None } else { Some(f.to_int64()) }
_ => None
}
}