1use kernel::{AxCode, AxError, ToolCall, ToolOutcome};
17use serde_json::{Map, Value};
18
19use crate::clock::ClockStamp;
20use crate::offload::OffloadSite;
21use crate::sieve::{CommandKey, FilterTable, SieveHistory};
22
23use super::{PackContext, SieveRequest, package};
24
25pub const EXEC_CAP_BYTES: u64 = 16_384;
29
30pub struct SieveSite<'a> {
33 pub offload: OffloadSite<'a>,
34 pub table: &'a FilterTable,
35 pub history: &'a mut SieveHistory,
36}
37
38fn text_field<'a>(map: &'a Map<String, Value>, name: &str) -> &'a str {
39 map.get(name).and_then(Value::as_str).unwrap_or("")
40}
41
42pub fn package_exec(
53 call: &ToolCall,
54 outcome: ToolOutcome,
55 site: SieveSite<'_>,
56 stamp: Option<ClockStamp>,
57) -> Result<ToolOutcome, AxError> {
58 let mut result = outcome.result.as_map().clone();
59 if !result.contains_key("stdout") && !result.contains_key("stderr") {
60 return Ok(outcome);
61 }
62 let key = CommandKey::of(&crate::tools::parse_arm(call.args.as_map())?);
63 let exit_code = super::exit_code_in(&result);
64 let mut text = text_field(&result, "stdout").to_owned();
65 let stderr = text_field(&result, "stderr");
66 if !stderr.is_empty() {
67 if !text.is_empty() {
68 text.push('\n');
69 }
70 text.push_str(stderr);
71 }
72 let packaged = package(
73 text.as_bytes(),
74 PackContext {
75 cap_bytes: EXEC_CAP_BYTES,
76 stamp,
77 net_notice: false,
78 steer: None,
79 reminder: None,
80 offload: Some(site.offload),
81 sieve: Some(SieveRequest {
82 key,
83 exit_code,
84 table: site.table,
85 history: site.history,
86 }),
87 adviser: None,
88 },
89 )?;
90 result.remove("stdout");
91 result.remove("stderr");
92 result.insert("content".to_owned(), Value::String(packaged.content));
93 let accounts = packaged
94 .events
95 .iter()
96 .map(serde_json::to_value)
97 .collect::<Result<Vec<Value>, _>>()
98 .map_err(|err| {
99 AxError::failure(AxCode::InvalidArgs, "encode sieve account", err.to_string())
100 .with_recovery(
101 "report this against runtime::pipeline::exec: a sieve account holds \
102 names and counts, and JSON refuses neither",
103 )
104 })?;
105 result.insert(super::EXEC_ACCOUNTS.to_owned(), Value::Array(accounts));
106 Ok(ToolOutcome {
107 result: kernel::Payload::new(result)?,
108 attachments: Vec::new(),
109 })
110}