this repo has no description
1use crate::sync::firehose::SequencedEvent;
2use cid::Cid;
3use serde::{Deserialize, Serialize};
4use std::str::FromStr;
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).map_err(|_| "Invalid commit CID")?;
90 let json_ops: Vec<JsonRepoOp> =
91 serde_json::from_value(self.ops_json).unwrap_or_else(|_| vec![]);
92 let ops: Vec<RepoOp> = json_ops
93 .into_iter()
94 .map(|op| 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
102 .blobs
103 .iter()
104 .filter_map(|s| Cid::from_str(s).ok())
105 .collect();
106 let rev = placeholder_rev();
107 Ok(CommitFrame {
108 seq: self.seq,
109 rebase: false,
110 too_big: false,
111 repo: self.did,
112 commit: commit_cid,
113 rev,
114 since: self.prev_cid_str.as_ref().map(|_| placeholder_rev()),
115 blocks: Vec::new(),
116 ops,
117 blobs,
118 time: format_atproto_time(self.time),
119 prev_data: None,
120 })
121 }
122}
123
124fn placeholder_rev() -> String {
125 use jacquard::types::{integer::LimitedU32, string::Tid};
126 Tid::now(LimitedU32::MIN).to_string()
127}
128
129fn format_atproto_time(dt: chrono::DateTime<chrono::Utc>) -> String {
130 dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string()
131}
132
133impl TryFrom<SequencedEvent> for CommitFrame {
134 type Error = &'static str;
135
136 fn try_from(event: SequencedEvent) -> Result<Self, Self::Error> {
137 let builder = CommitFrameBuilder {
138 seq: event.seq,
139 did: event.did,
140 commit_cid_str: event.commit_cid.ok_or("Missing commit_cid in event")?,
141 prev_cid_str: event.prev_cid,
142 ops_json: event.ops.unwrap_or_default(),
143 blobs: event.blobs.unwrap_or_default(),
144 time: event.created_at,
145 };
146 builder.build()
147 }
148}