#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CollectionModel {
Table,
Document,
Graph,
Vector,
Hll,
Sketch,
Filter,
Kv,
Config,
Vault,
Mixed,
TimeSeries,
Queue,
Metrics,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SubscriptionOperation {
Insert,
Update,
Delete,
}
impl SubscriptionOperation {
pub fn as_str(self) -> &'static str {
match self {
Self::Insert => "INSERT",
Self::Update => "UPDATE",
Self::Delete => "DELETE",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> {
match value.to_ascii_uppercase().as_str() {
"INSERT" => Some(Self::Insert),
"UPDATE" => Some(Self::Update),
"DELETE" => Some(Self::Delete),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptionDescriptor {
pub name: String,
pub source: String,
pub target_queue: String,
pub ops_filter: Vec<SubscriptionOperation>,
pub where_filter: Option<String>,
pub redact_fields: Vec<String>,
pub enabled: bool,
pub all_tenants: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AnalyticsOutput {
Communities,
Components,
Centrality,
}
impl AnalyticsOutput {
pub fn as_str(self) -> &'static str {
match self {
Self::Communities => "communities",
Self::Components => "components",
Self::Centrality => "centrality",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> {
match value.to_ascii_lowercase().as_str() {
"communities" => Some(Self::Communities),
"components" => Some(Self::Components),
"centrality" => Some(Self::Centrality),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AnalyticsViewDescriptor {
pub output: AnalyticsOutput,
pub algorithm: Option<String>,
pub resolution: Option<f64>,
pub max_iterations: Option<i64>,
pub tolerance: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct AiPolicy {
pub embed: Option<EmbedPolicy>,
pub moderate: Option<ModeratePolicy>,
pub vision: Option<VisionPolicy>,
}
impl AiPolicy {
pub fn is_empty(&self) -> bool {
self.embed.is_none() && self.moderate.is_none() && self.vision.is_none()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmbedPolicy {
pub fields: Vec<String>,
pub provider: String,
pub model: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModeratePolicy {
pub fields: Vec<String>,
pub provider: String,
pub model: String,
pub sync_gate: bool,
pub degraded_mode: ModerateDegradedMode,
pub reject_action: ModerateRejectAction,
pub hard_delete_on_reject: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ModerateDegradedMode {
#[default]
Open,
Closed,
}
impl ModerateDegradedMode {
pub fn as_str(self) -> &'static str {
match self {
Self::Open => "open",
Self::Closed => "closed",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"open" => Some(Self::Open),
"closed" => Some(Self::Closed),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ModerateRejectAction {
#[default]
Reject,
Flag,
Redact,
}
impl ModerateRejectAction {
pub fn as_str(self) -> &'static str {
match self {
Self::Reject => "reject",
Self::Flag => "flag",
Self::Redact => "redact",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: &str) -> Option<Self> {
match value.trim().to_ascii_lowercase().as_str() {
"reject" => Some(Self::Reject),
"flag" => Some(Self::Flag),
"redact" => Some(Self::Redact),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VisionPolicy {
pub image_field: String,
pub output_kinds: Vec<String>,
pub provider: String,
pub model: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn moderate_degraded_mode_round_trips() {
for (mode, name) in [
(ModerateDegradedMode::Open, "open"),
(ModerateDegradedMode::Closed, "closed"),
] {
assert_eq!(mode.as_str(), name);
assert_eq!(
ModerateDegradedMode::from_str(&name.to_ascii_uppercase()),
Some(mode)
);
}
assert_eq!(ModerateDegradedMode::from_str("ajar"), None);
assert_eq!(ModerateDegradedMode::default(), ModerateDegradedMode::Open);
}
#[test]
fn moderate_reject_action_round_trips() {
for (action, name) in [
(ModerateRejectAction::Reject, "reject"),
(ModerateRejectAction::Flag, "flag"),
(ModerateRejectAction::Redact, "redact"),
] {
assert_eq!(action.as_str(), name);
assert_eq!(
ModerateRejectAction::from_str(&name.to_ascii_uppercase()),
Some(action)
);
}
assert_eq!(ModerateRejectAction::from_str("ban"), None);
assert_eq!(
ModerateRejectAction::default(),
ModerateRejectAction::Reject
);
}
#[test]
fn ai_policy_empty_detection() {
assert!(AiPolicy::default().is_empty());
let with_embed = AiPolicy {
embed: Some(EmbedPolicy {
fields: vec!["body".to_string()],
provider: "openai".to_string(),
model: "text-embedding-3-small".to_string(),
}),
..AiPolicy::default()
};
assert!(!with_embed.is_empty());
}
#[test]
fn subscription_operations_round_trip_canonical_names() {
for (op, name) in [
(SubscriptionOperation::Insert, "INSERT"),
(SubscriptionOperation::Update, "UPDATE"),
(SubscriptionOperation::Delete, "DELETE"),
] {
assert_eq!(op.as_str(), name);
assert_eq!(
SubscriptionOperation::from_str(&name.to_ascii_lowercase()),
Some(op)
);
}
assert_eq!(SubscriptionOperation::from_str("UPSERT"), None);
}
#[test]
fn analytics_outputs_round_trip_lowercase_names() {
for (output, name) in [
(AnalyticsOutput::Communities, "communities"),
(AnalyticsOutput::Components, "components"),
(AnalyticsOutput::Centrality, "centrality"),
] {
assert_eq!(output.as_str(), name);
assert_eq!(
AnalyticsOutput::from_str(&name.to_ascii_uppercase()),
Some(output)
);
}
assert_eq!(AnalyticsOutput::from_str("pagerank"), None);
}
#[test]
fn descriptors_are_plain_data_carriers() {
let subscription = SubscriptionDescriptor {
name: "audit".to_string(),
source: "orders".to_string(),
target_queue: "events".to_string(),
ops_filter: vec![SubscriptionOperation::Insert, SubscriptionOperation::Delete],
where_filter: Some("amount > 0".to_string()),
redact_fields: vec!["secret".to_string()],
enabled: true,
all_tenants: false,
};
assert_eq!(subscription.ops_filter[1].as_str(), "DELETE");
let view = AnalyticsViewDescriptor {
output: AnalyticsOutput::Centrality,
algorithm: Some("pagerank".to_string()),
resolution: Some(1.0),
max_iterations: Some(20),
tolerance: Some(0.001),
};
assert_eq!(view.output.as_str(), "centrality");
assert_eq!(view.algorithm.as_deref(), Some("pagerank"));
}
}