1use std::collections::HashMap;
4use std::time::Duration;
5
6use serde::Serialize;
7
8use crate::Error;
9use crate::proto::v1::{
10 Condition, MatchLabels, Requirements, ResourceSelector, Result as FnResult, RunFunctionRequest,
11 RunFunctionResponse, SchemaSelector, Severity, Status, Target, resource_selector,
12};
13use crate::resource::{json_to_pb, json_to_struct};
14
15pub const DEFAULT_TTL: Duration = Duration::from_secs(60);
17
18pub fn to(req: &RunFunctionRequest, ttl: Duration) -> RunFunctionResponse {
25 RunFunctionResponse {
26 meta: Some(crate::proto::v1::ResponseMeta {
27 tag: req.meta.as_ref().map(|m| m.tag.clone()).unwrap_or_default(),
28 ttl: Some(pbjson_types::Duration {
29 seconds: ttl.as_secs() as i64,
30 nanos: ttl.subsec_nanos() as i32,
31 }),
32 }),
33 desired: req.desired.clone(),
34 context: req.context.clone(),
35 ..Default::default()
36 }
37}
38
39pub fn normal(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
52 add_result(rsp, Severity::Normal, message)
53}
54
55pub fn warning(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
57 add_result(rsp, Severity::Warning, message)
58}
59
60pub fn fatal(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
65 add_result(rsp, Severity::Fatal, message)
66}
67
68fn add_result(
69 rsp: &mut RunFunctionResponse,
70 severity: Severity,
71 message: impl Into<String>,
72) -> &mut FnResult {
73 rsp.results.push(FnResult {
74 severity: severity as i32,
75 message: message.into(),
76 reason: None,
77 target: Some(Target::Composite as i32),
79 });
80 rsp.results.last_mut().expect("a result was just pushed")
81}
82
83pub fn condition_true(
89 rsp: &mut RunFunctionResponse,
90 typ: impl Into<String>,
91 reason: impl Into<String>,
92) -> &mut Condition {
93 add_condition(rsp, Status::ConditionTrue, typ, reason)
94}
95
96pub fn condition_false(
98 rsp: &mut RunFunctionResponse,
99 typ: impl Into<String>,
100 reason: impl Into<String>,
101) -> &mut Condition {
102 add_condition(rsp, Status::ConditionFalse, typ, reason)
103}
104
105pub fn condition_unknown(
107 rsp: &mut RunFunctionResponse,
108 typ: impl Into<String>,
109 reason: impl Into<String>,
110) -> &mut Condition {
111 add_condition(rsp, Status::ConditionUnknown, typ, reason)
112}
113
114fn add_condition(
115 rsp: &mut RunFunctionResponse,
116 status: Status,
117 typ: impl Into<String>,
118 reason: impl Into<String>,
119) -> &mut Condition {
120 rsp.conditions.push(Condition {
121 r#type: typ.into(),
122 status: status as i32,
123 reason: reason.into(),
124 message: None,
125 target: None,
126 });
127 rsp.conditions
128 .last_mut()
129 .expect("a condition was just pushed")
130}
131
132#[derive(Clone, Debug)]
134pub enum ResourceMatch {
135 Name(String),
137 Labels(HashMap<String, String>),
139}
140
141pub fn require_resources(
149 rsp: &mut RunFunctionResponse,
150 name: impl Into<String>,
151 api_version: impl Into<String>,
152 kind: impl Into<String>,
153 r#match: Option<ResourceMatch>,
154 namespace: Option<String>,
155) {
156 let selector = ResourceSelector {
157 api_version: api_version.into(),
158 kind: kind.into(),
159 r#match: r#match.map(|m| match m {
160 ResourceMatch::Name(name) => resource_selector::Match::MatchName(name),
161 ResourceMatch::Labels(labels) => {
162 resource_selector::Match::MatchLabels(MatchLabels { labels })
163 }
164 }),
165 namespace,
166 };
167 requirements(rsp).resources.insert(name.into(), selector);
168}
169
170pub fn require_schema(
176 rsp: &mut RunFunctionResponse,
177 name: impl Into<String>,
178 api_version: impl Into<String>,
179 kind: impl Into<String>,
180) {
181 let selector = SchemaSelector {
182 api_version: api_version.into(),
183 kind: kind.into(),
184 };
185 requirements(rsp).schemas.insert(name.into(), selector);
186}
187
188fn requirements(rsp: &mut RunFunctionResponse) -> &mut Requirements {
189 rsp.requirements.get_or_insert_default()
190}
191
192pub fn set_context_key<T: Serialize + ?Sized>(
199 rsp: &mut RunFunctionResponse,
200 key: impl Into<String>,
201 value: &T,
202) -> Result<(), Error> {
203 let v = serde_json::to_value(value)?;
204 rsp.context
205 .get_or_insert_default()
206 .fields
207 .insert(key.into(), json_to_pb(&v));
208 Ok(())
209}
210
211pub fn set_output<T: Serialize + ?Sized>(
216 rsp: &mut RunFunctionResponse,
217 output: &T,
218) -> Result<(), Error> {
219 let value = serde_json::to_value(output)?;
220 let serde_json::Value::Object(map) = value else {
221 return Err(Error::NotAnObject);
222 };
223 rsp.output = Some(json_to_struct(&map));
224 Ok(())
225}