uira_core/protocol/primitives/
result.rs1use serde::{Deserialize, Serialize};
2
3use super::types::PermissionDecision;
4
5fn default_true() -> bool {
6 true
7}
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct HookOutput {
11 #[serde(default = "default_true", rename = "continue")]
12 pub continue_processing: bool,
13
14 #[serde(
15 default,
16 skip_serializing_if = "Option::is_none",
17 rename = "stopReason"
18 )]
19 pub stop_reason: Option<String>,
20
21 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub message: Option<String>,
23
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub decision: Option<PermissionDecision>,
26
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub reason: Option<String>,
29
30 #[serde(
31 default,
32 skip_serializing_if = "Option::is_none",
33 rename = "additionalContext"
34 )]
35 pub additional_context: Option<String>,
36
37 #[serde(
38 default,
39 skip_serializing_if = "Option::is_none",
40 rename = "suppressOutput"
41 )]
42 pub suppress_output: Option<bool>,
43
44 #[serde(
45 default,
46 skip_serializing_if = "Option::is_none",
47 rename = "systemMessage"
48 )]
49 pub system_message: Option<String>,
50}
51
52impl Default for HookOutput {
53 fn default() -> Self {
54 Self {
55 continue_processing: true,
56 stop_reason: None,
57 message: None,
58 decision: None,
59 reason: None,
60 additional_context: None,
61 suppress_output: None,
62 system_message: None,
63 }
64 }
65}
66
67impl HookOutput {
68 pub fn allow() -> Self {
69 Self::default()
70 }
71
72 pub fn deny(reason: &str) -> Self {
73 Self {
74 continue_processing: false,
75 decision: Some(PermissionDecision::Deny),
76 reason: Some(reason.to_string()),
77 ..Default::default()
78 }
79 }
80
81 pub fn with_message(message: &str) -> Self {
82 Self {
83 message: Some(message.to_string()),
84 ..Default::default()
85 }
86 }
87
88 pub fn stop(reason: &str) -> Self {
89 Self {
90 continue_processing: false,
91 stop_reason: Some(reason.to_string()),
92 ..Default::default()
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
98pub struct HookResult {
99 pub success: bool,
100 pub should_continue: bool,
101 pub message: Option<String>,
102 pub output: Option<String>,
103}
104
105impl Default for HookResult {
106 fn default() -> Self {
107 Self {
108 success: true,
109 should_continue: true,
110 message: None,
111 output: None,
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_hook_output_default() {
122 let output = HookOutput::default();
123 assert!(output.continue_processing);
124 assert!(output.message.is_none());
125 }
126
127 #[test]
128 fn test_hook_output_with_message() {
129 let output = HookOutput::with_message("test message");
130 assert!(output.continue_processing);
131 assert_eq!(output.message, Some("test message".to_string()));
132 }
133
134 #[test]
135 fn test_hook_output_deny() {
136 let output = HookOutput::deny("not allowed");
137 assert!(!output.continue_processing);
138 assert!(matches!(output.decision, Some(PermissionDecision::Deny)));
139 }
140
141 #[test]
142 fn test_serialize_output() {
143 let output = HookOutput::with_message("hello");
144 let json = serde_json::to_string(&output).unwrap();
145 assert!(json.contains("\"continue\":true"));
146 assert!(json.contains("\"message\":\"hello\""));
147 }
148}