1use lazy_static::lazy_static;
13use regex::Regex;
14use serde::{Deserialize, Serialize};
15
16use ng_repo::errors::NgError;
17#[allow(unused_imports)]
18use ng_repo::log::*;
19use ng_repo::repo::CommitInfo;
20use ng_repo::types::*;
21use ng_repo::utils::{decode_digest, decode_key, decode_sym_key};
22use ng_repo::utils::{decode_overlayid, display_timestamp_local};
23
24use crate::types::*;
25
26lazy_static! {
27 #[doc(hidden)]
28 static ref RE_FILE_READ_CAP: Regex =
29 Regex::new(r"^did:ng:j:([A-Za-z0-9-_]*):k:([A-Za-z0-9-_]*)$").unwrap();
30 #[doc(hidden)]
31 static ref RE_REPO_O: Regex =
32 Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*)$").unwrap();
33 #[doc(hidden)]
34 static ref RE_REPO: Regex =
35 Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*):v:([A-Za-z0-9-_]*)$").unwrap();
36 #[doc(hidden)]
37 static ref RE_BRANCH: Regex =
38 Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*):v:([A-Za-z0-9-_]*):b:([A-Za-z0-9-_]*)$").unwrap();
39 #[doc(hidden)]
40 static ref RE_NAMED_BRANCH_OR_COMMIT: Regex =
41 Regex::new(r"^did:ng:o:([A-Za-z0-9-_]*):v:([A-Za-z0-9-_]*):a:([A-Za-z0-9-_%]*)$").unwrap(); #[doc(hidden)]
43 static ref RE_OBJECTS: Regex =
44 Regex::new(r"^did:ng(?::o:([A-Za-z0-9-_]{44}))?:v:([A-Za-z0-9-_]{44})((?::[cj]:[A-Za-z0-9-_]{44}:k:[A-Za-z0-9-_]{44})+)(?::s:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44}))?:l:([A-Za-z0-9-_]*)$").unwrap();
45 #[doc(hidden)]
46 static ref RE_OBJECT_READ_CAPS: Regex =
47 Regex::new(r":[cj]:([A-Za-z0-9-_]{44}):k:([A-Za-z0-9-_]{44})").unwrap();
48
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize)]
52pub enum AppFetchContentV0 {
53 Get, Subscribe,
55 Update,
56 ReadQuery,
57 WriteQuery,
58 RdfDump,
59 History,
60 SignatureStatus,
61 SignatureRequest,
62 SignedSnapshotRequest,
63 Header,
64 }
66
67impl AppFetchContentV0 {
68 pub fn get_or_subscribe(subscribe: bool) -> Self {
69 if !subscribe {
70 AppFetchContentV0::Get
71 } else {
72 AppFetchContentV0::Subscribe
73 }
74 }
75}
76
77#[derive(Clone, Debug, Serialize, Deserialize)]
78pub enum NgAccessV0 {
79 ReadCap(ReadCap),
80 Token(Digest),
81 #[serde(with = "serde_bytes")]
82 ExtRequest(Vec<u8>),
83 Key(BlockKey),
84 Inbox(PubKey),
85}
86
87#[derive(Clone, Debug, Serialize, Deserialize)]
88pub enum TargetBranchV0 {
89 Chat,
90 Stream,
91 Comments,
92 BackLinks,
93 Context,
94 BranchId(BranchId),
95 Named(String), Commits(Vec<ObjectId>), }
98
99impl TargetBranchV0 {
100 pub fn is_valid_for_sparql_update(&self) -> bool {
101 match self {
102 Self::Commits(_) => false,
103 _ => true,
104 }
105 }
106 pub fn is_valid_for_discrete_update(&self) -> bool {
107 match self {
108 Self::BranchId(_) => true,
109 _ => false,
111 }
112 }
113 pub fn branch_id(&self) -> &BranchId {
114 match self {
115 Self::BranchId(id) => id,
116 _ => panic!("not a TargetBranchV0::BranchId"),
117 }
118 }
119}
120
121#[derive(Clone, Debug, Serialize, Deserialize)]
122pub enum NuriTargetV0 {
123 UserSite, PublicStore,
126 ProtectedStore,
127 PrivateStore,
128 AllDialogs,
129 Dialog(String), AllGroups,
131 Group(String), Repo(RepoId),
134
135 None,
136}
137
138impl NuriTargetV0 {
139 pub fn is_valid_for_sparql_update(&self) -> bool {
140 match self {
141 Self::UserSite | Self::AllDialogs | Self::AllGroups => false,
142 _ => true,
143 }
144 }
145 pub fn is_valid_for_discrete_update(&self) -> bool {
146 match self {
147 Self::UserSite | Self::AllDialogs | Self::AllGroups | Self::None => false,
148 _ => true,
149 }
150 }
151 pub fn is_repo_id(&self) -> bool {
152 match self {
153 Self::Repo(_) => true,
154 _ => false,
155 }
156 }
157 pub fn repo_id(&self) -> &RepoId {
158 match self {
159 Self::Repo(id) => id,
160 _ => panic!("not a NuriTargetV0::Repo"),
161 }
162 }
163}
164
165#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
166pub struct CommitInfoJs {
167 pub past: Vec<String>,
168 pub key: String,
169 pub signature: Option<String>,
170 pub author: String,
171 pub timestamp: String,
172 pub final_consistency: bool,
173 pub commit_type: CommitType,
174 pub branch: Option<String>,
175 pub x: u32,
176 pub y: u32,
177}
178
179impl From<&CommitInfo> for CommitInfoJs {
180 fn from(info: &CommitInfo) -> Self {
181 CommitInfoJs {
182 past: info.past.iter().map(|objid| objid.to_string()).collect(),
183 key: info.key.to_string(),
184 signature: info.signature.as_ref().map(|s| NuriV0::signature_ref(&s)),
185 author: info.author.clone(),
186 timestamp: display_timestamp_local(info.timestamp),
187 final_consistency: info.final_consistency,
188 commit_type: info.commit_type.clone(),
189 branch: info.branch.map(|b| b.to_string()),
190 x: info.x,
191 y: info.y,
192 }
193 }
194}
195
196const DID_PREFIX: &str = "did:ng";
197
198#[derive(Clone, Debug, Serialize, Deserialize)]
199pub struct NuriV0 {
200 pub identity: Option<UserId>, pub target: NuriTargetV0,
202 pub entire_store: bool, pub objects: Vec<ObjectRef>, pub signature: Option<ObjectRef>,
206
207 pub branch: Option<TargetBranchV0>, pub overlay: Option<OverlayLink>,
209
210 pub access: Vec<NgAccessV0>,
211 pub topic: Option<TopicId>,
212 pub locator: Option<Locator>,
213}
214
215impl NuriV0 {
216 pub fn new_empty() -> Self {
217 NuriV0 {
218 identity: None,
219 target: NuriTargetV0::None,
220 entire_store: false,
221 objects: vec![],
222 signature: None,
223 branch: None,
224 overlay: None,
225 access: vec![],
226 topic: None,
227 locator: None,
228 }
229 }
230 pub fn copy_target_from(&mut self, nuri: &NuriV0) {
231 self.target = nuri.target.clone();
232 }
233 pub fn commit_graph_name(commit_id: &ObjectId, overlay_id: &OverlayId) -> String {
234 format!("{DID_PREFIX}:c:{commit_id}:v:{overlay_id}")
235 }
236
237 pub fn commit_graph_name_from_base64(commit_base64: &String, overlay_id: &OverlayId) -> String {
238 format!("{DID_PREFIX}:c:{commit_base64}:v:{overlay_id}")
239 }
240
241 pub fn from_store_repo(store_repo: &StoreRepo) -> Self {
242 NuriV0 {
243 identity: None,
244 target: NuriTargetV0::Repo(store_repo.repo_id().clone()),
245 entire_store: false,
246 objects: vec![],
247 signature: None,
248 branch: None,
249 overlay: None,
250 access: vec![],
251 topic: None,
252 locator: None,
253 }
254 }
255
256 pub fn to_store_nuri_string(store_id: &RepoId) -> String {
257 let overlay_id = OverlayId::outer(store_id);
258 format!("o:{store_id}:v:{overlay_id}")
259 }
260
261 pub fn repo_graph_name(repo_id: &RepoId, overlay_id: &OverlayId) -> String {
262 format!("{DID_PREFIX}:o:{repo_id}:v:{overlay_id}")
263 }
264
265 pub fn branch_repo_graph_name(
266 branch_id: &BranchId,
267 repo_id: &RepoId,
268 overlay_id: &OverlayId,
269 ) -> String {
270 format!("{DID_PREFIX}:o:{repo_id}:v:{overlay_id}:b:{branch_id}")
271 }
272
273 pub fn repo_skolem(
274 repo_id: &RepoId,
275 peer_id: &Vec<u8>,
276 random: u128,
277 ) -> Result<String, NgError> {
278 let mut arr = Vec::with_capacity(32);
279 arr.extend_from_slice(peer_id);
280 arr.extend_from_slice(&random.to_be_bytes());
281 let sko: SymKey = arr.as_slice().try_into()?;
282 Ok(format!("{DID_PREFIX}:o:{repo_id}:u:{sko}"))
283 }
284
285 pub fn repo(&self) -> String {
286 Self::repo_id(self.target.repo_id())
287 }
288
289 pub fn repo_id(repo_id: &RepoId) -> String {
290 format!("{DID_PREFIX}:o:{}", repo_id)
291 }
292
293 pub fn overlay_id(overlay_id: &OverlayId) -> String {
294 format!("{DID_PREFIX}:v:{overlay_id}")
295 }
296
297 pub fn topic_id(topic_id: &TopicId) -> String {
298 format!("{DID_PREFIX}:h:{topic_id}")
299 }
300
301 pub fn branch_id(branch_id: &BranchId) -> String {
302 format!("{DID_PREFIX}:b:{branch_id}")
303 }
304
305 pub fn branch_id_from_base64(branch_base64: &String) -> String {
306 format!("{DID_PREFIX}:b:{branch_base64}")
307 }
308
309 pub fn object_ref(obj_ref: &ObjectRef) -> String {
310 format!("{DID_PREFIX}:{}", obj_ref.object_nuri())
311 }
312
313 pub fn signature_ref(obj_ref: &ObjectRef) -> String {
314 format!("s:{}:k:{}", obj_ref.id, obj_ref.key)
315 }
316
317 pub fn token(token: &Digest) -> String {
318 format!("{DID_PREFIX}:n:{token}")
319 }
320
321 pub fn tokenized_commit(repo_id: &RepoId, commit_id: &ObjectId) -> String {
322 format!("{DID_PREFIX}:o:{repo_id}:t:{commit_id}")
323 }
324
325 pub fn commit(repo_id: &RepoId, commit_id: &ObjectId) -> String {
326 format!("{DID_PREFIX}:o:{repo_id}:c:{commit_id}")
327 }
328
329 pub fn locator(locator: &Locator) -> String {
330 format!("l:{locator}")
331 }
332
333 pub fn is_branch_identifier(&self) -> bool {
334 self.locator.is_none()
335 && self.topic.is_none()
336 && self.access.is_empty()
337 && self.overlay.as_ref().map_or(false, |o| o.is_outer())
338 && self
339 .branch
340 .as_ref()
341 .map_or(true, |b| b.is_valid_for_sparql_update())
342 && self.objects.is_empty()
343 && self.signature.is_none()
344 && !self.entire_store
345 && self.target.is_repo_id()
346 }
347
348 pub fn is_valid_for_sparql_update(&self) -> bool {
349 self.objects.is_empty()
350 && self.signature.is_none()
351 && self.entire_store == false
352 && self.target.is_valid_for_sparql_update()
353 && self
354 .branch
355 .as_ref()
356 .map_or(true, |b| b.is_valid_for_sparql_update())
357 }
358 pub fn is_valid_for_discrete_update(&self) -> bool {
359 self.objects.is_empty()
360 && self.signature.is_none()
361 && self.entire_store == false
362 && self.target.is_valid_for_discrete_update()
363 && self
364 .branch
365 .as_ref()
366 .map_or(true, |b| b.is_valid_for_discrete_update())
367 }
368 pub fn new_repo_target_from_string(repo_id_string: String) -> Result<Self, NgError> {
369 let repo_id: RepoId = repo_id_string.as_str().try_into()?;
370 Ok(Self {
371 identity: None,
372 target: NuriTargetV0::Repo(repo_id),
373 entire_store: false,
374 objects: vec![],
375 signature: None,
376 branch: None,
377 overlay: None,
378 access: vec![],
379 topic: None,
380 locator: None,
381 })
382 }
383
384 pub fn new_from_obj_ref(obj_ref: &ObjectRef) -> Self {
385 Self {
386 identity: None,
387 target: NuriTargetV0::None,
388 entire_store: false,
389 objects: vec![obj_ref.clone()],
390 signature: None,
391 branch: None,
392 overlay: None,
393 access: vec![],
394 topic: None,
395 locator: None,
396 }
397 }
398
399 pub fn new_private_store_target() -> Self {
400 Self {
401 identity: None,
402 target: NuriTargetV0::PrivateStore,
403 entire_store: false,
404 objects: vec![],
405 signature: None,
406 branch: None,
407 overlay: None,
408 access: vec![],
409 topic: None,
410 locator: None,
411 }
412 }
413 pub fn new_entire_user_site() -> Self {
414 Self {
415 identity: None,
416 target: NuriTargetV0::UserSite,
417 entire_store: false,
418 objects: vec![],
419 signature: None,
420 branch: None,
421 overlay: None,
422 access: vec![],
423 topic: None,
424 locator: None,
425 }
426 }
427 pub fn new_for_readcaps(from: &str) -> Result<Self, NgError> {
428 let c = RE_OBJECTS.captures(from);
429 if let Some(c) = c {
430 let target = c.get(1).map_or(NuriTargetV0::None, |repo_match| {
431 if let Ok(id) = decode_key(repo_match.as_str()) {
432 NuriTargetV0::Repo(id)
433 } else {
434 NuriTargetV0::None
435 }
436 });
437 let overlay_id = decode_overlayid(c.get(2).ok_or(NgError::InvalidNuri)?.as_str())?;
438 let read_caps = c.get(3).ok_or(NgError::InvalidNuri)?.as_str();
439 let sign_obj_id = c.get(4).map(|c| decode_digest(c.as_str()));
440 let sign_obj_key = c.get(5).map(|c| decode_sym_key(c.as_str()));
441 let locator =
442 TryInto::<Locator>::try_into(c.get(6).ok_or(NgError::InvalidNuri)?.as_str())?;
443 let signature = if sign_obj_id.is_some() && sign_obj_key.is_some() {
444 Some(ObjectRef::from_id_key(
445 sign_obj_id.unwrap()?,
446 sign_obj_key.unwrap()?,
447 ))
448 } else {
449 None
450 };
451
452 let objects = RE_OBJECT_READ_CAPS
453 .captures_iter(read_caps)
454 .map(|c| {
455 Ok(ObjectRef::from_id_key(
456 decode_digest(c.get(1).ok_or(NgError::InvalidNuri)?.as_str())?,
457 decode_sym_key(c.get(2).ok_or(NgError::InvalidNuri)?.as_str())?,
458 ))
459 })
460 .collect::<Result<Vec<ObjectRef>, NgError>>()?;
461
462 if objects.len() < 1 {
463 return Err(NgError::InvalidNuri);
464 }
465
466 Ok(Self {
467 identity: None,
468 target,
469 entire_store: false,
470 objects,
471 signature,
472 branch: None,
473 overlay: Some(overlay_id.into()),
474 access: vec![],
475 topic: None,
476 locator: Some(locator),
477 })
478 } else {
479 Err(NgError::InvalidNuri)
480 }
481 }
482
483 pub fn new_from(from: &String) -> Result<Self, NgError> {
484 let c = RE_REPO_O.captures(from);
485
486 if c.is_some() && c.as_ref().unwrap().get(1).is_some() {
487 let cap = c.unwrap();
488 let o = cap.get(1).unwrap().as_str();
489
490 let repo_id = decode_key(o)?;
491 Ok(Self {
492 identity: None,
493 target: NuriTargetV0::Repo(repo_id),
494 entire_store: false,
495 objects: vec![],
496 signature: None,
497 branch: None,
498 overlay: None,
499 access: vec![],
500 topic: None,
501 locator: None,
502 })
503 } else {
504 let c = RE_FILE_READ_CAP.captures(from);
505 if c.is_some()
506 && c.as_ref().unwrap().get(1).is_some()
507 && c.as_ref().unwrap().get(2).is_some()
508 {
509 let cap = c.unwrap();
510 let j = cap.get(1).unwrap().as_str();
511 let k = cap.get(2).unwrap().as_str();
512 let id = decode_digest(j)?;
513 let key = decode_sym_key(k)?;
514 Ok(Self {
515 identity: None,
516 target: NuriTargetV0::None,
517 entire_store: false,
518 objects: vec![ObjectRef::from_id_key(id, key)],
519 signature: None,
520 branch: None,
521 overlay: None,
522 access: vec![],
523 topic: None,
524 locator: None,
525 })
526 } else {
527 let c = RE_REPO.captures(from);
528
529 if c.is_some()
530 && c.as_ref().unwrap().get(1).is_some()
531 && c.as_ref().unwrap().get(2).is_some()
532 {
533 let cap = c.unwrap();
534 let o = cap.get(1).unwrap().as_str();
535
536 let v = cap.get(2).unwrap().as_str();
537 let repo_id = decode_key(o)?;
538 let overlay_id = decode_overlayid(v)?;
539 Ok(Self {
540 identity: None,
541 target: NuriTargetV0::Repo(repo_id),
542 entire_store: false,
543 objects: vec![],
544 signature: None,
545 branch: None,
546 overlay: Some(overlay_id.into()),
547 access: vec![],
548 topic: None,
549 locator: None,
550 })
551 } else {
552 let c = RE_BRANCH.captures(from);
553
554 if c.is_some()
555 && c.as_ref().unwrap().get(1).is_some()
556 && c.as_ref().unwrap().get(2).is_some()
557 && c.as_ref().unwrap().get(3).is_some()
558 {
559 let cap = c.unwrap();
560 let o = cap.get(1).unwrap().as_str();
561 let v = cap.get(2).unwrap().as_str();
562 let b = cap.get(3).unwrap().as_str();
563 let repo_id = decode_key(o)?;
564 let overlay_id = decode_overlayid(v)?;
565 let branch_id = decode_key(b)?;
566 Ok(Self {
567 identity: None,
568 target: NuriTargetV0::Repo(repo_id),
569 entire_store: false,
570 objects: vec![],
571 signature: None,
572 branch: Some(TargetBranchV0::BranchId(branch_id)),
573 overlay: Some(overlay_id.into()),
574 access: vec![],
575 topic: None,
576 locator: None,
577 })
578 } else {
579 Err(NgError::InvalidNuri)
580 }
581 }
582 }
583 }
584 }
585}
586
587#[derive(Clone, Debug, Serialize, Deserialize)]
588pub enum AppRequestCommandV0 {
589 Fetch(AppFetchContentV0),
590 Pin,
591 UnPin,
592 Delete,
593 Create,
594 FileGet, FilePut, Header,
597}
598
599impl AppRequestCommandV0 {
600 pub fn is_stream(&self) -> bool {
601 match self {
602 Self::Fetch(AppFetchContentV0::Subscribe) | Self::FileGet => true,
603 Self::FilePut
604 | Self::Create
605 | Self::Delete
606 | Self::UnPin
607 | Self::Pin
608 | Self::Header
609 | Self::Fetch(_) => false,
610 }
611 }
612 pub fn new_read_query() -> Self {
613 AppRequestCommandV0::Fetch(AppFetchContentV0::ReadQuery)
614 }
615 pub fn new_write_query() -> Self {
616 AppRequestCommandV0::Fetch(AppFetchContentV0::WriteQuery)
617 }
618 pub fn new_update() -> Self {
619 AppRequestCommandV0::Fetch(AppFetchContentV0::Update)
620 }
621 pub fn new_rdf_dump() -> Self {
622 AppRequestCommandV0::Fetch(AppFetchContentV0::RdfDump)
623 }
624 pub fn new_history() -> Self {
625 AppRequestCommandV0::Fetch(AppFetchContentV0::History)
626 }
627 pub fn new_signature_status() -> Self {
628 AppRequestCommandV0::Fetch(AppFetchContentV0::SignatureStatus)
629 }
630 pub fn new_signature_request() -> Self {
631 AppRequestCommandV0::Fetch(AppFetchContentV0::SignatureRequest)
632 }
633 pub fn new_signed_snapshot_request() -> Self {
634 AppRequestCommandV0::Fetch(AppFetchContentV0::SignedSnapshotRequest)
635 }
636 pub fn new_create() -> Self {
637 AppRequestCommandV0::Create
638 }
639 pub fn new_header() -> Self {
640 AppRequestCommandV0::Header
641 }
642 pub fn new_fetch_header() -> Self {
643 AppRequestCommandV0::Fetch(AppFetchContentV0::Header)
644 }
645}
646
647#[derive(Clone, Debug, Serialize, Deserialize)]
648pub struct AppRequestV0 {
649 pub command: AppRequestCommandV0,
650
651 pub nuri: NuriV0,
652
653 pub payload: Option<AppRequestPayload>,
654
655 pub session_id: u64,
656}
657
658#[derive(Clone, Debug, Serialize, Deserialize)]
659pub enum AppRequest {
660 V0(AppRequestV0),
661}
662
663impl AppRequest {
664 pub fn set_session_id(&mut self, session_id: u64) {
665 match self {
666 Self::V0(v0) => v0.session_id = session_id,
667 }
668 }
669 pub fn session_id(&self) -> u64 {
670 match self {
671 Self::V0(v0) => v0.session_id,
672 }
673 }
674 pub fn command(&self) -> &AppRequestCommandV0 {
675 match self {
676 Self::V0(v0) => &v0.command,
677 }
678 }
679 pub fn new(
680 command: AppRequestCommandV0,
681 nuri: NuriV0,
682 payload: Option<AppRequestPayload>,
683 ) -> Self {
684 AppRequest::V0(AppRequestV0 {
685 command,
686 nuri,
687 payload,
688 session_id: 0,
689 })
690 }
691
692 pub fn doc_fetch_repo_subscribe(repo_o: String) -> Result<Self, NgError> {
693 Ok(AppRequest::new(
694 AppRequestCommandV0::Fetch(AppFetchContentV0::get_or_subscribe(true)),
695 NuriV0::new_from(&repo_o)?,
696 None,
697 ))
698 }
699}
700
701#[derive(Clone, Debug, Serialize, Deserialize)]
702pub struct AppSessionStopV0 {
703 pub session_id: u64,
704 pub force_close: bool,
705}
706
707#[derive(Clone, Debug, Serialize, Deserialize)]
708pub enum AppSessionStop {
709 V0(AppSessionStopV0),
710}
711impl AppSessionStop {
712 pub fn session_id(&self) -> u64 {
713 match self {
714 Self::V0(v0) => v0.session_id,
715 }
716 }
717 pub fn is_force_close(&self) -> bool {
718 match self {
719 Self::V0(v0) => v0.force_close,
720 }
721 }
722}
723#[derive(Clone, Debug, Serialize, Deserialize)]
724pub struct AppSessionStartV0 {
725 pub session_id: u64,
726
727 pub credentials: Option<Credentials>,
728
729 pub user_id: UserId,
730
731 pub detach: bool,
732}
733
734#[derive(Clone, Debug, Serialize, Deserialize)]
735pub enum AppSessionStart {
736 V0(AppSessionStartV0),
737}
738
739#[derive(Clone, Debug, Serialize, Deserialize)]
740pub struct AppSessionStartResponseV0 {
741 pub private_store: RepoId,
742 pub protected_store: RepoId,
743 pub public_store: RepoId,
744}
745
746#[derive(Clone, Debug, Serialize, Deserialize)]
747pub enum AppSessionStartResponse {
748 V0(AppSessionStartResponseV0),
749}
750
751impl AppSessionStart {
752 pub fn session_id(&self) -> u64 {
753 match self {
754 Self::V0(v0) => v0.session_id,
755 }
756 }
757 pub fn credentials(&self) -> &Option<Credentials> {
758 match self {
759 Self::V0(v0) => &v0.credentials,
760 }
761 }
762 pub fn user_id(&self) -> &UserId {
763 match self {
764 Self::V0(v0) => &v0.user_id,
765 }
766 }
767}
768
769#[derive(Clone, Debug, Serialize, Deserialize)]
770pub enum DocQuery {
771 V0 {
772 sparql: String,
773 base: Option<String>,
774 },
775}
776
777#[derive(Clone, Debug, Serialize, Deserialize)]
778pub struct GraphUpdate {
779 #[serde(with = "serde_bytes")]
781 pub inserts: Vec<u8>,
782 #[serde(with = "serde_bytes")]
784 pub removes: Vec<u8>,
785}
786
787#[derive(Clone, Debug, Serialize, Deserialize)]
788pub enum DiscreteUpdate {
789 #[serde(with = "serde_bytes")]
791 YMap(Vec<u8>),
792 #[serde(with = "serde_bytes")]
793 YArray(Vec<u8>),
794 #[serde(with = "serde_bytes")]
795 YXml(Vec<u8>),
796 #[serde(with = "serde_bytes")]
797 YText(Vec<u8>),
798 #[serde(with = "serde_bytes")]
800 Automerge(Vec<u8>),
801}
802
803impl DiscreteUpdate {
804 pub fn from(crdt: String, update: Vec<u8>) -> Self {
805 match crdt.as_str() {
806 "YMap" => Self::YMap(update),
807 "YArray" => Self::YArray(update),
808 "YXml" => Self::YXml(update),
809 "YText" => Self::YText(update),
810 "Automerge" => Self::Automerge(update),
811 _ => panic!("wrong crdt type"),
812 }
813 }
814}
815
816#[derive(Clone, Debug, Serialize, Deserialize)]
817pub struct DocUpdate {
818 pub heads: Vec<ObjectId>,
819 pub graph: Option<GraphUpdate>,
820 pub discrete: Option<DiscreteUpdate>,
821}
822
823#[derive(Clone, Debug, Serialize, Deserialize)]
824pub struct DocAddFile {
825 pub filename: Option<String>,
826 pub object: ObjectRef,
827}
828
829#[derive(Clone, Debug, Serialize, Deserialize)]
830pub struct DocHeader {
831 pub title: Option<String>,
832 pub about: Option<String>,
833}
834
835#[derive(Clone, Debug, Serialize, Deserialize)]
836pub enum DocCreateDestination {
837 Store,
838 Stream,
839 MagicCarpet,
840}
841
842impl DocCreateDestination {
843 pub fn from(s: String) -> Result<Self, NgError> {
844 Ok(match s.as_str() {
845 "store" => Self::Store,
846 "stream" => Self::Stream,
847 "mc" => Self::MagicCarpet,
848 _ => return Err(NgError::InvalidArgument),
849 })
850 }
851}
852
853#[derive(Clone, Debug, Serialize, Deserialize)]
854pub struct DocCreate {
855 pub class: BranchCrdt,
856 pub destination: DocCreateDestination,
857}
858
859#[derive(Clone, Debug, Serialize, Deserialize)]
860pub struct DocDelete {
861 nuri: String,
863}
864
865#[derive(Clone, Debug, Serialize, Deserialize)]
866pub enum AppRequestPayloadV0 {
867 Create(DocCreate),
868 Query(DocQuery),
869 Update(DocUpdate),
870 AddFile(DocAddFile),
871
872 Delete(DocDelete),
873
874 SmallFilePut(SmallFile),
875 RandomAccessFilePut(String), RandomAccessFilePutChunk((u32, serde_bytes::ByteBuf)), Header(DocHeader),
879 }
882
883#[derive(Clone, Debug, Serialize, Deserialize)]
884pub enum AppRequestPayload {
885 V0(AppRequestPayloadV0),
886}
887
888impl AppRequestPayload {
889 pub fn new_sparql_query(sparql: String, base: Option<String>) -> Self {
890 AppRequestPayload::V0(AppRequestPayloadV0::Query(DocQuery::V0 { sparql, base }))
891 }
892 pub fn new_header(title: Option<String>, about: Option<String>) -> Self {
893 AppRequestPayload::V0(AppRequestPayloadV0::Header(DocHeader { title, about }))
894 }
895 pub fn new_discrete_update(
896 head_strings: Vec<String>,
897 crdt: String,
898 update: Vec<u8>,
899 ) -> Result<Self, NgError> {
900 let mut heads = Vec::with_capacity(head_strings.len());
901 for head in head_strings {
902 heads.push(decode_digest(&head)?);
903 }
904 let discrete = Some(DiscreteUpdate::from(crdt, update));
905 Ok(AppRequestPayload::V0(AppRequestPayloadV0::Update(
906 DocUpdate {
907 heads,
908 graph: None,
909 discrete,
910 },
911 )))
912 }
913}
914
915#[derive(Clone, Debug, Serialize, Deserialize)]
916pub enum DiscretePatch {
917 #[serde(with = "serde_bytes")]
919 YMap(Vec<u8>),
920 #[serde(with = "serde_bytes")]
921 YArray(Vec<u8>),
922 #[serde(with = "serde_bytes")]
923 YXml(Vec<u8>),
924 #[serde(with = "serde_bytes")]
925 YText(Vec<u8>),
926 #[serde(with = "serde_bytes")]
928 Automerge(Vec<u8>),
929}
930
931#[derive(Clone, Debug, Serialize, Deserialize)]
932pub struct GraphPatch {
933 #[serde(with = "serde_bytes")]
935 pub inserts: Vec<u8>,
936 #[serde(with = "serde_bytes")]
938 pub removes: Vec<u8>,
939}
940
941#[derive(Clone, Debug, Serialize, Deserialize)]
942pub enum DiscreteState {
943 #[serde(with = "serde_bytes")]
945 YMap(Vec<u8>),
946 #[serde(with = "serde_bytes")]
947 YArray(Vec<u8>),
948 #[serde(with = "serde_bytes")]
949 YXml(Vec<u8>),
950 #[serde(with = "serde_bytes")]
951 YText(Vec<u8>),
952 #[serde(with = "serde_bytes")]
954 Automerge(Vec<u8>),
955}
956
957#[derive(Clone, Debug, Serialize, Deserialize)]
958pub struct GraphState {
959 #[serde(with = "serde_bytes")]
961 pub triples: Vec<u8>,
962}
963
964#[derive(Clone, Debug, Serialize, Deserialize)]
965pub struct AppState {
966 pub heads: Vec<ObjectId>,
967 pub head_keys: Vec<ObjectKey>,
968 pub graph: Option<GraphState>, pub discrete: Option<DiscreteState>,
970 pub files: Vec<FileName>,
971}
972
973#[derive(Clone, Debug, Serialize, Deserialize)]
974pub struct AppHistory {
975 pub history: Vec<(ObjectId, CommitInfo)>,
976 pub swimlane_state: Vec<Option<ObjectId>>,
977}
978
979#[derive(Clone, Debug, Serialize, Deserialize)]
980pub struct AppHistoryJs {
981 pub history: Vec<(String, CommitInfoJs)>,
982 pub swimlane_state: Vec<Option<String>>,
983}
984
985impl AppHistory {
986 pub fn to_js(&self) -> AppHistoryJs {
987 AppHistoryJs {
988 history: Vec::from_iter(
989 self.history
990 .iter()
991 .map(|(id, info)| (id.to_string(), info.into())),
992 ),
993 swimlane_state: Vec::from_iter(
994 self.swimlane_state
995 .iter()
996 .map(|lane| lane.map_or(None, |b| Some(b.to_string()))),
997 ),
998 }
999 }
1000}
1001
1002#[derive(Clone, Debug, Serialize, Deserialize)]
1003pub enum OtherPatch {
1004 FileAdd(FileName),
1005 FileRemove(ObjectId),
1006 AsyncSignature((String, Vec<String>)),
1007 Snapshot(ObjectRef),
1008 Compact(ObjectRef),
1009 Other,
1010}
1011
1012#[derive(Clone, Debug, Serialize, Deserialize)]
1013pub struct AppPatch {
1014 pub commit_id: String,
1015 pub commit_info: CommitInfoJs,
1016 pub graph: Option<GraphPatch>,
1018 pub discrete: Option<DiscretePatch>,
1019 pub other: Option<OtherPatch>,
1020}
1021
1022#[derive(Clone, Debug, Serialize, Deserialize)]
1023pub struct FileName {
1024 pub name: Option<String>,
1025 pub reference: ObjectRef,
1026 pub nuri: String,
1027}
1028
1029#[derive(Clone, Debug, Serialize, Deserialize)]
1030pub struct FileMetaV0 {
1031 pub content_type: String,
1032 pub size: u64,
1033}
1034
1035#[derive(Clone, Debug, Serialize, Deserialize)]
1036pub struct AppTabStoreInfo {
1037 pub repo: Option<StoreRepo>, pub overlay: Option<String>, pub has_outer: Option<String>,
1040 pub store_type: Option<String>, pub readcap: Option<String>,
1042 pub is_member: Option<String>,
1043 pub inner: Option<String>,
1044 pub title: Option<String>,
1045 pub icon: Option<String>,
1046 pub description: Option<String>,
1047}
1048
1049#[derive(Clone, Debug, Serialize, Deserialize)]
1050pub struct AppTabDocInfo {
1051 pub nuri: Option<String>, pub is_store: Option<bool>, pub is_member: Option<String>, pub title: Option<String>,
1055 pub icon: Option<String>,
1056 pub description: Option<String>,
1057 pub authors: Option<Vec<String>>,
1058 pub inbox: Option<String>,
1059 pub can_edit: Option<bool>, }
1064
1065impl AppTabDocInfo {
1066 pub fn new() -> Self {
1067 AppTabDocInfo {
1068 nuri: None,
1069 is_store: None,
1070 is_member: None,
1071 title: None,
1072 icon: None,
1073 description: None,
1074 authors: None,
1075 inbox: None,
1076 can_edit: None,
1077 }
1078 }
1079}
1080
1081#[derive(Clone, Debug, Serialize, Deserialize)]
1082pub struct AppTabBranchInfo {
1083 pub id: Option<String>, pub readcap: Option<String>, pub comment_branch: Option<String>,
1086 pub class: Option<String>, }
1088
1089#[derive(Clone, Debug, Serialize, Deserialize)]
1090pub struct AppTabInfo {
1091 pub branch: Option<AppTabBranchInfo>,
1092 pub doc: Option<AppTabDocInfo>,
1093 pub store: Option<AppTabStoreInfo>,
1094}
1095
1096#[derive(Clone, Debug, Serialize, Deserialize)]
1097pub struct AppHeader {
1098 pub about: Option<String>,
1099 pub title: Option<String>,
1100 pub class: Option<String>,
1101}
1102
1103#[derive(Clone, Debug, Serialize, Deserialize)]
1104pub enum AppResponseV0 {
1105 SessionStart(AppSessionStartResponse),
1106 TabInfo(AppTabInfo),
1107 State(AppState),
1108 Patch(AppPatch),
1109 History(AppHistory),
1110 SignatureStatus(Vec<(String, Option<String>, bool)>),
1111 Text(String),
1112 FileUploading(u32),
1114 FileUploaded(ObjectRef),
1115 #[serde(with = "serde_bytes")]
1116 FileBinary(Vec<u8>),
1117 FileMeta(FileMetaV0),
1118 #[serde(with = "serde_bytes")]
1119 QueryResult(Vec<u8>), #[serde(with = "serde_bytes")]
1121 Graph(Vec<u8>), Ok,
1123 True,
1124 False,
1125 Error(String),
1126 EndOfStream,
1127 Nuri(String),
1128 Header(AppHeader),
1129 Commits(Vec<String>),
1130}
1131
1132#[derive(Clone, Debug, Serialize, Deserialize)]
1133pub enum AppResponse {
1134 V0(AppResponseV0),
1135}
1136
1137impl AppResponse {
1138 pub fn error(err: String) -> Self {
1139 AppResponse::V0(AppResponseV0::Error(err))
1140 }
1141 pub fn ok() -> Self {
1142 AppResponse::V0(AppResponseV0::Ok)
1143 }
1144 pub fn commits(commits: Vec<String>) -> Self {
1145 AppResponse::V0(AppResponseV0::Commits(commits))
1146 }
1147}