use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
use super::metadata::{CanonicalMap, Meta};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct NodeId(pub String);
impl NodeId {
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
}
impl fmt::Display for NodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub enum NodeType {
Intent,
Plan,
Feature,
Story,
Task,
Spec,
Commit,
Test,
PR,
Bug,
Artifact,
}
impl fmt::Display for NodeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
NodeType::Intent => "Intent",
NodeType::Plan => "Plan",
NodeType::Feature => "Feature",
NodeType::Story => "Story",
NodeType::Task => "Task",
NodeType::Spec => "Spec",
NodeType::Commit => "Commit",
NodeType::Test => "Test",
NodeType::PR => "PR",
NodeType::Bug => "Bug",
NodeType::Artifact => "Artifact",
};
write!(f, "{}", s)
}
}
impl FromStr for NodeType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Intent" => Ok(NodeType::Intent),
"Plan" => Ok(NodeType::Plan),
"Feature" => Ok(NodeType::Feature),
"Story" => Ok(NodeType::Story),
"Task" => Ok(NodeType::Task),
"Spec" => Ok(NodeType::Spec),
"Commit" => Ok(NodeType::Commit),
"Test" => Ok(NodeType::Test),
"PR" => Ok(NodeType::PR),
"Bug" => Ok(NodeType::Bug),
"Artifact" => Ok(NodeType::Artifact),
_ => Err(format!("Unknown NodeType: {}", s)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DagStage {
Intent,
Plan,
Feature,
Story,
Task,
Spec,
Commit,
Test,
Pr,
Bug,
Artifact,
}
impl fmt::Display for DagStage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
DagStage::Intent => "intent",
DagStage::Plan => "plan",
DagStage::Feature => "feature",
DagStage::Story => "story",
DagStage::Task => "task",
DagStage::Spec => "spec",
DagStage::Commit => "commit",
DagStage::Test => "test",
DagStage::Pr => "pr",
DagStage::Bug => "bug",
DagStage::Artifact => "artifact",
};
write!(f, "{}", s)
}
}
impl FromStr for DagStage {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"intent" => Ok(DagStage::Intent),
"plan" => Ok(DagStage::Plan),
"feature" => Ok(DagStage::Feature),
"story" => Ok(DagStage::Story),
"task" => Ok(DagStage::Task),
"spec" => Ok(DagStage::Spec),
"commit" => Ok(DagStage::Commit),
"test" => Ok(DagStage::Test),
"pr" => Ok(DagStage::Pr),
"bug" => Ok(DagStage::Bug),
"artifact" => Ok(DagStage::Artifact),
_ => Err(format!("Unknown DagStage: {}", s)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Status {
Draft,
Active,
Completed,
Deprecated,
Rejected,
Open,
InProgress,
Blocked,
Deferred,
Cancelled,
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Status::Draft => "draft",
Status::Active => "active",
Status::Completed => "completed",
Status::Deprecated => "deprecated",
Status::Rejected => "rejected",
Status::Open => "open",
Status::InProgress => "in_progress",
Status::Blocked => "blocked",
Status::Deferred => "deferred",
Status::Cancelled => "cancelled",
};
write!(f, "{}", s)
}
}
impl FromStr for Status {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"draft" => Ok(Status::Draft),
"active" => Ok(Status::Active),
"completed" => Ok(Status::Completed),
"deprecated" => Ok(Status::Deprecated),
"rejected" => Ok(Status::Rejected),
"open" => Ok(Status::Open),
"in_progress" => Ok(Status::InProgress),
"blocked" => Ok(Status::Blocked),
"deferred" => Ok(Status::Deferred),
"cancelled" => Ok(Status::Cancelled),
_ => Err(format!("Unknown Status: {}", s)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum RelationshipType {
Implements,
Tests,
Covers,
TracesTo,
DerivesFrom,
Resolves,
Blocks,
DependsOn,
}
impl fmt::Display for RelationshipType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
RelationshipType::Implements => "implements",
RelationshipType::Tests => "tests",
RelationshipType::Covers => "covers",
RelationshipType::TracesTo => "traces-to",
RelationshipType::DerivesFrom => "derives-from",
RelationshipType::Resolves => "resolves",
RelationshipType::Blocks => "blocks",
RelationshipType::DependsOn => "depends-on",
};
write!(f, "{}", s)
}
}
impl FromStr for RelationshipType {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"implements" => Ok(RelationshipType::Implements),
"tests" => Ok(RelationshipType::Tests),
"covers" => Ok(RelationshipType::Covers),
"traces-to" => Ok(RelationshipType::TracesTo),
"derives-from" => Ok(RelationshipType::DerivesFrom),
"resolves" => Ok(RelationshipType::Resolves),
"blocks" => Ok(RelationshipType::Blocks),
"depends-on" => Ok(RelationshipType::DependsOn),
_ => Err(format!("Unknown RelationshipType: {}", s)),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Node {
pub id: NodeId,
pub node_type: NodeType,
pub dag_stage: DagStage,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub status: Status,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
pub meta: Meta,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub properties: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_ref: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub table_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated_at: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct Edge {
pub id: String,
pub source: NodeId,
pub target: NodeId,
pub relationship_type: RelationshipType,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub canonical_map: Option<CanonicalMap>,
pub meta: Meta,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub properties: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
}