use std::fmt;
use indexmap::IndexMap;
use serde::de::{Deserializer, Error, MapAccess, Visitor};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use super::metadata::Metadata;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RulePhase {
Precondition,
Execution,
Postcondition,
}
impl RulePhase {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Precondition => "precondition",
Self::Execution => "execution",
Self::Postcondition => "postcondition",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum RuleScope {
Contract,
#[default]
Input,
Output,
Expression,
SemanticAction,
Plan,
ExecutionPlan,
}
impl RuleScope {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Contract => "contract",
Self::Input => "input",
Self::Output => "output",
Self::Expression => "expression",
Self::SemanticAction => "semanticAction",
Self::Plan => "plan",
Self::ExecutionPlan => "executionPlan",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum RuleOutcome {
Satisfied,
Violated,
Indeterminate,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Rule {
pub id: String,
pub rule: String,
pub target: String,
pub phase: RulePhase,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scope: Option<RuleScope>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub allow_indeterminate: bool,
#[serde(default = "default_true", skip_serializing_if = "Clone::clone")]
pub deterministic: bool,
#[serde(
default,
skip_serializing_if = "IndexMap::is_empty",
deserialize_with = "deserialize_unique_parameters"
)]
pub parameters: IndexMap<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(default, flatten)]
pub extensions: IndexMap<String, Value>,
}
fn default_true() -> bool {
true
}
fn deserialize_unique_parameters<'de, D>(
deserializer: D,
) -> Result<IndexMap<String, Value>, D::Error>
where
D: Deserializer<'de>,
{
struct UniqueMapVisitor;
impl<'de> Visitor<'de> for UniqueMapVisitor {
type Value = IndexMap<String, Value>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map with unique parameter keys")
}
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut map = IndexMap::new();
while let Some((key, value)) = access.next_entry::<String, Value>()? {
if map.contains_key(&key) {
return Err(M::Error::custom(format!(
"duplicate key '{key}' in rule.parameters"
)));
}
map.insert(key, value);
}
Ok(map)
}
}
deserializer.deserialize_map(UniqueMapVisitor)
}