use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use super::argument::ArgumentDefinition;
use crate::schema::{field_type::DeprecationInfo, security_config::InjectedParamSource};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MutationDefinition {
pub name: String,
pub return_type: String,
#[serde(default)]
pub arguments: Vec<ArgumentDefinition>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub operation: MutationOperation,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deprecation: Option<DeprecationInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sql_source: Option<String>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub inject_params: IndexMap<String, InjectedParamSource>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub invalidates_fact_tables: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub invalidates_views: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rest_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rest_method: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub upsert_function: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub requires_role: Option<String>,
#[serde(default = "default_changelog")]
pub changelog: bool,
#[serde(default, skip_serializing_if = "InputStyle::is_flatten")]
pub input_style: InputStyle,
#[serde(default, skip_serializing_if = "core::ops::Not::not")]
pub changelog_pre_image: bool,
#[serde(default, skip_serializing_if = "core::ops::Not::not")]
pub cascade: bool,
}
const fn default_changelog() -> bool {
true
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum InputStyle {
#[default]
Flatten,
Jsonb,
}
impl InputStyle {
#[must_use]
pub const fn is_flatten(&self) -> bool {
matches!(self, Self::Flatten)
}
}
impl MutationDefinition {
#[must_use]
pub fn new(name: impl Into<String>, return_type: impl Into<String>) -> Self {
Self {
name: name.into(),
return_type: return_type.into(),
arguments: Vec::new(),
description: None,
operation: MutationOperation::default(),
deprecation: None,
sql_source: None,
inject_params: IndexMap::new(),
invalidates_fact_tables: Vec::new(),
invalidates_views: Vec::new(),
rest_path: None,
rest_method: None,
upsert_function: None,
requires_role: None,
changelog: true,
input_style: InputStyle::Flatten,
changelog_pre_image: false,
cascade: false,
}
}
#[must_use]
pub fn deprecated(mut self, reason: Option<String>) -> Self {
self.deprecation = Some(DeprecationInfo { reason });
self
}
#[must_use]
pub const fn is_deprecated(&self) -> bool {
self.deprecation.is_some()
}
#[must_use]
pub fn deprecation_reason(&self) -> Option<&str> {
self.deprecation.as_ref().and_then(|d| d.reason.as_deref())
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
#[non_exhaustive]
pub enum MutationOperation {
Insert {
table: String,
},
Update {
table: String,
},
Delete {
table: String,
},
#[default]
Custom,
}
impl MutationOperation {
#[must_use]
pub const fn kind_str(&self) -> &'static str {
match self {
Self::Insert { .. } => "insert",
Self::Update { .. } => "update",
Self::Delete { .. } => "delete",
Self::Custom => "custom",
}
}
}