use serde_json::Value;
use crate::control::placeholder::{self, text};
use crate::metadata::{Constraints, ParameterDef, ParameterType};
pub(super) const ENTRY: &str = "entry";
pub(super) const WHOLE_ENTRY: &str = "{{entry}}";
#[derive(Debug)]
pub(super) struct ReportParams {
pub message: String,
pub each: Option<Value>,
}
impl ReportParams {
pub(super) fn read(params: &serde_json::Map<String, Value>) -> Result<Self, String> {
let message = params
.get("message")
.map(text)
.filter(|m| !m.is_empty())
.ok_or("'message' is required: the line to print")?;
let each = params.get("each").filter(|v| !v.is_null()).cloned();
check_each(each.as_ref())?;
check_namespaces(&message, each.is_some())?;
Ok(Self { message, each })
}
pub(super) fn entries(&self) -> Result<Option<&[Value]>, String> {
match &self.each {
None => Ok(None),
Some(Value::Array(entries)) => Ok(Some(entries)),
Some(other) => Err(not_a_list(other)),
}
}
}
fn check_each(each: Option<&Value>) -> Result<(), String> {
match each {
None | Some(Value::Array(_)) => Ok(()),
Some(Value::String(s)) if s.contains("{{") => Ok(()),
Some(other) => Err(not_a_list(other)),
}
}
fn not_a_list(value: &Value) -> String {
format!("'each' must resolve to a list, got {value}")
}
fn check_namespaces(message: &str, has_each: bool) -> Result<(), String> {
let entry = placeholder::reads(message, ENTRY) || message.contains(WHOLE_ENTRY);
if entry && !has_each {
return Err("'message' reads {{entry}} but 'each' is not set: \
set 'each' to the list to print a line per entry of"
.to_string());
}
if entry && placeholder::reads_file(message) {
return Err("'message' reads {{entry}} and {{file}} or {{item.*}}: \
a report prints a line per entry or per file, not both"
.to_string());
}
Ok(())
}
pub(super) fn build_report_parameters() -> Vec<ParameterDef> {
vec![message_param(), each_param()]
}
fn message_param() -> ParameterDef {
ParameterDef {
name: "message".to_string(),
label: "Message".to_string(),
description: "The line to print, usually a template. {{file}} (the file's name) or \
{{item.<key>}} prints it once per file, {{entry.<key>}} (or {{entry}}) \
once per entry of each."
.to_string(),
param_type: ParameterType::String,
constraints: Some(Constraints {
min: None,
max: None,
required: true,
}),
..Default::default()
}
}
fn each_param() -> ParameterDef {
ParameterDef {
name: "each".to_string(),
label: "For Each".to_string(),
description: "A list to print the message once per entry of, in order, usually a \
{{node.<id>.<key>}} template."
.to_string(),
param_type: ParameterType::Object,
placeholder: Some("{{node.<id>.<key>}}".to_string()),
..Default::default()
}
}