jacquard_api/sh_weaver/edit/
diff.rs1#[allow(unused_imports)]
9use alloc::collections::BTreeMap;
10
11#[allow(unused_imports)]
12use core::marker::PhantomData;
13use jacquard_common::CowStr;
14use jacquard_common::deps::bytes::Bytes;
15
16#[allow(unused_imports)]
17use jacquard_common::deps::codegen::unicode_segmentation::UnicodeSegmentation;
18use jacquard_common::types::blob::BlobRef;
19use jacquard_common::types::collection::{Collection, RecordError};
20use jacquard_common::types::string::{AtUri, Cid, Datetime};
21use jacquard_common::types::uri::{RecordUri, UriError};
22use jacquard_common::xrpc::XrpcResp;
23use jacquard_derive::{IntoStatic, lexicon};
24use jacquard_lexicon::lexicon::LexiconDoc;
25use jacquard_lexicon::schema::LexiconSchema;
26
27#[allow(unused_imports)]
28use jacquard_lexicon::validation::{ConstraintError, ValidationPath};
29use serde::{Serialize, Deserialize};
30use crate::com_atproto::repo::strong_ref::StrongRef;
31use crate::sh_weaver::edit::DocRef;
32#[lexicon]
35#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
36#[serde(rename_all = "camelCase", rename = "sh.weaver.edit.diff", tag = "$type")]
37pub struct Diff<'a> {
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub created_at: Option<Datetime>,
40 #[serde(borrow)]
41 pub doc: DocRef<'a>,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 #[serde(default, with = "jacquard_common::opt_serde_bytes_helper")]
45 pub inline_diff: Option<Bytes>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 #[serde(borrow)]
48 pub prev: Option<StrongRef<'a>>,
49 #[serde(borrow)]
50 pub root: StrongRef<'a>,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 #[serde(borrow)]
54 pub snapshot: Option<BlobRef<'a>>,
55}
56
57#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, IntoStatic)]
60#[serde(rename_all = "camelCase")]
61pub struct DiffGetRecordOutput<'a> {
62 #[serde(skip_serializing_if = "Option::is_none")]
63 #[serde(borrow)]
64 pub cid: Option<Cid<'a>>,
65 #[serde(borrow)]
66 pub uri: AtUri<'a>,
67 #[serde(borrow)]
68 pub value: Diff<'a>,
69}
70
71impl<'a> Diff<'a> {
72 pub fn uri(
73 uri: impl Into<CowStr<'a>>,
74 ) -> Result<RecordUri<'a, DiffRecord>, UriError> {
75 RecordUri::try_from_uri(AtUri::new_cow(uri.into())?)
76 }
77}
78
79#[derive(Debug, Serialize, Deserialize)]
82pub struct DiffRecord;
83impl XrpcResp for DiffRecord {
84 const NSID: &'static str = "sh.weaver.edit.diff";
85 const ENCODING: &'static str = "application/json";
86 type Output<'de> = DiffGetRecordOutput<'de>;
87 type Err<'de> = RecordError<'de>;
88}
89
90impl From<DiffGetRecordOutput<'_>> for Diff<'_> {
91 fn from(output: DiffGetRecordOutput<'_>) -> Self {
92 use jacquard_common::IntoStatic;
93 output.value.into_static()
94 }
95}
96
97impl Collection for Diff<'_> {
98 const NSID: &'static str = "sh.weaver.edit.diff";
99 type Record = DiffRecord;
100}
101
102impl Collection for DiffRecord {
103 const NSID: &'static str = "sh.weaver.edit.diff";
104 type Record = DiffRecord;
105}
106
107impl<'a> LexiconSchema for Diff<'a> {
108 fn nsid() -> &'static str {
109 "sh.weaver.edit.diff"
110 }
111 fn def_name() -> &'static str {
112 "main"
113 }
114 fn lexicon_doc() -> LexiconDoc<'static> {
115 lexicon_doc_sh_weaver_edit_diff()
116 }
117 fn validate(&self) -> Result<(), ConstraintError> {
118 if let Some(ref value) = self.snapshot {
119 {
120 let size = value.blob().size;
121 if size > 3000000usize {
122 return Err(ConstraintError::BlobTooLarge {
123 path: ValidationPath::from_field("snapshot"),
124 max: 3000000usize,
125 actual: size,
126 });
127 }
128 }
129 }
130 if let Some(ref value) = self.snapshot {
131 {
132 let mime = value.blob().mime_type.as_str();
133 let accepted: &[&str] = &["*/*"];
134 let matched = accepted
135 .iter()
136 .any(|pattern| {
137 if *pattern == "*/*" {
138 true
139 } else if pattern.ends_with("/*") {
140 let prefix = &pattern[..pattern.len() - 2];
141 mime.starts_with(prefix)
142 && mime.as_bytes().get(prefix.len()) == Some(&b'/')
143 } else {
144 mime == *pattern
145 }
146 });
147 if !matched {
148 return Err(ConstraintError::BlobMimeTypeNotAccepted {
149 path: ValidationPath::from_field("snapshot"),
150 accepted: vec!["*/*".to_string()],
151 actual: mime.to_string(),
152 });
153 }
154 }
155 }
156 Ok(())
157 }
158}
159
160pub mod diff_state {
161
162 pub use crate::builder_types::{Set, Unset, IsSet, IsUnset};
163 #[allow(unused)]
164 use ::core::marker::PhantomData;
165 mod sealed {
166 pub trait Sealed {}
167 }
168 pub trait State: sealed::Sealed {
170 type Root;
171 type Doc;
172 }
173 pub struct Empty(());
175 impl sealed::Sealed for Empty {}
176 impl State for Empty {
177 type Root = Unset;
178 type Doc = Unset;
179 }
180 pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>);
182 impl<S: State> sealed::Sealed for SetRoot<S> {}
183 impl<S: State> State for SetRoot<S> {
184 type Root = Set<members::root>;
185 type Doc = S::Doc;
186 }
187 pub struct SetDoc<S: State = Empty>(PhantomData<fn() -> S>);
189 impl<S: State> sealed::Sealed for SetDoc<S> {}
190 impl<S: State> State for SetDoc<S> {
191 type Root = S::Root;
192 type Doc = Set<members::doc>;
193 }
194 #[allow(non_camel_case_types)]
196 pub mod members {
197 pub struct root(());
199 pub struct doc(());
201 }
202}
203
204pub struct DiffBuilder<'a, S: diff_state::State> {
206 _state: PhantomData<fn() -> S>,
207 _fields: (
208 Option<Datetime>,
209 Option<DocRef<'a>>,
210 Option<Bytes>,
211 Option<StrongRef<'a>>,
212 Option<StrongRef<'a>>,
213 Option<BlobRef<'a>>,
214 ),
215 _lifetime: PhantomData<&'a ()>,
216}
217
218impl<'a> Diff<'a> {
219 pub fn new() -> DiffBuilder<'a, diff_state::Empty> {
221 DiffBuilder::new()
222 }
223}
224
225impl<'a> DiffBuilder<'a, diff_state::Empty> {
226 pub fn new() -> Self {
228 DiffBuilder {
229 _state: PhantomData,
230 _fields: (None, None, None, None, None, None),
231 _lifetime: PhantomData,
232 }
233 }
234}
235
236impl<'a, S: diff_state::State> DiffBuilder<'a, S> {
237 pub fn created_at(mut self, value: impl Into<Option<Datetime>>) -> Self {
239 self._fields.0 = value.into();
240 self
241 }
242 pub fn maybe_created_at(mut self, value: Option<Datetime>) -> Self {
244 self._fields.0 = value;
245 self
246 }
247}
248
249impl<'a, S> DiffBuilder<'a, S>
250where
251 S: diff_state::State,
252 S::Doc: diff_state::IsUnset,
253{
254 pub fn doc(
256 mut self,
257 value: impl Into<DocRef<'a>>,
258 ) -> DiffBuilder<'a, diff_state::SetDoc<S>> {
259 self._fields.1 = Option::Some(value.into());
260 DiffBuilder {
261 _state: PhantomData,
262 _fields: self._fields,
263 _lifetime: PhantomData,
264 }
265 }
266}
267
268impl<'a, S: diff_state::State> DiffBuilder<'a, S> {
269 pub fn inline_diff(mut self, value: impl Into<Option<Bytes>>) -> Self {
271 self._fields.2 = value.into();
272 self
273 }
274 pub fn maybe_inline_diff(mut self, value: Option<Bytes>) -> Self {
276 self._fields.2 = value;
277 self
278 }
279}
280
281impl<'a, S: diff_state::State> DiffBuilder<'a, S> {
282 pub fn prev(mut self, value: impl Into<Option<StrongRef<'a>>>) -> Self {
284 self._fields.3 = value.into();
285 self
286 }
287 pub fn maybe_prev(mut self, value: Option<StrongRef<'a>>) -> Self {
289 self._fields.3 = value;
290 self
291 }
292}
293
294impl<'a, S> DiffBuilder<'a, S>
295where
296 S: diff_state::State,
297 S::Root: diff_state::IsUnset,
298{
299 pub fn root(
301 mut self,
302 value: impl Into<StrongRef<'a>>,
303 ) -> DiffBuilder<'a, diff_state::SetRoot<S>> {
304 self._fields.4 = Option::Some(value.into());
305 DiffBuilder {
306 _state: PhantomData,
307 _fields: self._fields,
308 _lifetime: PhantomData,
309 }
310 }
311}
312
313impl<'a, S: diff_state::State> DiffBuilder<'a, S> {
314 pub fn snapshot(mut self, value: impl Into<Option<BlobRef<'a>>>) -> Self {
316 self._fields.5 = value.into();
317 self
318 }
319 pub fn maybe_snapshot(mut self, value: Option<BlobRef<'a>>) -> Self {
321 self._fields.5 = value;
322 self
323 }
324}
325
326impl<'a, S> DiffBuilder<'a, S>
327where
328 S: diff_state::State,
329 S::Root: diff_state::IsSet,
330 S::Doc: diff_state::IsSet,
331{
332 pub fn build(self) -> Diff<'a> {
334 Diff {
335 created_at: self._fields.0,
336 doc: self._fields.1.unwrap(),
337 inline_diff: self._fields.2,
338 prev: self._fields.3,
339 root: self._fields.4.unwrap(),
340 snapshot: self._fields.5,
341 extra_data: Default::default(),
342 }
343 }
344 pub fn build_with_data(
346 self,
347 extra_data: BTreeMap<
348 jacquard_common::deps::smol_str::SmolStr,
349 jacquard_common::types::value::Data<'a>,
350 >,
351 ) -> Diff<'a> {
352 Diff {
353 created_at: self._fields.0,
354 doc: self._fields.1.unwrap(),
355 inline_diff: self._fields.2,
356 prev: self._fields.3,
357 root: self._fields.4.unwrap(),
358 snapshot: self._fields.5,
359 extra_data: Some(extra_data),
360 }
361 }
362}
363
364fn lexicon_doc_sh_weaver_edit_diff() -> LexiconDoc<'static> {
365 #[allow(unused_imports)]
366 use jacquard_common::{CowStr, deps::smol_str::SmolStr, types::blob::MimeType};
367 use jacquard_lexicon::lexicon::*;
368 use alloc::collections::BTreeMap;
369 LexiconDoc {
370 lexicon: Lexicon::Lexicon1,
371 id: CowStr::new_static("sh.weaver.edit.diff"),
372 defs: {
373 let mut map = BTreeMap::new();
374 map.insert(
375 SmolStr::new_static("main"),
376 LexUserType::Record(LexRecord {
377 description: Some(
378 CowStr::new_static("An edit record for a notebook."),
379 ),
380 key: Some(CowStr::new_static("tid")),
381 record: LexRecordRecord::Object(LexObject {
382 required: Some(
383 vec![SmolStr::new_static("root"), SmolStr::new_static("doc")],
384 ),
385 properties: {
386 #[allow(unused_mut)]
387 let mut map = BTreeMap::new();
388 map.insert(
389 SmolStr::new_static("createdAt"),
390 LexObjectProperty::String(LexString {
391 format: Some(LexStringFormat::Datetime),
392 ..Default::default()
393 }),
394 );
395 map.insert(
396 SmolStr::new_static("doc"),
397 LexObjectProperty::Ref(LexRef {
398 r#ref: CowStr::new_static("sh.weaver.edit.defs#docRef"),
399 ..Default::default()
400 }),
401 );
402 map.insert(
403 SmolStr::new_static("inlineDiff"),
404 LexObjectProperty::Bytes(LexBytes {
405 max_length: Some(8192usize),
406 ..Default::default()
407 }),
408 );
409 map.insert(
410 SmolStr::new_static("prev"),
411 LexObjectProperty::Ref(LexRef {
412 r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
413 ..Default::default()
414 }),
415 );
416 map.insert(
417 SmolStr::new_static("root"),
418 LexObjectProperty::Ref(LexRef {
419 r#ref: CowStr::new_static("com.atproto.repo.strongRef"),
420 ..Default::default()
421 }),
422 );
423 map.insert(
424 SmolStr::new_static("snapshot"),
425 LexObjectProperty::Blob(LexBlob { ..Default::default() }),
426 );
427 map
428 },
429 ..Default::default()
430 }),
431 ..Default::default()
432 }),
433 );
434 map
435 },
436 ..Default::default()
437 }
438}