LogLine

Struct LogLine 

Source
pub struct LogLine {
    pub who: String,
    pub did: Verb,
    pub this: Payload,
    pub when: u64,
    pub confirmed_by: Option<String>,
    pub if_ok: Outcome,
    pub if_doubt: Escalation,
    pub if_not: FailureHandling,
    pub status: Status,
}
Expand description

O “átomo” LogLine — 9-field tuple rígido.

Representa uma ação verificável com lifecycle determinístico e invariants obrigatórios. Conforme Paper I §3, cada LogLine deve ter exatamente 9 campos e seguir o lifecycle DRAFT → PENDING → COMMITTED | GHOST.

§Exemplo

use logline_core::*;

let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .this(Payload::Text("purchase:123".into()))
    .when(1_735_671_234)
    .if_ok(Outcome { label: "approved".into(), effects: vec!["emit_receipt".into()] })
    .if_doubt(Escalation { label: "manual_review".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "rejected".into(), action: "notify".into() })
    .build_draft()?;

Fields§

§who: String

Identidade do agente que executa a ação (DID futuro, ex: did:ubl:...).

§did: Verb

Verbo canônico ou custom (Paper I: validar contra ALLOWED_ACTIONS via VerbRegistry).

§this: Payload

Carga útil mínima/typed (Paper I: JSON estrito validado por schema do verbo).

§when: u64

Unix timestamp em nanosegundos (interno). Na serialização canônica JSON✯Atomic será ISO8601.

§confirmed_by: Option<String>

Identidade que confirma a ação. Paper I: obrigatório para ações de Risk Level 3+ (L3+).

§if_ok: Outcome

Consequência positiva obrigatória (Paper I: invariante obrigatório).

§if_doubt: Escalation

Via de dúvida obrigatória (Paper I: invariante obrigatório).

§if_not: FailureHandling

Fallback/erro obrigatório (Paper I: invariante obrigatório).

§status: Status

Estado do lifecycle rígido: DRAFT → PENDING → COMMITTED | GHOST.

Implementations§

Source§

impl LogLine

Source

pub fn builder() -> LogLineBuilder

Cria um novo builder para construir um LogLine passo a passo.

§Exemplo
use logline_core::*;

let builder = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Transfer);
Source

pub fn verify_invariants(&self) -> Result<(), LogLineError>

Verifica invariants do 9-tuple (Paper I §3).

Valida que todos os campos obrigatórios estão presentes e não vazios:

  • who não pode ser vazio
  • when deve ser > 0
  • if_ok, if_doubt, if_not devem estar presentes e não vazios
§Erros

Retorna LogLineError::MissingField ou LogLineError::MissingInvariant se algum invariant não for satisfeito.

§Exemplo
use logline_core::*;

let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?;

assert!(line.verify_invariants().is_ok());
Source

pub fn sign(self, signer: &dyn Signer) -> Result<Self, LogLineError>

Assina o LogLine (DRAFT ou PENDING). Paper I: “nada acontece sem estar assinado”.

A assinatura é calculada sobre os bytes determinísticos retornados por to_signable_bytes(). Retorna self para method chaining.

§Erros
  • LogLineError::InvalidTransition se o status não for Draft ou Pending
  • LogLineError::Signing se a assinatura falhar
§Exemplo
use logline_core::*;

struct NoopSigner;
impl Signer for NoopSigner {
    fn sign(&self, _msg: &[u8]) -> Result<Signature, SignError> {
        Ok(Signature { alg: "none".into(), bytes: vec![] })
    }
}

let signer = NoopSigner;
let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?
    .sign(&signer)?;
Source

pub fn freeze(self) -> Result<Self, LogLineError>

Congela o DRAFT em PENDING (pronto para sign/commit/ghost).

Valida os invariants antes de fazer a transição. Após freeze(), o LogLine está pronto para ser assinado e commitado, ou abandonado como Ghost.

§Erros
  • LogLineError::InvalidTransition se o status não for Draft
  • LogLineError::MissingField ou LogLineError::MissingInvariant se os invariants falharem
§Exemplo
use logline_core::*;

let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?
    .freeze()?;

assert_eq!(line.status, Status::Pending);
Source

pub fn freeze_with_registry( self, registry: &dyn VerbRegistry, ) -> Result<Self, LogLineError>

Congela com validação de verbo contra ALLOWED_ACTIONS (Paper I: verbo deve estar no registro).

Equivalente a freeze(), mas valida primeiro se o verbo (did) está permitido no sistema através do VerbRegistry. Útil para implementar políticas de segurança onde apenas verbos específicos são permitidos.

§Erros
  • LogLineError::MissingField("did (unknown verb)") se o verbo não estiver no registro
  • Erros de freeze() se os invariants falharem
§Exemplo
use logline_core::*;

struct SimpleRegistry;
impl VerbRegistry for SimpleRegistry {
    fn is_allowed(&self, verb: &Verb) -> bool {
        matches!(verb, Verb::Transfer | Verb::Approve)
    }
}

let registry = SimpleRegistry;
let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?
    .freeze_with_registry(&registry)?;
Source

pub fn commit(self, signer: &dyn Signer) -> Result<Self, LogLineError>

Commit final (PENDING → COMMITTED). Paper I: requer assinatura obrigatória.

Transiciona o LogLine de Pending para Committed, assinando os bytes determinísticos. Uma vez Committed, o LogLine não pode mais ser modificado ou abandonado.

§Erros
  • LogLineError::InvalidTransition se o status não for Pending
  • LogLineError::Signing se a assinatura falhar
§Exemplo
use logline_core::*;

struct NoopSigner;
impl Signer for NoopSigner {
    fn sign(&self, _msg: &[u8]) -> Result<Signature, SignError> {
        Ok(Signature { alg: "none".into(), bytes: vec![] })
    }
}

let signer = NoopSigner;
let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?
    .freeze()?
    .commit(&signer)?;

assert_eq!(line.status, Status::Committed);
Source

pub fn abandon( self, reason: Option<String>, ) -> Result<GhostRecord, LogLineError>

Abandona intenção: DRAFT/PENDING → GHOST (forensics).

Versão sem assinatura (compatibilidade). Para versão assinada, use abandon_signed(). Cria um GhostRecord que preserva o LogLine original para análise forense.

§Erros
  • LogLineError::AlreadyCommitted se o LogLine já estiver Committed
§Exemplo
use logline_core::*;

let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Deploy)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "qa".into() })
    .if_not(FailureHandling { label: "not".into(), action: "rollback".into() })
    .build_draft()?;

let ghost = line.abandon(Some("user_cancelled".into()))?;
assert_eq!(ghost.status, Status::Ghost);
Source

pub fn abandon_signed( self, signer: &dyn Signer, reason: Option<String>, ) -> Result<GhostRecord, LogLineError>

Abandona intenção assinada: DRAFT/PENDING → GHOST (forensics).

Paper I: attempt já nasce assinado, então o abandon também deve ser assinado. Versão recomendada que assina o LogLine antes de criar o GhostRecord.

§Erros
  • LogLineError::AlreadyCommitted se o LogLine já estiver Committed
  • LogLineError::Signing se a assinatura falhar
§Exemplo
use logline_core::*;

struct NoopSigner;
impl Signer for NoopSigner {
    fn sign(&self, _msg: &[u8]) -> Result<Signature, SignError> {
        Ok(Signature { alg: "none".into(), bytes: vec![] })
    }
}

let signer = NoopSigner;
let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Deploy)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "qa".into() })
    .if_not(FailureHandling { label: "not".into(), action: "rollback".into() })
    .build_draft()?;

let ghost = line.abandon_signed(&signer, Some("timeout".into()))?;
assert_eq!(ghost.status, Status::Ghost);
Source

pub fn to_signable_bytes(&self) -> Vec<u8>

Bytes determinísticos “suficientes” para v0.1 (placeholder).

Gera uma representação determinística dos campos principais do LogLine para assinatura. Em versões futuras, isso será substituído por bytes canônicos JSON✯Atomic (via json_atomic).

Formato atual: who|verb|when|status|confirmed_by|this.kind

§Exemplo
use logline_core::*;

let line = LogLine::builder()
    .who("did:ubl:alice")
    .did(Verb::Approve)
    .when(1_735_671_234)
    .if_ok(Outcome { label: "ok".into(), effects: vec![] })
    .if_doubt(Escalation { label: "doubt".into(), route_to: "auditor".into() })
    .if_not(FailureHandling { label: "not".into(), action: "notify".into() })
    .build_draft()?;

let bytes = line.to_signable_bytes();
assert!(!bytes.is_empty());

Trait Implementations§

Source§

impl Clone for LogLine

Source§

fn clone(&self) -> LogLine

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for LogLine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for LogLine

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for LogLine

Source§

fn eq(&self, other: &LogLine) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for LogLine

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for LogLine

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,