lex_runtime/handler/
approval.rs1use super::*;
4
5pub trait ApprovalSink: Send {
11 fn request(&self, scope: &str, reason: &str) -> Result<String, String>;
12}
13
14pub struct NullApprovalSink;
20impl ApprovalSink for NullApprovalSink {
21 fn request(&self, _scope: &str, _reason: &str) -> Result<String, String> {
22 Err("no ApprovalSink configured — call DefaultHandler::with_approval_sink".into())
23 }
24}
25
26pub struct StdinApprovalSink;
30impl ApprovalSink for StdinApprovalSink {
31 fn request(&self, scope: &str, reason: &str) -> Result<String, String> {
32 use std::io::Write;
33 print!("[approval:{scope}] {reason} (blank/n to deny) > ");
34 let _ = std::io::stdout().flush();
35 let mut line = String::new();
36 std::io::stdin().read_line(&mut line).map_err(|e| e.to_string())?;
37 let answer = line.trim();
38 if answer.is_empty() || answer.eq_ignore_ascii_case("n") || answer.eq_ignore_ascii_case("no") {
39 Err("denied by operator".into())
40 } else {
41 Ok(answer.to_string())
42 }
43 }
44}
45
46impl DefaultHandler {
47 pub(super) fn dispatch_approval(&mut self, op: &str, args: Vec<Value>) -> Result<Value, String> {
52 match op {
53 "request" => {
54 let scope = expect_str(args.first())?.to_string();
55 let reason = expect_str(args.get(1))?.to_string();
56 if !self.policy.allow_approval.is_empty()
57 && !self.policy.allow_approval.iter().any(|a| a == &scope)
58 {
59 return Ok(err(Value::Str(format!(
60 "approval.request: scope `{scope}` not in --allow-approval {:?}",
61 self.policy.allow_approval
62 ).into())));
63 }
64 match self.approval_sink.request(&scope, &reason) {
65 Ok(answer) => Ok(ok(Value::Str(answer.into()))),
66 Err(reason) => Ok(err(Value::Str(reason.into()))),
67 }
68 }
69 other => Err(format!("unsupported approval.{other}")),
70 }
71 }
72}