agileplus-domain 0.3.0

Typed intent graphs for engineering work — convert prompts into validated DAGs of intents, features, tasks, tests, and edges, then store, query, and serve them from Rust, HTTP, or MCP.
Documentation
//! `EdgeBuilder` — constructs `Edge` records with required field validation.

use crate::intent_graph::{CanonicalMap, Edge, Meta, NodeId, RelationshipType};

/// Builder for `Edge`.
#[derive(Debug, Default)]
pub struct EdgeBuilder {
    id: Option<String>,
    source: Option<String>,
    target: Option<String>,
    relationship_type: Option<RelationshipType>,
    canonical_map: Option<CanonicalMap>,
    meta: Option<Meta>,
    properties: Option<serde_json::Value>,
    created_at: Option<String>,
}

impl EdgeBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn id(mut self, id: impl Into<String>) -> Self {
        self.id = Some(id.into());
        self
    }

    pub fn auto_id(mut self) -> Self {
        self.id = Some(uuid::Uuid::new_v4().to_string());
        self
    }

    pub fn source(mut self, src: impl Into<String>) -> Self {
        self.source = Some(src.into());
        self
    }

    pub fn target(mut self, tgt: impl Into<String>) -> Self {
        self.target = Some(tgt.into());
        self
    }

    pub fn relationship_type(mut self, rt: RelationshipType) -> Self {
        self.relationship_type = Some(rt);
        self
    }

    pub fn canonical_map(mut self, cm: CanonicalMap) -> Self {
        self.canonical_map = Some(cm);
        self
    }

    pub fn meta(mut self, meta: Meta) -> Self {
        self.meta = Some(meta);
        self
    }

    pub fn with_meta(mut self, source: impl Into<String>) -> Self {
        let now = chrono::Utc::now().to_rfc3339();
        self.meta = Some(Meta {
            confidence: None,
            source: source.into(),
            timestamp: now,
            agent_id: None,
        });
        self
    }

    pub fn properties(mut self, props: serde_json::Value) -> Self {
        self.properties = Some(props);
        self
    }

    pub fn created_at(mut self, at: impl Into<String>) -> Self {
        self.created_at = Some(at.into());
        self
    }

    pub fn build(self) -> Result<Edge, String> {
        let now = chrono::Utc::now().to_rfc3339();
        Ok(Edge {
            id: self.id.ok_or("id is required")?,
            source: NodeId::new(self.source.ok_or("source is required")?),
            target: NodeId::new(self.target.ok_or("target is required")?),
            relationship_type: self
                .relationship_type
                .ok_or("relationship_type is required")?,
            canonical_map: self.canonical_map,
            meta: self.meta.ok_or("meta is required")?,
            properties: self.properties,
            created_at: Some(self.created_at.unwrap_or(now)),
        })
    }
}