1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! values — the values of a collection, insertion order.
//!
//! See `docs/LANGUAGE.md`, "keys / values". `values $collection` takes a
//! collection directly and is never nested through another builtin, for the
//! same reason as [`super::keys`].
//!
//! jq semantics for the two collection shapes:
//! - a **record** → its values, insertion order (pairwise-aligned with `keys`);
//! - a **list** → its elements (the list itself, element-wise).
//!
//! This makes `$(values $c)` the uniform element-iteration idiom over ANY
//! collection.
//!
//! Pure data transform — no OS, no VFS — so it belongs in every capability
//! build, same footing as `fromjson`/`tojson`.
//!
//! # Examples
//!
//! ```kaish
//! user=$(fromjson '{"name":"amy","role":"maintainer"}')
//! vs=$(values $user) # ["amy","maintainer"]
//! xs=$(fromjson '["a","b","c"]')
//! for x in $(values $xs); do echo $x; done # a b c (list elements)
//! ```
use async_trait::async_trait;
use clap::{CommandFactory, Parser};
use crate::ast::Value;
use crate::interpreter::ExecResult;
use crate::tools::{schema_from_clap, ExecContext, GlobalFlags, Tool, ToolArgs, ToolCtx, ToolSchema};
use super::keys::describe_kind;
/// values tool: the values of a collection, insertion order.
pub struct Values;
/// clap-derived argv layer for values.
#[derive(Parser, Debug)]
#[command(name = "values", about = "The values of a collection, in insertion order")]
struct ValuesArgs {
#[command(flatten)]
global: GlobalFlags,
/// The list or record to read values from.
// Hidden sink: the real, typed value is read off `args.positional`.
#[arg(hide = true)]
collection: Vec<String>,
}
#[async_trait]
impl Tool for Values {
fn name(&self) -> &str {
"values"
}
fn schema(&self) -> ToolSchema {
schema_from_clap(
&ValuesArgs::command(),
"values",
"The values of a collection — record values or list elements (in .data)",
[
("Record values, insertion order", "vs=$(values $user)"),
("List elements", "for x in $(values $xs); do echo $x; done"),
(
"Pairwise-aligned with keys",
"for v in $(values $user); do echo $v; done",
),
],
)
.with_typed_substitution()
}
async fn execute(&self, args: ToolArgs, ctx: &mut dyn ToolCtx) -> ExecResult {
let Some(ctx) = ctx.as_any_mut().downcast_mut::<ExecContext>() else {
return ExecResult::failure(1, "internal error: kernel builtin requires ExecContext");
};
let argv = match args.to_argv() {
Ok(v) => v,
Err(e) => return ExecResult::failure(2, format!("values: {e}")),
};
let parsed = match ValuesArgs::try_parse_from(
std::iter::once("values".to_string()).chain(argv),
) {
Ok(p) => p,
Err(e) => return ExecResult::failure(2, format!("values: {e}")),
};
parsed.global.apply(ctx);
match args.positional.first() {
// A record → its values, insertion order (pairwise-aligned with keys).
Some(Value::Json(serde_json::Value::Object(map))) => {
let values: Vec<serde_json::Value> = map.values().cloned().collect();
ExecResult::success_data(Value::Json(serde_json::Value::Array(values)))
}
// A list → its elements (jq semantics: the list itself, element-wise).
// Makes `for x in $(values $xs)` the uniform element-iteration idiom.
Some(Value::Json(serde_json::Value::Array(items))) => {
ExecResult::success_data(Value::Json(serde_json::Value::Array(items.clone())))
}
Some(other) => ExecResult::failure(
1,
format!(
"values: expected a record or list, got {}",
describe_kind(other)
),
),
None => ExecResult::failure(1, "values: no argument (expected a record or list)"),
}
}
}