this repo has no description
1use cid::Cid;
2use serde::{Deserialize, Serialize};
3use std::str::FromStr;
4use crate::sync::firehose::SequencedEvent;
5
6#[derive(Debug, Serialize, Deserialize)]
7pub struct FrameHeader {
8 pub op: i64,
9 pub t: String,
10}
11
12#[derive(Debug, Serialize, Deserialize)]
13pub struct CommitFrame {
14 pub seq: i64,
15 pub rebase: bool,
16 #[serde(rename = "tooBig")]
17 pub too_big: bool,
18 pub repo: String,
19 pub commit: Cid,
20 pub rev: String,
21 pub since: Option<String>,
22 #[serde(with = "serde_bytes")]
23 pub blocks: Vec<u8>,
24 pub ops: Vec<RepoOp>,
25 pub blobs: Vec<Cid>,
26 pub time: String,
27 #[serde(rename = "prevData", skip_serializing_if = "Option::is_none")]
28 pub prev_data: Option<Cid>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32struct JsonRepoOp {
33 action: String,
34 path: String,
35 cid: Option<String>,
36 prev: Option<String>,
37}
38
39#[derive(Debug, Serialize, Deserialize)]
40pub struct RepoOp {
41 pub action: String,
42 pub path: String,
43 pub cid: Option<Cid>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub prev: Option<Cid>,
46}
47
48#[derive(Debug, Serialize, Deserialize)]
49pub struct IdentityFrame {
50 pub did: String,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub handle: Option<String>,
53 pub seq: i64,
54 pub time: String,
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58pub struct AccountFrame {
59 pub did: String,
60 pub active: bool,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub status: Option<String>,
63 pub seq: i64,
64 pub time: String,
65}
66
67#[derive(Debug, Serialize, Deserialize)]
68pub struct SyncFrame {
69 pub did: String,
70 pub rev: String,
71 #[serde(with = "serde_bytes")]
72 pub blocks: Vec<u8>,
73 pub seq: i64,
74 pub time: String,
75}
76
77pub struct CommitFrameBuilder {
78 pub seq: i64,
79 pub did: String,
80 pub commit_cid_str: String,
81 pub prev_cid_str: Option<String>,
82 pub ops_json: serde_json::Value,
83 pub blobs: Vec<String>,
84 pub time: chrono::DateTime<chrono::Utc>,
85}
86
87impl CommitFrameBuilder {
88 pub fn build(self) -> Result<CommitFrame, &'static str> {
89 let commit_cid = Cid::from_str(&self.commit_cid_str)
90 .map_err(|_| "Invalid commit CID")?;
91 let json_ops: Vec<JsonRepoOp> = serde_json::from_value(self.ops_json)
92 .unwrap_or_else(|_| vec![]);
93 let ops: Vec<RepoOp> = json_ops.into_iter().map(|op| {
94 RepoOp {
95 action: op.action,
96 path: op.path,
97 cid: op.cid.and_then(|s| Cid::from_str(&s).ok()),
98 prev: op.prev.and_then(|s| Cid::from_str(&s).ok()),
99 }
100 }).collect();
101 let blobs: Vec<Cid> = self.blobs.iter()
102 .filter_map(|s| Cid::from_str(s).ok())
103 .collect();
104 let rev = placeholder_rev();
105 Ok(CommitFrame {
106 seq: self.seq,
107 rebase: false,
108 too_big: false,
109 repo: self.did,
110 commit: commit_cid,
111 rev,
112 since: self.prev_cid_str.as_ref().map(|_| placeholder_rev()),
113 blocks: Vec::new(),
114 ops,
115 blobs,
116 time: format_atproto_time(self.time),
117 prev_data: None,
118 })
119 }
120}
121
122fn placeholder_rev() -> String {
123 use jacquard::types::{integer::LimitedU32, string::Tid};
124 Tid::now(LimitedU32::MIN).to_string()
125}
126
127fn format_atproto_time(dt: chrono::DateTime<chrono::Utc>) -> String {
128 dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string()
129}
130
131impl TryFrom<SequencedEvent> for CommitFrame {
132 type Error = &'static str;
133
134 fn try_from(event: SequencedEvent) -> Result<Self, Self::Error> {
135 let builder = CommitFrameBuilder {
136 seq: event.seq,
137 did: event.did,
138 commit_cid_str: event.commit_cid.ok_or("Missing commit_cid in event")?,
139 prev_cid_str: event.prev_cid,
140 ops_json: event.ops.unwrap_or_default(),
141 blobs: event.blobs.unwrap_or_default(),
142 time: event.created_at,
143 };
144 builder.build()
145 }
146}