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}
540
541#[derive(Debug, Serialize, Deserialize)]
542#[serde(deny_unknown_fields)]
543pub struct DocumentAppendPrune {
544 pub collection: String,
545 pub scope: Vec<String>,
546 pub suffix: String,
547 pub document: Box<RawValue>,
548 pub keep: u32,
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
552#[serde(deny_unknown_fields)]
553pub struct RetentionResult {
554 pub document_id: String,
555 pub removed: u32,
556 pub more: bool,
557}
558
559tagged_vocabulary! {
560 Change ("kind", "value", known_kind) {
566 Collection(CollectionWrite) => "collection.put",
567 Put(DocumentWrite) => "document.put",
568 AppendPrune(DocumentAppendPrune) => "document.append_prune",
569 Delete(DocumentDelete) => "document.delete",
570 DeleteMany(DocumentDeleteMany) => "documents.delete_many",
571 Fork(Fork) => "branch.fork",
572 Discard(BranchDiscard) => "branch.discard",
573 Merge(BranchMerge) => "branch.merge",
576 EdgePut(Edge) => "edge.put",
577 EdgeDelete(EdgeDelete) => "edge.delete",
578 IndexDeclare(IndexDeclaration) => "index.declare",
579 VectorDeclare(VectorDeclaration) => "vector.declare",
580 VectorPut(VectorWrite) => "vector.put",
581 VectorPutMany(VectorImport) => "vectors.put_many",
582 VectorDelete(VectorDelete) => "vector.delete",
583 PutMany(DocumentImport) => "documents.put_many",
584 EdgePutMany(EdgeImport) => "edges.put_many",
587 RelationPut(Relation) => "relation.put",
588 RelationDelete(RelationDelete) => "relation.delete",
589 }
590}
591
592pub(super) enum DecodedRequest {
593 Request(Request),
594 Refused {
595 id: String,
596 failure: ResponseFailure,
597 },
598}
599
600#[derive(Deserialize)]
601struct Correlation {
602 id: String,
603}
604
605#[derive(Deserialize)]
606struct Envelope<'a> {
607 #[serde(borrow)]
608 operation: Option<&'a RawValue>,
609}
610
611#[derive(Deserialize)]
612struct OperationProbe<'a> {
613 method: Option<String>,
614 #[serde(borrow)]
615 params: Option<&'a RawValue>,
616}
617
618#[derive(Deserialize)]
619struct MutationProbe<'a> {
620 request_id: Option<Value>,
621 branch: Option<Value>,
622 #[serde(borrow)]
623 change: Option<&'a RawValue>,
624}
625
626#[derive(Deserialize)]
627struct ChangeProbe {
628 kind: Option<String>,
629}
630
631#[derive(Deserialize)]
632struct ReceiptBranchProbe<'a> {
633 kind: Option<String>,
634 #[serde(borrow)]
635 value: Option<&'a RawValue>,
636}
637
638#[derive(Deserialize)]
639struct ForkNameProbe {
640 name: Option<Value>,
641}
642
643pub(super) fn decode_request(bytes: &[u8]) -> io::Result<DecodedRequest> {
644 if let Ok(request) = serde_json::from_slice::<Request>(bytes) {
645 validate_id(&request.id)?;
646 return Ok(DecodedRequest::Request(request));
647 }
648 let correlation: Correlation = serde_json::from_slice(bytes)
649 .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
650 validate_id(&correlation.id)?;
651 let failure = serde_json::from_slice::<Envelope<'_>>(bytes).map_or_else(
652 |_| invalid_params(),
653 |envelope| classify_refusal(envelope.operation),
654 );
655 Ok(DecodedRequest::Refused {
656 id: correlation.id,
657 failure: ResponseFailure::new(failure, malformed_mutation_context(bytes)),
658 })
659}
660
661fn malformed_mutation_context(bytes: &[u8]) -> Option<MutationContext> {
662 let envelope = serde_json::from_slice::<Envelope<'_>>(bytes).ok()?;
663 let operation = serde_json::from_str::<OperationProbe<'_>>(envelope.operation?.get()).ok()?;
664 let rebuild = operation.method.as_deref() == Some("counts.rebuild");
665 if !rebuild && operation.method.as_deref() != Some("mutation.apply") {
666 return None;
667 }
668 let mutation = operation
669 .params
670 .and_then(|params| serde_json::from_str::<MutationProbe<'_>>(params.get()).ok());
671 let (request_id, mut receipt_branch, change) = match mutation {
672 Some(mutation) => (
673 mutation.request_id.unwrap_or(Value::Null),
674 mutation.branch.unwrap_or(Value::Null),
675 mutation.change,
676 ),
677 None => (Value::Null, Value::Null, None),
678 };
679 if !rebuild
680 && let Some(change) = change
681 && let Ok(change) = serde_json::from_str::<ReceiptBranchProbe<'_>>(change.get())
682 && change.kind.as_deref() == Some("branch.fork")
683 {
684 receipt_branch = change
685 .value
686 .and_then(|value| serde_json::from_str::<ForkNameProbe>(value.get()).ok())
687 .and_then(|fork| fork.name)
688 .unwrap_or(Value::Null);
689 }
690 Some(MutationContext {
691 outcome: MutationOutcome::NotApplied,
692 request_id,
693 receipt_branch,
694 })
695}
696
697fn validate_id(id: &str) -> io::Result<()> {
698 if id.is_empty() || id.len() > 512 {
699 return Err(io::Error::new(
700 io::ErrorKind::InvalidData,
701 "invalid correlation ID",
702 ));
703 }
704 Ok(())
705}
706
707fn classify_refusal(operation: Option<&RawValue>) -> Failure {
708 let Some(operation) = operation else {
709 return invalid_params();
710 };
711 let Ok(operation) = serde_json::from_str::<OperationProbe<'_>>(operation.get()) else {
712 return invalid_params();
713 };
714 let Some(method) = operation.method else {
715 return invalid_params();
716 };
717 if !Operation::known_method(&method) {
718 return Failure::new("unknown_method", "method is not in the served vocabulary");
719 }
720 if method == "hello" {
721 return Failure::new(
722 "typed_receipt_capability_missing",
723 format!("hello must agree {RECEIPT_CAPABILITY} and {PROTOCOL}"),
724 );
725 }
726 if method == "mutation.apply"
727 && let Some(params) = operation.params
728 && let Ok(mutation) = serde_json::from_str::<MutationProbe<'_>>(params.get())
729 && let Some(change) = mutation.change
730 && let Ok(change) = serde_json::from_str::<ChangeProbe>(change.get())
731 && let Some(kind) = change.kind
732 && !Change::known_kind(&kind)
733 {
734 return Failure::new(
735 "unknown_change_kind",
736 "change kind is not in the served vocabulary",
737 );
738 }
739 invalid_params()
740}
741
742fn invalid_params() -> Failure {
743 Failure::new(
744 "invalid_params",
745 "request does not match the operation shape",
746 )
747}
748
749#[derive(Debug, Serialize, Deserialize)]
750#[serde(deny_unknown_fields)]
751pub struct BranchDiscard {
752 pub name: String,
753}
754
755#[derive(Debug, Serialize, Deserialize)]
756#[serde(deny_unknown_fields)]
757pub struct BranchMerge {
758 pub from: String,
760 pub reviewed: Version,
762}
763
764#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
766#[serde(deny_unknown_fields)]
767pub struct Relation {
768 pub relation: String,
769 pub from_collection: String,
770 pub to_collection: String,
771 pub cardinality: Cardinality,
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
775#[serde(rename_all = "snake_case")]
776pub enum Cardinality {
777 OneToOne,
778 OneToMany,
779 ManyToMany,
780}
781
782#[derive(Debug, Serialize, Deserialize)]
783#[serde(deny_unknown_fields)]
784pub struct RelationDelete {
785 pub relation: String,
786}
787
788#[derive(Debug, Serialize, Deserialize)]
789#[serde(deny_unknown_fields)]
790pub struct RelationsRead {
791 pub selection: Selection,
792}
793
794#[derive(Debug, Serialize, Deserialize)]
795#[serde(deny_unknown_fields)]
796pub struct CollectionWrite {
797 pub collection: String,
798 #[serde(deserialize_with = "explicit_schema")]
800 pub schema: Option<Value>,
801}
802
803#[derive(Debug, Serialize, Deserialize)]
804#[serde(deny_unknown_fields)]
805pub struct DocumentWrite {
806 pub collection: String,
807 pub document_id: String,
808 pub document: Box<RawValue>,
810}
811
812#[derive(Debug, Serialize, Deserialize)]
814#[serde(deny_unknown_fields)]
815pub struct DocumentImport {
816 pub collection: String,
817 pub documents: Vec<ImportedDocument>,
818}
819
820#[derive(Debug, Serialize, Deserialize)]
821#[serde(deny_unknown_fields)]
822pub struct ImportedDocument {
823 pub document_id: String,
824 pub document: Box<RawValue>,
825}
826
827#[derive(Debug, Serialize, Deserialize)]
828#[serde(deny_unknown_fields)]
829pub struct StoreRead {
830 pub selection: Selection,
831}
832
833#[derive(Debug, Serialize, Deserialize)]
834#[serde(deny_unknown_fields)]
835pub struct DocumentDelete {
836 pub collection: String,
837 pub document_id: String,
838}
839
840#[derive(Debug, Serialize, Deserialize)]
842#[serde(deny_unknown_fields)]
843pub struct DocumentDeleteMany {
844 pub collection: String,
845 pub document_ids: Vec<String>,
846}
847
848#[derive(Debug, Serialize, Deserialize)]
849#[serde(deny_unknown_fields)]
850pub struct Fork {
851 pub name: String,
852 pub from: Version,
854}
855
856#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
858#[serde(deny_unknown_fields)]
859pub struct Node {
860 pub collection: String,
861 pub document_id: String,
862}
863
864#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
867#[serde(deny_unknown_fields)]
868pub struct Edge {
869 pub relation: String,
870 pub from: Node,
871 pub to: Node,
872 pub properties: Value,
874}
875
876#[derive(Debug, Serialize, Deserialize)]
879#[serde(deny_unknown_fields)]
880pub struct EdgeImport {
881 pub edges: Vec<Edge>,
882}
883
884#[derive(Debug, Serialize, Deserialize)]
885#[serde(deny_unknown_fields)]
886pub struct EdgeDelete {
887 pub relation: String,
888 pub from: Node,
889 pub to: Node,
890}
891
892#[derive(Debug, Serialize, Deserialize)]
896#[serde(deny_unknown_fields)]
897pub struct EdgePage {
898 pub selection: Selection,
899 pub node: Node,
900 pub relation: Option<String>,
901 pub after: Option<String>,
902 pub limit: std::num::NonZeroU32,
903}
904
905#[derive(Debug, Serialize, Deserialize)]
907#[serde(deny_unknown_fields)]
908pub struct GraphPage {
909 pub selection: Selection,
910 pub after: Option<String>,
911 pub limit: std::num::NonZeroU32,
912 #[serde(default, skip_serializing_if = "Option::is_none")]
915 pub relations: Option<Vec<String>>,
916 #[serde(default, skip_serializing_if = "Option::is_none")]
918 pub collections: Option<Vec<String>>,
919 #[serde(default, skip_serializing_if = "Option::is_none")]
922 pub members: Option<std::collections::BTreeMap<String, Vec<String>>>,
923 #[serde(default, skip_serializing_if = "Option::is_none")]
927 pub records: Option<Vec<Node>>,
928 #[serde(default, skip_serializing_if = "Option::is_none")]
932 pub unrelated: Option<Unrelated>,
933}
934
935#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
937#[serde(deny_unknown_fields)]
938pub struct Unrelated {
939 pub per_collection: std::num::NonZeroU64,
941 pub most: std::num::NonZeroU64,
943}
944
945#[derive(Debug, Serialize, Deserialize)]
947#[serde(deny_unknown_fields)]
948pub struct VectorDeclaration {
949 pub collection: String,
950 pub field: String,
951 pub declaration: super::storage::embedded::Declaration,
952}
953
954#[derive(Debug, Serialize, Deserialize)]
960#[serde(deny_unknown_fields)]
961pub struct VectorWrite {
962 pub collection: String,
963 pub document_id: String,
964 pub field: String,
965 pub vector: Vec<f32>,
966}
967
968#[derive(Debug, Serialize, Deserialize)]
971#[serde(deny_unknown_fields)]
972pub struct VectorImport {
973 pub vectors: Vec<VectorWrite>,
974}
975
976#[derive(Debug, Serialize, Deserialize)]
977#[serde(deny_unknown_fields)]
978pub struct VectorDelete {
979 pub collection: String,
980 pub document_id: String,
981 pub field: String,
982}
983
984#[derive(Debug, Serialize, Deserialize)]
985#[serde(deny_unknown_fields)]
986pub struct VectorRead {
987 pub selection: Selection,
988 pub collection: String,
989 pub document_id: String,
990 pub field: String,
991}
992
993#[derive(Debug, Serialize, Deserialize)]
996#[serde(deny_unknown_fields)]
997pub struct VectorPage {
998 pub selection: Selection,
999 pub collection: String,
1000 pub field: String,
1001 pub after: Option<String>,
1002 pub limit: std::num::NonZeroU32,
1003}
1004
1005#[derive(Debug, Serialize, Deserialize)]
1008#[serde(deny_unknown_fields)]
1009pub struct VectorSearch {
1010 pub selection: Selection,
1011 pub collection: String,
1012 pub field: String,
1013 pub query: Vec<f32>,
1014 pub k: std::num::NonZeroU32,
1015 pub metric: Metric,
1016}
1017
1018#[derive(Debug, Serialize, Deserialize)]
1022#[serde(deny_unknown_fields)]
1023pub struct VectorSearchText {
1024 pub selection: Selection,
1025 pub collection: String,
1026 pub field: String,
1027 pub text: String,
1028 pub k: std::num::NonZeroU32,
1029}
1030
1031#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1034#[serde(rename_all = "lowercase")]
1035pub enum Metric {
1036 Cosine,
1037 L2,
1038 Dot,
1039}
1040
1041mod decimal {
1042 use serde::{Deserialize, Deserializer, Serializer, de::Error};
1043
1044 pub fn serialize<S: Serializer>(
1045 value: impl std::fmt::Display,
1046 serializer: S,
1047 ) -> Result<S::Ok, S::Error> {
1048 serializer.serialize_str(&value.to_string())
1049 }
1050
1051 pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u64, D::Error> {
1052 let text = String::deserialize(deserializer)?;
1053 let value = text.parse::<u64>().map_err(D::Error::custom)?;
1054 if text != value.to_string() {
1055 return Err(D::Error::custom(
1056 "expected a canonical unsigned decimal string",
1057 ));
1058 }
1059 Ok(value)
1060 }
1061}
1062
1063fn explicit_schema<'de, D: serde::Deserializer<'de>>(
1065 deserializer: D,
1066) -> Result<Option<Value>, D::Error> {
1067 Option::<Value>::deserialize(deserializer)
1068}
1069
1070#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1071pub enum WireOwnerKind {
1072 Database,
1073 Api,
1074 LeafLog,
1075 Observer,
1076 Branch,
1077 BranchCommit,
1078 BranchRef,
1079}
1080
1081#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1082#[serde(deny_unknown_fields)]
1083pub struct WireOwnedCause {
1084 pub kind: WireOwnerKind,
1085 pub message: String,
1086 pub database: Option<super::receipt_wire::WireCause>,
1087}
1088
1089#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1090#[serde(deny_unknown_fields)]
1091pub struct WireRefusal {
1092 pub code: String,
1093 pub message: String,
1094 pub path: Option<String>,
1095 pub cause: Option<WireOwnedCause>,
1096}
1097
1098#[derive(Debug, Serialize, Deserialize)]
1099#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
1100pub enum WirePacket {
1101 Receipt {
1102 receipt: Box<super::receipt_wire::WireReceipt>,
1103 refusal: Option<WireRefusal>,
1104 },
1105 Refusal {
1106 refusal: WireRefusal,
1107 },
1108}
1109impl WirePacket {
1110 #[must_use]
1111 pub fn receipt(&self) -> Option<&super::receipt_wire::WireReceipt> {
1112 match self {
1113 Self::Receipt { receipt, .. } => Some(receipt.as_ref()),
1114 Self::Refusal { .. } => None,
1115 }
1116 }
1117 #[must_use]
1118 pub const fn refusal(&self) -> Option<&WireRefusal> {
1119 match self {
1120 Self::Receipt { refusal, .. } => refusal.as_ref(),
1121 Self::Refusal { refusal } => Some(refusal),
1122 }
1123 }
1124}
1125
1126#[derive(Clone, Debug, Serialize, Deserialize)]
1127#[serde(deny_unknown_fields)]
1128pub struct HelloAgreement {
1129 pub protocol: String,
1130 pub capabilities: Vec<String>,
1131 pub max_frame: std::num::NonZeroU32,
1132}
1133impl HelloAgreement {
1134 pub(crate) fn client() -> Self {
1135 Self {
1136 protocol: PROTOCOL.to_owned(),
1137 capabilities: vec![RECEIPT_CAPABILITY.to_owned()],
1138 max_frame: std::num::NonZeroU32::MAX,
1139 }
1140 }
1141 pub(crate) fn validate(
1142 &self,
1143 server_cap: std::num::NonZeroU32,
1144 ) -> Result<std::num::NonZeroU32, Failure> {
1145 if self.protocol != PROTOCOL {
1146 return Err(Failure::new(
1147 "receipt_protocol_mismatch",
1148 format!("expected {PROTOCOL}, received {}", self.protocol),
1149 ));
1150 }
1151 if !self
1152 .capabilities
1153 .iter()
1154 .any(|capability| capability == RECEIPT_CAPABILITY)
1155 {
1156 return Err(Failure::new(
1157 "typed_receipt_capability_missing",
1158 format!("hello must agree {RECEIPT_CAPABILITY}"),
1159 ));
1160 }
1161 let cap = self.max_frame.min(server_cap);
1162 let minimum = super::receipt_fragments::minimum_cap().map_err(Failure::storage)?;
1163 if u64::from(cap.get()) < u64::try_from(minimum).map_err(Failure::storage)? {
1164 return Err(Failure::new(
1165 "receipt_frame_too_small",
1166 format!("offered {}, minimum {minimum}", cap.get()),
1167 ));
1168 }
1169 Ok(cap)
1170 }
1171}
1172
1173#[cfg(test)]
1174mod receipt_agreement_tests {
1175 use super::*;
1176
1177 type TestResult = Result<(), Box<dyn std::error::Error>>;
1178
1179 #[test]
1180 fn missing_capability_and_derived_floor_are_named_before_dispatch() -> TestResult {
1181 let mut agreement = HelloAgreement::client();
1182 agreement.capabilities.clear();
1183 let cap = std::num::NonZeroU32::MAX;
1184 let Err(missing) = agreement.validate(cap) else {
1185 return Err("a hello without the receipt capability was agreed".into());
1186 };
1187 assert_eq!(missing.code, "typed_receipt_capability_missing");
1188 agreement.capabilities.push(RECEIPT_CAPABILITY.to_owned());
1189 let minimum = super::super::receipt_fragments::minimum_cap()?;
1190 let below = u32::try_from(minimum - 1)?;
1191 agreement.max_frame = std::num::NonZeroU32::new(below).ok_or("floor is one")?;
1192 let Err(error) = agreement.validate(cap) else {
1193 return Err("a frame below the receipt floor was agreed".into());
1194 };
1195 assert_eq!(error.code, "receipt_frame_too_small");
1196 assert!(error.message.contains(&minimum.to_string()));
1197 assert!(error.message.contains(&agreement.max_frame.to_string()));
1198 Ok(())
1199 }
1200}