logline_core/
builder.rs

1#[cfg(not(feature = "std"))]
2use alloc::string::String;
3#[cfg(feature = "std")]
4use std::string::String;
5
6use crate::{Escalation, FailureHandling, LogLine, LogLineError, Outcome, Payload, Status, Verb};
7
8/// Builder para construir um `LogLine` passo a passo.
9///
10/// Use `LogLine::builder()` para criar um novo builder. Todos os campos
11/// obrigatórios devem ser fornecidos antes de chamar `build_draft()`.
12///
13/// # Exemplo
14///
15/// ```rust
16/// use logline_core::*;
17///
18/// let line = LogLine::builder()
19///     .who("did:ubl:alice")
20///     .did(Verb::Transfer)
21///     .this(Payload::Text("100 USD".into()))
22///     .when(1_735_671_234)
23///     .if_ok(Outcome { label: "transferred".into(), effects: vec!["emit_receipt".into()] })
24///     .if_doubt(Escalation { label: "verify".into(), route_to: "auditor".into() })
25///     .if_not(FailureHandling { label: "failed".into(), action: "compensate".into() })
26///     .build_draft()?;
27/// # Ok::<(), LogLineError>(())
28/// ```
29#[derive(Default)]
30pub struct LogLineBuilder {
31    who: Option<String>,
32    did: Option<Verb>,
33    this: Option<Payload>,
34    when: Option<u64>,
35    confirmed_by: Option<String>,
36    if_ok: Option<Outcome>,
37    if_doubt: Option<Escalation>,
38    if_not: Option<FailureHandling>,
39}
40
41impl LogLineBuilder {
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    /// Define o agente que executa a ação (DID futuro, ex: `did:ubl:...`).
47    pub fn who(mut self, v: impl Into<String>) -> Self {
48        self.who = Some(v.into());
49        self
50    }
51    /// Define o verbo da ação (canônico ou custom).
52    pub fn did(mut self, v: Verb) -> Self {
53        self.did = Some(v);
54        self
55    }
56    /// Define a carga útil da ação (payload).
57    pub fn this(mut self, v: Payload) -> Self {
58        self.this = Some(v);
59        self
60    }
61    /// Define o timestamp Unix em nanosegundos.
62    pub fn when(mut self, v: u64) -> Self {
63        self.when = Some(v);
64        self
65    }
66    /// Define a identidade que confirma a ação (opcional).
67    ///
68    /// Paper I: obrigatório para ações de Risk Level 3+ (L3+).
69    pub fn confirmed_by(mut self, v: impl Into<String>) -> Self {
70        self.confirmed_by = Some(v.into());
71        self
72    }
73    /// Define a consequência positiva obrigatória (invariant).
74    pub fn if_ok(mut self, v: Outcome) -> Self {
75        self.if_ok = Some(v);
76        self
77    }
78    /// Define a via de dúvida obrigatória (invariant).
79    pub fn if_doubt(mut self, v: Escalation) -> Self {
80        self.if_doubt = Some(v);
81        self
82    }
83    /// Define o fallback/erro obrigatório (invariant).
84    pub fn if_not(mut self, v: FailureHandling) -> Self {
85        self.if_not = Some(v);
86        self
87    }
88
89    /// Constrói um DRAFT válido (invariants obrigatórios presentes).
90    ///
91    /// Valida que todos os campos obrigatórios estão presentes e cria um `LogLine`
92    /// com status `Draft`. Os invariants são verificados antes de retornar.
93    ///
94    /// # Erros
95    ///
96    /// - `LogLineError::MissingField` se algum campo obrigatório estiver faltando
97    /// - `LogLineError::MissingInvariant` se algum invariant estiver faltando ou vazio
98    ///
99    /// # Campos obrigatórios
100    ///
101    /// - `who`: identidade do agente
102    /// - `did`: verbo da ação
103    /// - `when`: timestamp
104    /// - `if_ok`: consequência positiva
105    /// - `if_doubt`: via de dúvida
106    /// - `if_not`: fallback/erro
107    pub fn build_draft(self) -> Result<LogLine, LogLineError> {
108        let line = LogLine {
109            who: self.who.ok_or(LogLineError::MissingField("who"))?,
110            did: self.did.ok_or(LogLineError::MissingField("did"))?,
111            this: self.this.unwrap_or(Payload::None),
112            when: self.when.ok_or(LogLineError::MissingField("when"))?,
113            confirmed_by: self.confirmed_by,
114            if_ok: self.if_ok.ok_or(LogLineError::MissingInvariant("if_ok"))?,
115            if_doubt: self
116                .if_doubt
117                .ok_or(LogLineError::MissingInvariant("if_doubt"))?,
118            if_not: self
119                .if_not
120                .ok_or(LogLineError::MissingInvariant("if_not"))?,
121            status: Status::Draft,
122        };
123        line.verify_invariants()?;
124        Ok(line)
125    }
126}