use crate::error::{BallError, Result};
use crate::negotiation::CommitPolicy;
use crate::participant::{Event, Field, Projection};
use serde::Deserialize;
use serde_json::Value;
use std::collections::BTreeSet;
#[derive(Debug, Clone, Deserialize)]
pub struct DescribeResponse {
#[serde(default)]
pub subscriptions: Vec<Event>,
pub projection: ProjectionWire,
#[serde(default)]
pub retry_budget: Option<usize>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct ProjectionWire {
#[serde(default)]
pub owns: Vec<String>,
#[serde(default)]
pub reads: Vec<String>,
#[serde(default)]
pub external_prefixes: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProposeResponse {
#[serde(default)]
pub ok: Option<ProposeOk>,
#[serde(default)]
pub conflict: Option<ProposeConflict>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProposeOk {
#[serde(default)]
pub task: Value,
#[serde(default)]
pub commit_policy: Option<CommitPolicyWire>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ProposeConflict {
#[serde(default)]
pub fields: Vec<String>,
#[serde(default)]
pub remote_view: Value,
#[serde(default)]
pub hint: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum CommitPolicyWire {
Commit {
#[serde(default)]
message: Option<String>,
},
Batch {
tag: String,
},
Suppress,
}
impl CommitPolicyWire {
pub fn into_policy(self) -> CommitPolicy {
match self {
CommitPolicyWire::Commit { message } => CommitPolicy::Commit { message },
CommitPolicyWire::Batch { tag } => CommitPolicy::Batch { tag },
CommitPolicyWire::Suppress => CommitPolicy::Suppress,
}
}
}
pub fn field_wire_name(f: Field) -> &'static str {
match f {
Field::Title => "title",
Field::Type => "type",
Field::Priority => "priority",
Field::Status => "status",
Field::Parent => "parent",
Field::DependsOn => "depends_on",
Field::Description => "description",
Field::Tags => "tags",
Field::Notes => "notes",
Field::Links => "links",
Field::ClaimedBy => "claimed_by",
Field::Branch => "branch",
Field::ClosedAt => "closed_at",
Field::UpdatedAt => "updated_at",
Field::ClosedChildren => "closed_children",
Field::External => "external",
Field::SyncedAt => "synced_at",
Field::DeliveredIn => "delivered_in",
}
}
pub fn parse_field(name: &str) -> Result<Field> {
Ok(match name {
"title" => Field::Title,
"type" => Field::Type,
"priority" => Field::Priority,
"status" => Field::Status,
"parent" => Field::Parent,
"depends_on" => Field::DependsOn,
"description" => Field::Description,
"tags" => Field::Tags,
"notes" => Field::Notes,
"links" => Field::Links,
"claimed_by" => Field::ClaimedBy,
"branch" => Field::Branch,
"closed_at" => Field::ClosedAt,
"updated_at" => Field::UpdatedAt,
"closed_children" => Field::ClosedChildren,
"external" => Field::External,
"synced_at" => Field::SyncedAt,
"delivered_in" => Field::DeliveredIn,
other => {
return Err(BallError::Other(format!(
"unknown projection field: {other}"
)))
}
})
}
impl ProjectionWire {
pub fn into_projection(self) -> Result<Projection> {
let owns: BTreeSet<Field> = self
.owns
.iter()
.map(|s| parse_field(s))
.collect::<Result<_>>()?;
let reads: BTreeSet<Field> = self
.reads
.iter()
.map(|s| parse_field(s))
.collect::<Result<_>>()?;
let external_prefixes: BTreeSet<String> = self.external_prefixes.into_iter().collect();
Ok(Projection {
owns,
reads,
external_prefixes,
})
}
}
#[cfg(test)]
#[path = "native_types_tests.rs"]
mod tests;