1use std::io;
4
5use serde::{Deserialize, Serialize};
6use serde_json::{Value, value::RawValue};
7
8use super::storage::Failure;
9
10mod equality;
11pub use equality::IndexDeclaration;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
14#[serde(rename_all = "snake_case")]
15pub enum CountReason {
16 Withheld,
17 CountsNotReady,
18}
19
20#[derive(Debug, Serialize)]
21pub struct KeptCount {
22 pub value: Option<u64>,
23 pub reason: Option<CountReason>,
24}
25
26impl KeptCount {
27 pub(in crate::serve) fn insert(self, answer: &mut Value, member: &str) -> Result<(), Failure> {
28 let object = answer
29 .as_object_mut()
30 .ok_or_else(|| Failure::new("answer_shape", "count answer must be an object"))?;
31 object.insert(
32 member.to_owned(),
33 serde_json::to_value(self.value).map_err(Failure::storage)?,
34 );
35 if let Some(reason) = self.reason {
36 object.insert(
37 format!("{member}_reason"),
38 serde_json::to_value(reason).map_err(Failure::storage)?,
39 );
40 }
41 Ok(())
42 }
43}
44
45#[derive(Serialize)]
46pub struct RelationTotal {
47 #[serde(flatten)]
48 pub declaration: Relation,
49 pub relationships: Option<u64>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub relationships_reason: Option<CountReason>,
52}
53
54#[derive(Debug, Serialize, Deserialize)]
55#[serde(deny_unknown_fields)]
56pub struct CountsRebuild {
57 pub branch: String,
58 pub request_id: String,
59}
60
61#[derive(Debug, Serialize, Deserialize)]
63#[serde(deny_unknown_fields)]
64pub struct EmbedRewalk {
65 pub branch: String,
66 pub collection: String,
67 pub field: String,
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
71#[serde(tag = "kind", rename_all = "snake_case")]
72pub enum CountBucket {
73 Documents { collection: String },
74 Relationships { relation: String },
75 Vectors { collection: String, field: String },
76}
77
78#[derive(Debug, Serialize)]
79#[serde(tag = "result", rename_all = "snake_case")]
80pub enum AuditVerdict {
81 Agree,
82 Differ { kept: Option<u64>, counted: u64 },
83}
84
85#[derive(Debug, Serialize)]
86pub struct AuditedBucket {
87 #[serde(flatten)]
88 pub bucket: CountBucket,
89 #[serde(flatten)]
90 pub verdict: AuditVerdict,
91}
92
93#[derive(Debug, Serialize)]
94#[serde(tag = "result", rename_all = "snake_case")]
95pub enum CountsAudit {
96 Compared { buckets: Vec<AuditedBucket> },
97 CannotTell { reason: CountReason },
98}
99
100#[derive(Clone, Copy, Debug, Serialize)]
101#[serde(rename_all = "snake_case")]
102pub enum MutationOutcome {
103 NotApplied,
104 Unknown,
105}
106
107#[derive(Clone, Debug, Serialize)]
108pub struct MutationContext {
109 pub outcome: MutationOutcome,
110 pub request_id: Value,
111 pub receipt_branch: Value,
112}
113
114impl MutationContext {
115 pub(super) fn from_operation(operation: &Operation) -> Option<Self> {
116 if let Operation::CountsRebuild(request) = operation {
117 return Some(Self {
118 outcome: MutationOutcome::Unknown,
119 request_id: Value::String(request.request_id.clone()),
120 receipt_branch: Value::String(request.branch.clone()),
121 });
122 }
123 let Operation::Mutate(request) = operation else {
124 return None;
125 };
126 let branch = match &request.change {
127 Change::Fork(fork) => &fork.name,
128 _ => &request.branch,
129 };
130 Some(Self {
131 outcome: MutationOutcome::Unknown,
132 request_id: Value::String(request.request_id.clone()),
133 receipt_branch: Value::String(branch.clone()),
134 })
135 }
136}
137
138#[derive(Clone, Debug, Serialize)]
140pub struct ResponseFailure {
141 #[serde(flatten)]
142 failure: Failure,
143 #[serde(flatten)]
144 mutation: Option<Box<MutationContext>>,
145}
146
147impl ResponseFailure {
148 pub(super) fn new(failure: Failure, mut mutation: Option<MutationContext>) -> Self {
149 if let Some(context) = &mut mutation {
150 context.outcome = if proves_not_applied(&failure.code) {
151 MutationOutcome::NotApplied
152 } else {
153 MutationOutcome::Unknown
154 };
155 }
156 Self {
157 failure,
158 mutation: mutation.map(Box::new),
159 }
160 }
161
162 pub(super) fn after_dispatch(failure: Failure, mutation: Option<MutationContext>) -> Self {
163 Self {
164 failure,
165 mutation: mutation.map(Box::new),
166 }
167 }
168
169 pub(super) fn mutation_context(&self) -> Option<MutationContext> {
170 self.mutation.as_deref().cloned()
171 }
172
173 pub(super) const fn failure(&self) -> &Failure {
174 &self.failure
175 }
176}
177
178impl std::ops::Deref for ResponseFailure {
179 type Target = Failure;
180 fn deref(&self) -> &Self::Target {
181 self.failure()
182 }
183}
184
185impl std::fmt::Display for ResponseFailure {
186 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 std::fmt::Display::fmt(&self.failure, formatter)
188 }
189}
190
191impl std::error::Error for ResponseFailure {
192 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
193 Some(&self.failure)
194 }
195}
196
197pub const NOT_APPLIED_REFUSALS: &[&str] = &[
201 "invalid_identifier",
202 "version_conflict",
203 "main_protected",
204 "discard_target_mismatch",
205 "request_conflict",
206 "branch_exists",
207 "collection_missing",
208 "document_missing",
209 "relation_missing",
210 "relation_collection",
211 "relation_cardinality",
212 "edge_properties",
213 "vector_dimension_missing",
214 "vector_dimension",
215 "vector_empty",
216 "vector_non_finite",
217 "schema_refused",
218 "json_text_invalid",
219 "import_empty",
220 "import_duplicate",
221 "invalid_document",
222 "import_limit",
223 "import_limit_required",
224 "sequence_exhausted",
225 "invalid_operation",
226 "busy",
227 "writer_unavailable",
228 "embed_admission_wait_exceeded",
229 "invalid_params",
230 "unknown_change_kind",
231 "hello_required",
232 "access_unavailable",
233 "write_refused",
234 "counts_corrupt",
235 "count_overflow",
236 "count_underflow",
237 "counts_merge_pending",
238 "counts_discard_pending",
239 "counts_scan_refused",
240 "index_field_not_top_level",
241 "index_already_declared",
242 "index_vector_field",
243 "index_declaration_corrupt",
244 "vector_field_embedded",
245 "vector_field_supplied",
246 "embed_model_change_not_available",
247 "embed_manifest_invalid",
248 "counts_access_refused",
249 "discard_access_refused",
250 "counts_branch_missing",
251 "append_prune_scope_components_invalid",
252 "append_prune_scope_component_invalid",
253 "append_prune_suffix_invalid",
254 "append_prune_document_id_invalid",
255 "append_prune_keep_zero",
256 "append_prune_keep_limit",
257 "append_prune_key_exists",
258 "append_prune_vector_field",
259 "append_prune_limit_overflow",
260 "append_prune_key_would_be_removed",
261 "append_prune_outgoing_edge",
262 "append_prune_incoming_edge",
263 "append_prune_revision_missing",
264 "append_prune_preparation_required",
265 "route_unclassified",
266 "permission_refused",
267 "held_for_approval",
268 "pass_missing",
269 "lys_could_not_be_asked",
270 "wrong_audience",
271 "wrong_issuer",
272 "pass_expired",
273 "pass_not_yet_valid",
274 "unpublished_key",
275 "signature_refused",
276 "pass_encoding_refused",
277 "contract_refused",
278 "rights_outside_audience",
279 "rights_truncated",
280 "unknown_method",
281 "discard_pending",
282 "branch_missing",
283 "edge_key",
284];
285
286fn proves_not_applied(code: &str) -> bool {
287 NOT_APPLIED_REFUSALS.contains(&code)
288}
289
290macro_rules! tagged_vocabulary {
292 (
293 $(#[$enum_doc:meta])*
294 $name:ident ($tag:literal, $content:literal, $known:ident) {
295 $( $(#[$doc:meta])* $variant:ident $(($parameter:ty))? => $wire:literal, )*
296 }
297 ) => {
298 $(#[$enum_doc])*
299 #[derive(Debug, Serialize, Deserialize)]
300 #[serde(tag = $tag, content = $content, deny_unknown_fields)]
301 pub enum $name {
302 $( $(#[$doc])* #[serde(rename = $wire)] $variant $(($parameter))?, )*
303 }
304
305 impl $name {
306 pub const WIRE_NAMES: &'static [&'static str] = &[$($wire),*];
309
310 fn $known(name: &str) -> bool {
311 matches!(name, $($wire)|*)
312 }
313 }
314 };
315}
316
317pub const PROTOCOL: &str = "haem/1";
319pub const RECEIPT_CAPABILITY: &str = "typed_receipt_fragments";
320
321#[derive(Debug, Serialize, Deserialize)]
323#[serde(deny_unknown_fields)]
324pub struct Request {
325 pub id: String,
326 pub operation: Operation,
327}
328
329tagged_vocabulary! {
330 Operation ("method", "params", known_method) {
332 Hello(HelloAgreement) => "hello",
333 Present(PassPresent) => "session.pass",
337 DraftsRun(DraftsRun) => "drafts.run",
342 Branches(BranchPage) => "branches.list",
343 Collections(CollectionPage) => "collections.list",
344 Get(DocumentRead) => "documents.get",
345 List(DocumentPage) => "documents.list",
346 History(HistoryPage) => "history.list",
347 Follow(HeadFollow) => "head.follow",
353 Review(ReviewPage) => "branch.review",
354 ReviewCounts(ReviewPage) => "branch.review_counts",
356 MergePrepare(ReviewPage) => "branch.merge_prepare",
358 MergeDangling(ReviewPage) => "branch.merge_dangling",
360 Mutate(Mutation) => "mutation.apply",
361 Receipt(ReceiptQuery) => "mutation.receipt",
362 CountsRebuild(CountsRebuild) => "counts.rebuild",
364 CountsAudit(Selection) => "counts.audit",
366 EdgesOut(EdgePage) => "edge.out",
368 EdgesIn(EdgePage) => "edge.in",
370 GraphExport(GraphPage) => "graph.export",
372 VectorGet(VectorRead) => "vectors.get",
374 VectorList(VectorPage) => "vectors.list",
376 VectorSearch(VectorSearch) => "vectors.search",
378 VectorSearchText(VectorSearchText) => "vectors.search_text",
381 StoreStats(StoreRead) => "store.stats",
382 VectorFields(StoreRead) => "vectors.fields",
383 EmbedRewalk(EmbedRewalk) => "embed.rewalk_longer",
387 Relations(RelationsRead) => "relations.list",
389 QueryRun(QueryRequest) => "query.run",
393 QueryCheck(crate::serve::query::check::CheckRequest) => "query.check",
394 QueryWords(crate::serve::query::check::WordsRequest) => "query.words",
395 ChangesList(crate::serve::changes::ChangesRequest) => "changes.list",
398 }
399}
400
401#[derive(Debug, Serialize, Deserialize)]
403#[serde(deny_unknown_fields)]
404pub struct PassPresent {
405 pub pass: String,
406}
407
408#[derive(Debug, Serialize, Deserialize)]
410#[serde(deny_unknown_fields)]
411pub struct DraftsRun {
412 #[serde(default)]
413 pub after: Option<String>,
414}
415
416#[derive(Debug, Serialize, Deserialize)]
418#[serde(deny_unknown_fields)]
419pub struct QueryRequest {
420 pub selection: Selection,
421 pub query: String,
422 #[serde(default)]
424 pub parameters: std::collections::BTreeMap<String, Box<RawValue>>,
425 pub page: QueryPage,
426 #[serde(default)]
427 pub continuation: Option<String>,
428 #[serde(default)]
430 pub rows: crate::serve::query::Rows,
431}
432
433#[derive(Debug, Serialize, Deserialize)]
435#[serde(deny_unknown_fields)]
436pub struct QueryPage {
437 pub rows: std::num::NonZeroU64,
438 pub bytes: std::num::NonZeroU64,
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
443#[serde(deny_unknown_fields)]
444pub struct Version {
445 #[serde(with = "decimal")]
446 pub generation: u64,
447 #[serde(with = "decimal")]
448 pub sequence: u64,
449}
450
451#[derive(Debug, Serialize, Deserialize)]
453#[serde(deny_unknown_fields)]
454pub struct Selection {
455 pub branch: String,
456 pub version: Option<Version>,
457}
458
459#[derive(Debug, Serialize, Deserialize)]
460#[serde(deny_unknown_fields)]
461pub struct BranchPage {
462 pub after: Option<String>,
463 pub limit: std::num::NonZeroU32,
464}
465
466#[derive(Debug, Serialize, Deserialize)]
467#[serde(deny_unknown_fields)]
468pub struct CollectionPage {
469 pub selection: Selection,
470 pub after: Option<String>,
471 pub limit: std::num::NonZeroU32,
472}
473
474#[derive(Debug, Serialize, Deserialize)]
475#[serde(deny_unknown_fields)]
476pub struct DocumentRead {
477 pub selection: Selection,
478 pub collection: String,
479 pub document_id: String,
480}
481
482#[derive(Debug, Serialize, Deserialize)]
484#[serde(deny_unknown_fields)]
485pub struct DocumentPage {
486 pub selection: Selection,
487 pub collection: String,
488 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub prefix: Option<String>,
490 #[serde(default, skip_serializing_if = "Option::is_none")]
491 pub content: Option<String>,
492 pub after: Option<String>,
493 pub limit: std::num::NonZeroU32,
494}
495
496#[derive(Debug, Serialize, Deserialize)]
497#[serde(deny_unknown_fields)]
498pub struct HistoryPage {
499 pub branch: String,
500 pub after: Option<Version>,
501 pub limit: std::num::NonZeroU32,
502}
503
504#[derive(Debug, Serialize, Deserialize)]
506#[serde(deny_unknown_fields)]
507pub struct HeadFollow {
508 pub branch: String,
509}
510
511#[derive(Debug, Serialize, Deserialize)]
513#[serde(deny_unknown_fields)]
514pub struct ReviewPage {
515 pub branch: String,
516 pub after: Option<String>,
517 pub limit: std::num::NonZeroU32,
518}
519
520#[derive(Debug, Serialize, Deserialize)]
525#[serde(deny_unknown_fields)]
526pub struct ReceiptQuery {
527 pub branch: String,
528 pub request_id: String,
529}
530
531#[derive(Debug, Serialize, Deserialize)]
533#[serde(deny_unknown_fields)]
534pub struct Mutation {
535 pub request_id: String,
536 pub branch: String,
537 pub expected: Version,
538 pub change: Change,
539 #[serde(default, skip_serializing_if = "Option::is_none")]
543 pub stage: Option<Box<Stage>>,
544}
545
546#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
549#[serde(deny_unknown_fields)]
550pub struct Stage {
551 pub line: String,
552 pub run: String,
553 pub stage: String,
554 pub subject: String,
555 pub inputs: Vec<StageInput>,
558}
559
560#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
562#[serde(deny_unknown_fields)]
563pub struct StageInput {
564 pub branch: String,
565 pub version: Version,
566}
567
568#[derive(Debug, Serialize, Deserialize)]
569#[serde(deny_unknown_fields)]
570pub struct DocumentAppendPrune {
571 pub collection: String,
572 pub scope: Vec<String>,
573 pub suffix: String,
574 pub document: Box<RawValue>,
575 pub keep: u32,
576}
577
578#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
579#[serde(deny_unknown_fields)]
580pub struct RetentionResult {
581 pub document_id: String,
582 pub removed: u32,
583 pub more: bool,
584}
585
586tagged_vocabulary! {
587 Change ("kind", "value", known_kind) {
593 Collection(CollectionWrite) => "collection.put",
594 Put(DocumentWrite) => "document.put",
595 AppendPrune(DocumentAppendPrune) => "document.append_prune",
596 Delete(DocumentDelete) => "document.delete",
597 DeleteMany(DocumentDeleteMany) => "documents.delete_many",
598 Fork(Fork) => "branch.fork",
599 Discard(BranchDiscard) => "branch.discard",
600 Merge(BranchMerge) => "branch.merge",
603 EdgePut(Edge) => "edge.put",
604 EdgeDelete(EdgeDelete) => "edge.delete",
605 IndexDeclare(IndexDeclaration) => "index.declare",
606 VectorDeclare(VectorDeclaration) => "vector.declare",
607 VectorPut(VectorWrite) => "vector.put",
608 VectorPutMany(VectorImport) => "vectors.put_many",
609 VectorDelete(VectorDelete) => "vector.delete",
610 PutMany(DocumentImport) => "documents.put_many",
611 EdgePutMany(EdgeImport) => "edges.put_many",
614 RelationPut(Relation) => "relation.put",
615 RelationDelete(RelationDelete) => "relation.delete",
616 }
617}
618
619pub(super) enum DecodedRequest {
620 Request(Request),
621 Refused {
622 id: String,
623 failure: ResponseFailure,
624 },
625}
626
627#[derive(Deserialize)]
628struct Correlation {
629 id: String,
630}
631
632#[derive(Deserialize)]
633struct Envelope<'a> {
634 #[serde(borrow)]
635 operation: Option<&'a RawValue>,
636}
637
638#[derive(Deserialize)]
639struct OperationProbe<'a> {
640 method: Option<String>,
641 #[serde(borrow)]
642 params: Option<&'a RawValue>,
643}
644
645#[derive(Deserialize)]
646struct MutationProbe<'a> {
647 request_id: Option<Value>,
648 branch: Option<Value>,
649 #[serde(borrow)]
650 change: Option<&'a RawValue>,
651}
652
653#[derive(Deserialize)]
654struct ChangeProbe {
655 kind: Option<String>,
656}
657
658#[derive(Deserialize)]
659struct ReceiptBranchProbe<'a> {
660 kind: Option<String>,
661 #[serde(borrow)]
662 value: Option<&'a RawValue>,
663}
664
665#[derive(Deserialize)]
666struct ForkNameProbe {
667 name: Option<Value>,
668}
669
670pub(super) fn decode_request(bytes: &[u8]) -> io::Result<DecodedRequest> {
671 if let Ok(request) = serde_json::from_slice::<Request>(bytes) {
672 validate_id(&request.id)?;
673 return Ok(DecodedRequest::Request(request));
674 }
675 let correlation: Correlation = serde_json::from_slice(bytes)
676 .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
677 validate_id(&correlation.id)?;
678 let failure = serde_json::from_slice::<Envelope<'_>>(bytes).map_or_else(
679 |_| invalid_params(),
680 |envelope| classify_refusal(envelope.operation),
681 );
682 Ok(DecodedRequest::Refused {
683 id: correlation.id,
684 failure: ResponseFailure::new(failure, malformed_mutation_context(bytes)),
685 })
686}
687
688fn malformed_mutation_context(bytes: &[u8]) -> Option<MutationContext> {
689 let envelope = serde_json::from_slice::<Envelope<'_>>(bytes).ok()?;
690 let operation = serde_json::from_str::<OperationProbe<'_>>(envelope.operation?.get()).ok()?;
691 let rebuild = operation.method.as_deref() == Some("counts.rebuild");
692 if !rebuild && operation.method.as_deref() != Some("mutation.apply") {
693 return None;
694 }
695 let mutation = operation
696 .params
697 .and_then(|params| serde_json::from_str::<MutationProbe<'_>>(params.get()).ok());
698 let (request_id, mut receipt_branch, change) = match mutation {
699 Some(mutation) => (
700 mutation.request_id.unwrap_or(Value::Null),
701 mutation.branch.unwrap_or(Value::Null),
702 mutation.change,
703 ),
704 None => (Value::Null, Value::Null, None),
705 };
706 if !rebuild
707 && let Some(change) = change
708 && let Ok(change) = serde_json::from_str::<ReceiptBranchProbe<'_>>(change.get())
709 && change.kind.as_deref() == Some("branch.fork")
710 {
711 receipt_branch = change
712 .value
713 .and_then(|value| serde_json::from_str::<ForkNameProbe>(value.get()).ok())
714 .and_then(|fork| fork.name)
715 .unwrap_or(Value::Null);
716 }
717 Some(MutationContext {
718 outcome: MutationOutcome::NotApplied,
719 request_id,
720 receipt_branch,
721 })
722}
723
724fn validate_id(id: &str) -> io::Result<()> {
725 if id.is_empty() || id.len() > 512 {
726 return Err(io::Error::new(
727 io::ErrorKind::InvalidData,
728 "invalid correlation ID",
729 ));
730 }
731 Ok(())
732}
733
734fn classify_refusal(operation: Option<&RawValue>) -> Failure {
735 let Some(operation) = operation else {
736 return invalid_params();
737 };
738 let Ok(operation) = serde_json::from_str::<OperationProbe<'_>>(operation.get()) else {
739 return invalid_params();
740 };
741 let Some(method) = operation.method else {
742 return invalid_params();
743 };
744 if !Operation::known_method(&method) {
745 return Failure::new("unknown_method", "method is not in the served vocabulary");
746 }
747 if method == "hello" {
748 return Failure::new(
749 "typed_receipt_capability_missing",
750 format!("hello must agree {RECEIPT_CAPABILITY} and {PROTOCOL}"),
751 );
752 }
753 if method == "mutation.apply"
754 && let Some(params) = operation.params
755 && let Ok(mutation) = serde_json::from_str::<MutationProbe<'_>>(params.get())
756 && let Some(change) = mutation.change
757 && let Ok(change) = serde_json::from_str::<ChangeProbe>(change.get())
758 && let Some(kind) = change.kind
759 && !Change::known_kind(&kind)
760 {
761 return Failure::new(
762 "unknown_change_kind",
763 "change kind is not in the served vocabulary",
764 );
765 }
766 invalid_params()
767}
768
769fn invalid_params() -> Failure {
770 Failure::new(
771 "invalid_params",
772 "request does not match the operation shape",
773 )
774}
775
776#[derive(Debug, Serialize, Deserialize)]
777#[serde(deny_unknown_fields)]
778pub struct BranchDiscard {
779 pub name: String,
780}
781
782#[derive(Debug, Serialize, Deserialize)]
783#[serde(deny_unknown_fields)]
784pub struct BranchMerge {
785 pub from: String,
787 pub reviewed: Version,
789}
790
791#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
793#[serde(deny_unknown_fields)]
794pub struct Relation {
795 pub relation: String,
796 pub from_collection: String,
797 pub to_collection: String,
798 pub cardinality: Cardinality,
799}
800
801#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
802#[serde(rename_all = "snake_case")]
803pub enum Cardinality {
804 OneToOne,
805 OneToMany,
806 ManyToMany,
807}
808
809#[derive(Debug, Serialize, Deserialize)]
810#[serde(deny_unknown_fields)]
811pub struct RelationDelete {
812 pub relation: String,
813}
814
815#[derive(Debug, Serialize, Deserialize)]
816#[serde(deny_unknown_fields)]
817pub struct RelationsRead {
818 pub selection: Selection,
819}
820
821#[derive(Debug, Serialize, Deserialize)]
822#[serde(deny_unknown_fields)]
823pub struct CollectionWrite {
824 pub collection: String,
825 #[serde(deserialize_with = "explicit_schema")]
827 pub schema: Option<Value>,
828}
829
830#[derive(Debug, Serialize, Deserialize)]
831#[serde(deny_unknown_fields)]
832pub struct DocumentWrite {
833 pub collection: String,
834 pub document_id: String,
835 pub document: Box<RawValue>,
837}
838
839#[derive(Debug, Serialize, Deserialize)]
841#[serde(deny_unknown_fields)]
842pub struct DocumentImport {
843 pub collection: String,
844 pub documents: Vec<ImportedDocument>,
845}
846
847#[derive(Debug, Serialize, Deserialize)]
848#[serde(deny_unknown_fields)]
849pub struct ImportedDocument {
850 pub document_id: String,
851 pub document: Box<RawValue>,
852}
853
854#[derive(Debug, Serialize, Deserialize)]
855#[serde(deny_unknown_fields)]
856pub struct StoreRead {
857 pub selection: Selection,
858}
859
860#[derive(Debug, Serialize, Deserialize)]
861#[serde(deny_unknown_fields)]
862pub struct DocumentDelete {
863 pub collection: String,
864 pub document_id: String,
865}
866
867#[derive(Debug, Serialize, Deserialize)]
869#[serde(deny_unknown_fields)]
870pub struct DocumentDeleteMany {
871 pub collection: String,
872 pub document_ids: Vec<String>,
873}
874
875#[derive(Debug, Serialize, Deserialize)]
876#[serde(deny_unknown_fields)]
877pub struct Fork {
878 pub name: String,
879 pub from: Version,
881 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
885 pub line: bool,
886}
887
888#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
890#[serde(deny_unknown_fields)]
891pub struct Node {
892 pub collection: String,
893 pub document_id: String,
894}
895
896#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
899#[serde(deny_unknown_fields)]
900pub struct Edge {
901 pub relation: String,
902 pub from: Node,
903 pub to: Node,
904 pub properties: Value,
906}
907
908#[derive(Debug, Serialize, Deserialize)]
911#[serde(deny_unknown_fields)]
912pub struct EdgeImport {
913 pub edges: Vec<Edge>,
914}
915
916#[derive(Debug, Serialize, Deserialize)]
917#[serde(deny_unknown_fields)]
918pub struct EdgeDelete {
919 pub relation: String,
920 pub from: Node,
921 pub to: Node,
922}
923
924#[derive(Debug, Serialize, Deserialize)]
928#[serde(deny_unknown_fields)]
929pub struct EdgePage {
930 pub selection: Selection,
931 pub node: Node,
932 pub relation: Option<String>,
933 pub after: Option<String>,
934 pub limit: std::num::NonZeroU32,
935}
936
937#[derive(Debug, Serialize, Deserialize)]
939#[serde(deny_unknown_fields)]
940pub struct GraphPage {
941 pub selection: Selection,
942 pub after: Option<String>,
943 pub limit: std::num::NonZeroU32,
944 #[serde(default, skip_serializing_if = "Option::is_none")]
947 pub relations: Option<Vec<String>>,
948 #[serde(default, skip_serializing_if = "Option::is_none")]
950 pub collections: Option<Vec<String>>,
951 #[serde(default, skip_serializing_if = "Option::is_none")]
954 pub members: Option<std::collections::BTreeMap<String, Vec<String>>>,
955 #[serde(default, skip_serializing_if = "Option::is_none")]
959 pub records: Option<Vec<Node>>,
960 #[serde(default, skip_serializing_if = "Option::is_none")]
964 pub unrelated: Option<Unrelated>,
965}
966
967#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
969#[serde(deny_unknown_fields)]
970pub struct Unrelated {
971 pub per_collection: std::num::NonZeroU64,
973 pub most: std::num::NonZeroU64,
975}
976
977#[derive(Debug, Serialize, Deserialize)]
979#[serde(deny_unknown_fields)]
980pub struct VectorDeclaration {
981 pub collection: String,
982 pub field: String,
983 pub declaration: super::storage::embedded::Declaration,
984}
985
986#[derive(Debug, Serialize, Deserialize)]
992#[serde(deny_unknown_fields)]
993pub struct VectorWrite {
994 pub collection: String,
995 pub document_id: String,
996 pub field: String,
997 pub vector: Vec<f32>,
998}
999
1000#[derive(Debug, Serialize, Deserialize)]
1003#[serde(deny_unknown_fields)]
1004pub struct VectorImport {
1005 pub vectors: Vec<VectorWrite>,
1006}
1007
1008#[derive(Debug, Serialize, Deserialize)]
1009#[serde(deny_unknown_fields)]
1010pub struct VectorDelete {
1011 pub collection: String,
1012 pub document_id: String,
1013 pub field: String,
1014}
1015
1016#[derive(Debug, Serialize, Deserialize)]
1017#[serde(deny_unknown_fields)]
1018pub struct VectorRead {
1019 pub selection: Selection,
1020 pub collection: String,
1021 pub document_id: String,
1022 pub field: String,
1023}
1024
1025#[derive(Debug, Serialize, Deserialize)]
1028#[serde(deny_unknown_fields)]
1029pub struct VectorPage {
1030 pub selection: Selection,
1031 pub collection: String,
1032 pub field: String,
1033 pub after: Option<String>,
1034 pub limit: std::num::NonZeroU32,
1035}
1036
1037#[derive(Debug, Serialize, Deserialize)]
1040#[serde(deny_unknown_fields)]
1041pub struct VectorSearch {
1042 pub selection: Selection,
1043 pub collection: String,
1044 pub field: String,
1045 pub query: Vec<f32>,
1046 pub k: std::num::NonZeroU32,
1047 pub metric: Metric,
1048}
1049
1050#[derive(Debug, Serialize, Deserialize)]
1054#[serde(deny_unknown_fields)]
1055pub struct VectorSearchText {
1056 pub selection: Selection,
1057 pub collection: String,
1058 pub field: String,
1059 pub text: String,
1060 pub k: std::num::NonZeroU32,
1061}
1062
1063#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1066#[serde(rename_all = "lowercase")]
1067pub enum Metric {
1068 Cosine,
1069 L2,
1070 Dot,
1071}
1072
1073mod decimal {
1074 use serde::{Deserialize, Deserializer, Serializer, de::Error};
1075
1076 pub fn serialize<S: Serializer>(
1077 value: impl std::fmt::Display,
1078 serializer: S,
1079 ) -> Result<S::Ok, S::Error> {
1080 serializer.serialize_str(&value.to_string())
1081 }
1082
1083 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
1084 let text = String::deserialize(deserializer)?;
1085 let value = text.parse::<u64>().map_err(D::Error::custom)?;
1086 if text != value.to_string() {
1087 return Err(D::Error::custom(
1088 "expected a canonical unsigned decimal string",
1089 ));
1090 }
1091 Ok(value)
1092 }
1093}
1094
1095fn explicit_schema<'de, D: serde::Deserializer<'de>>(
1097 deserializer: D,
1098) -> Result<Option<Value>, D::Error> {
1099 Option::<Value>::deserialize(deserializer)
1100}
1101
1102#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1103pub enum WireOwnerKind {
1104 Database,
1105 Api,
1106 LeafLog,
1107 Observer,
1108 Branch,
1109 BranchCommit,
1110 BranchRef,
1111}
1112
1113#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1114#[serde(deny_unknown_fields)]
1115pub struct WireOwnedCause {
1116 pub kind: WireOwnerKind,
1117 pub message: String,
1118 pub database: Option<super::receipt_wire::WireCause>,
1119}
1120
1121#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1122#[serde(deny_unknown_fields)]
1123pub struct WireRefusal {
1124 pub code: String,
1125 pub message: String,
1126 pub path: Option<String>,
1127 pub cause: Option<WireOwnedCause>,
1128}
1129
1130#[derive(Debug, Serialize, Deserialize)]
1131#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1132pub enum WirePacket {
1133 Receipt {
1134 receipt: Box<super::receipt_wire::WireReceipt>,
1135 refusal: Option<WireRefusal>,
1136 },
1137 Refusal {
1138 refusal: WireRefusal,
1139 },
1140}
1141impl WirePacket {
1142 #[must_use]
1143 pub fn receipt(&self) -> Option<&super::receipt_wire::WireReceipt> {
1144 match self {
1145 Self::Receipt { receipt, .. } => Some(receipt.as_ref()),
1146 Self::Refusal { .. } => None,
1147 }
1148 }
1149 #[must_use]
1150 pub const fn refusal(&self) -> Option<&WireRefusal> {
1151 match self {
1152 Self::Receipt { refusal, .. } => refusal.as_ref(),
1153 Self::Refusal { refusal } => Some(refusal),
1154 }
1155 }
1156}
1157
1158#[derive(Clone, Debug, Serialize, Deserialize)]
1159#[serde(deny_unknown_fields)]
1160pub struct HelloAgreement {
1161 pub protocol: String,
1162 pub capabilities: Vec<String>,
1163 pub max_frame: std::num::NonZeroU32,
1164}
1165impl HelloAgreement {
1166 pub(crate) fn client() -> Self {
1167 Self {
1168 protocol: PROTOCOL.to_owned(),
1169 capabilities: vec![RECEIPT_CAPABILITY.to_owned()],
1170 max_frame: std::num::NonZeroU32::MAX,
1171 }
1172 }
1173 pub(crate) fn validate(
1174 &self,
1175 server_cap: std::num::NonZeroU32,
1176 ) -> Result<std::num::NonZeroU32, Failure> {
1177 if self.protocol != PROTOCOL {
1178 return Err(Failure::new(
1179 "receipt_protocol_mismatch",
1180 format!("expected {PROTOCOL}, received {}", self.protocol),
1181 ));
1182 }
1183 if !self
1184 .capabilities
1185 .iter()
1186 .any(|capability| capability == RECEIPT_CAPABILITY)
1187 {
1188 return Err(Failure::new(
1189 "typed_receipt_capability_missing",
1190 format!("hello must agree {RECEIPT_CAPABILITY}"),
1191 ));
1192 }
1193 let cap = self.max_frame.min(server_cap);
1194 let minimum = super::receipt_fragments::minimum_cap().map_err(Failure::storage)?;
1195 if u64::from(cap.get()) < u64::try_from(minimum).map_err(Failure::storage)? {
1196 return Err(Failure::new(
1197 "receipt_frame_too_small",
1198 format!("offered {}, minimum {minimum}", cap.get()),
1199 ));
1200 }
1201 Ok(cap)
1202 }
1203}
1204
1205#[cfg(test)]
1206mod receipt_agreement_tests {
1207 use super::*;
1208
1209 type TestResult = Result<(), Box<dyn std::error::Error>>;
1210
1211 #[test]
1212 fn missing_capability_and_derived_floor_are_named_before_dispatch() -> TestResult {
1213 let mut agreement = HelloAgreement::client();
1214 agreement.capabilities.clear();
1215 let cap = std::num::NonZeroU32::MAX;
1216 let Err(missing) = agreement.validate(cap) else {
1217 return Err("a hello without the receipt capability was agreed".into());
1218 };
1219 assert_eq!(missing.code, "typed_receipt_capability_missing");
1220 agreement.capabilities.push(RECEIPT_CAPABILITY.to_owned());
1221 let minimum = super::super::receipt_fragments::minimum_cap()?;
1222 let below = u32::try_from(minimum - 1)?;
1223 agreement.max_frame = std::num::NonZeroU32::new(below).ok_or("floor is one")?;
1224 let Err(error) = agreement.validate(cap) else {
1225 return Err("a frame below the receipt floor was agreed".into());
1226 };
1227 assert_eq!(error.code, "receipt_frame_too_small");
1228 assert!(error.message.contains(&minimum.to_string()));
1229 assert!(error.message.contains(&agreement.max_frame.to_string()));
1230 Ok(())
1231 }
1232}