use std::collections::HashMap;
use std::time::Duration;
use serde::Serialize;
use crate::Error;
use crate::proto::v1::{
Condition, MatchLabels, Requirements, ResourceSelector, Result as FnResult, RunFunctionRequest,
RunFunctionResponse, SchemaSelector, Severity, Status, Target, resource_selector,
};
use crate::resource::{json_to_pb, json_to_struct};
pub const DEFAULT_TTL: Duration = Duration::from_secs(60);
pub fn to(req: &RunFunctionRequest, ttl: Duration) -> RunFunctionResponse {
RunFunctionResponse {
meta: Some(crate::proto::v1::ResponseMeta {
tag: req.meta.as_ref().map(|m| m.tag.clone()).unwrap_or_default(),
ttl: Some(pbjson_types::Duration {
seconds: ttl.as_secs() as i64,
nanos: ttl.subsec_nanos() as i32,
}),
}),
desired: req.desired.clone(),
context: req.context.clone(),
..Default::default()
}
}
pub fn normal(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
add_result(rsp, Severity::Normal, message)
}
pub fn warning(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
add_result(rsp, Severity::Warning, message)
}
pub fn fatal(rsp: &mut RunFunctionResponse, message: impl Into<String>) -> &mut FnResult {
add_result(rsp, Severity::Fatal, message)
}
fn add_result(
rsp: &mut RunFunctionResponse,
severity: Severity,
message: impl Into<String>,
) -> &mut FnResult {
rsp.results.push(FnResult {
severity: severity as i32,
message: message.into(),
reason: None,
target: Some(Target::Composite as i32),
});
rsp.results.last_mut().expect("a result was just pushed")
}
pub fn condition_true(
rsp: &mut RunFunctionResponse,
typ: impl Into<String>,
reason: impl Into<String>,
) -> &mut Condition {
add_condition(rsp, Status::ConditionTrue, typ, reason)
}
pub fn condition_false(
rsp: &mut RunFunctionResponse,
typ: impl Into<String>,
reason: impl Into<String>,
) -> &mut Condition {
add_condition(rsp, Status::ConditionFalse, typ, reason)
}
pub fn condition_unknown(
rsp: &mut RunFunctionResponse,
typ: impl Into<String>,
reason: impl Into<String>,
) -> &mut Condition {
add_condition(rsp, Status::ConditionUnknown, typ, reason)
}
fn add_condition(
rsp: &mut RunFunctionResponse,
status: Status,
typ: impl Into<String>,
reason: impl Into<String>,
) -> &mut Condition {
rsp.conditions.push(Condition {
r#type: typ.into(),
status: status as i32,
reason: reason.into(),
message: None,
target: None,
});
rsp.conditions
.last_mut()
.expect("a condition was just pushed")
}
#[derive(Clone, Debug)]
pub enum ResourceMatch {
Name(String),
Labels(HashMap<String, String>),
}
pub fn require_resources(
rsp: &mut RunFunctionResponse,
name: impl Into<String>,
api_version: impl Into<String>,
kind: impl Into<String>,
r#match: Option<ResourceMatch>,
namespace: Option<String>,
) {
let selector = ResourceSelector {
api_version: api_version.into(),
kind: kind.into(),
r#match: r#match.map(|m| match m {
ResourceMatch::Name(name) => resource_selector::Match::MatchName(name),
ResourceMatch::Labels(labels) => {
resource_selector::Match::MatchLabels(MatchLabels { labels })
}
}),
namespace,
};
requirements(rsp).resources.insert(name.into(), selector);
}
pub fn require_schema(
rsp: &mut RunFunctionResponse,
name: impl Into<String>,
api_version: impl Into<String>,
kind: impl Into<String>,
) {
let selector = SchemaSelector {
api_version: api_version.into(),
kind: kind.into(),
};
requirements(rsp).schemas.insert(name.into(), selector);
}
fn requirements(rsp: &mut RunFunctionResponse) -> &mut Requirements {
rsp.requirements.get_or_insert_default()
}
pub fn set_context_key<T: Serialize + ?Sized>(
rsp: &mut RunFunctionResponse,
key: impl Into<String>,
value: &T,
) -> Result<(), Error> {
let v = serde_json::to_value(value)?;
rsp.context
.get_or_insert_default()
.fields
.insert(key.into(), json_to_pb(&v));
Ok(())
}
pub fn set_output<T: Serialize + ?Sized>(
rsp: &mut RunFunctionResponse,
output: &T,
) -> Result<(), Error> {
let value = serde_json::to_value(output)?;
let serde_json::Value::Object(map) = value else {
return Err(Error::NotAnObject);
};
rsp.output = Some(json_to_struct(&map));
Ok(())
}