Streaming Tree ARchive format
at rust-impl 115 lines 3.3 kB view raw
1use crate::error::{Result, StarError}; 2use cid::Cid; 3use serde::{Deserialize, Serialize}; 4use serde_bytes::ByteBuf; 5use sha2::{Digest, Sha256}; 6 7// --- STAR Types (Wire Format) --- 8 9/// The STAR Commit object 10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 11pub struct StarCommit { 12 pub did: String, 13 pub version: i64, 14 #[serde(default, skip_serializing_if = "Option::is_none")] 15 pub data: Option<Cid>, 16 pub rev: String, 17 #[serde(default, skip_serializing_if = "Option::is_none")] 18 pub prev: Option<Cid>, 19 #[serde(default, skip_serializing_if = "Option::is_none")] 20 pub sig: Option<ByteBuf>, 21} 22 23/// The STAR MST Node object 24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 25pub struct StarMstNode { 26 #[serde(default, skip_serializing_if = "Option::is_none")] 27 pub l: Option<Cid>, 28 #[serde(rename = "L", default, skip_serializing_if = "Option::is_none")] 29 pub l_archived: Option<bool>, 30 pub e: Vec<StarMstEntry>, 31} 32 33/// The STAR MST Entry object 34#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 35pub struct StarMstEntry { 36 pub p: u32, 37 pub k: ByteBuf, 38 #[serde(default, skip_serializing_if = "Option::is_none")] 39 pub v: Option<Cid>, 40 #[serde(rename = "V", default, skip_serializing_if = "Option::is_none")] 41 pub v_archived: Option<bool>, 42 #[serde(default, skip_serializing_if = "Option::is_none")] 43 pub t: Option<Cid>, 44 #[serde(rename = "T", default, skip_serializing_if = "Option::is_none")] 45 pub t_archived: Option<bool>, 46} 47 48// --- CAR Repo Types (Canonical / Hashing Format) --- 49 50/// The Canonical MST Node object (for CID calculation) 51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 52pub struct RepoMstNode { 53 pub l: Option<Cid>, 54 pub e: Vec<RepoMstEntry>, 55} 56 57/// The Canonical MST Entry object 58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 59pub struct RepoMstEntry { 60 pub p: u32, 61 pub k: ByteBuf, 62 pub v: Cid, // Required in Repo spec 63 pub t: Option<Cid>, 64} 65 66// --- Conversion --- 67 68impl StarMstNode { 69 pub fn to_repo(&self) -> Result<RepoMstNode> { 70 let mut entries = Vec::with_capacity(self.e.len()); 71 for e in &self.e { 72 entries.push(RepoMstEntry { 73 p: e.p, 74 k: e.k.clone(), 75 v: e.v.ok_or_else(|| { 76 StarError::InvalidState( 77 "Cannot convert implicit STAR entry to Repo entry: missing 'v'".into(), 78 ) 79 })?, 80 t: e.t, 81 }); 82 } 83 Ok(RepoMstNode { 84 l: self.l, 85 e: entries, 86 }) 87 } 88} 89 90/// Calculates the MST height of a key (number of leading zero bits in SHA256 / 2). 91pub fn calculate_height(key: &[u8]) -> u32 { 92 let digest = Sha256::digest(key); 93 let mut zeros = 0; 94 for &byte in digest.iter() { 95 if byte == 0 { 96 zeros += 8; 97 } else { 98 zeros += byte.leading_zeros(); 99 break; 100 } 101 } 102 zeros / 2 103} 104 105/// A parsed item from the STAR stream 106#[derive(Debug, Clone)] 107pub enum StarItem { 108 Commit(StarCommit), 109 Node(StarMstNode), 110 Record { 111 key: Vec<u8>, 112 cid: Cid, 113 content: Option<Vec<u8>>, 114 }, 115}