1use serde::{Deserialize, Serialize};
17use serde_json::Value;
18use std::collections::HashMap;
19use std::sync::{Arc, Mutex, RwLock};
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
27pub enum RelationKind {
28 OneToOne,
30 OneToMany,
32 ManyToOne,
34 ManyToMany,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Relation {
43 pub name: String,
45 pub from_type: String,
47 pub from_field: String,
49 pub to_type: String,
51 pub to_field: String,
53 pub kind: RelationKind,
55}
56
57impl Relation {
58 pub fn new(
59 name: impl Into<String>,
60 from_type: impl Into<String>,
61 from_field: impl Into<String>,
62 to_type: impl Into<String>,
63 to_field: impl Into<String>,
64 kind: RelationKind,
65 ) -> Self {
66 Self {
67 name: name.into(),
68 from_type: from_type.into(),
69 from_field: from_field.into(),
70 to_type: to_type.into(),
71 to_field: to_field.into(),
72 kind,
73 }
74 }
75
76 pub fn one_to_many(
78 name: impl Into<String>,
79 from_type: impl Into<String>,
80 to_type: impl Into<String>,
81 ) -> Self {
82 let from_type_str = from_type.into();
83 let to_field = format!("{}_id", from_type_str.to_lowercase());
84 Self::new(
85 name,
86 from_type_str,
87 "id",
88 to_type,
89 to_field,
90 RelationKind::OneToMany,
91 )
92 }
93
94 pub fn many_to_one(
96 name: impl Into<String>,
97 from_type: impl Into<String>,
98 to_type: impl Into<String>,
99 ) -> Self {
100 let to_type_str = to_type.into();
101 let from_field = format!("{}_id", to_type_str.to_lowercase());
102 Self::new(
103 name,
104 from_type,
105 from_field,
106 to_type_str,
107 "id",
108 RelationKind::ManyToOne,
109 )
110 }
111}
112
113pub type ResolverFn = Box<dyn Fn(&Value, &HashMap<String, Value>) -> Value + Send + Sync>;
117
118pub struct ResolverRegistry {
122 resolvers: RwLock<HashMap<String, Arc<ResolverFn>>>,
124}
125
126impl ResolverRegistry {
127 pub fn new() -> Self {
128 Self {
129 resolvers: RwLock::new(HashMap::new()),
130 }
131 }
132
133 pub fn register(&self, type_name: &str, field_name: &str, resolver: ResolverFn) {
137 let key = format!("{}.{}", type_name, field_name);
138 let mut map = self.resolvers.write().expect("resolver lock poisoned");
139 map.insert(key, Arc::new(resolver));
140 }
141
142 pub fn get(&self, type_name: &str, field_name: &str) -> Option<Arc<ResolverFn>> {
144 let key = format!("{}.{}", type_name, field_name);
145 let map = self.resolvers.read().expect("resolver lock poisoned");
146 map.get(&key).cloned()
147 }
148
149 pub fn resolve(
151 &self,
152 type_name: &str,
153 field_name: &str,
154 parent: &Value,
155 args: &HashMap<String, Value>,
156 ) -> Option<Value> {
157 self.get(type_name, field_name)
158 .map(|resolver| resolver(parent, args))
159 }
160
161 pub fn len(&self) -> usize {
163 self.resolvers.read().expect("resolver lock poisoned").len()
164 }
165
166 pub fn is_empty(&self) -> bool {
168 self.len() == 0
169 }
170}
171
172impl Default for ResolverRegistry {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178pub struct RelationDataSource {
182 data: RwLock<HashMap<String, Vec<Value>>>,
184 relations: RwLock<Vec<Relation>>,
186}
187
188impl RelationDataSource {
189 pub fn new() -> Self {
190 Self {
191 data: RwLock::new(HashMap::new()),
192 relations: RwLock::new(Vec::new()),
193 }
194 }
195
196 pub fn insert(&self, type_name: &str, doc: Value) {
198 let mut data = self.data.write().expect("data lock poisoned");
199 data.entry(type_name.to_string()).or_default().push(doc);
200 }
201
202 pub fn insert_many(&self, type_name: &str, docs: Vec<Value>) {
204 let mut data = self.data.write().expect("data lock poisoned");
205 data.entry(type_name.to_string()).or_default().extend(docs);
206 }
207
208 pub fn get_all(&self, type_name: &str) -> Vec<Value> {
210 let data = self.data.read().expect("data lock poisoned");
211 data.get(type_name).cloned().unwrap_or_default()
212 }
213
214 pub fn find_by_id(&self, type_name: &str, id: &str) -> Option<Value> {
216 let data = self.data.read().expect("data lock poisoned");
217 data.get(type_name).and_then(|docs| {
218 docs.iter()
219 .find(|d| d.get("id").and_then(|v| v.as_str()) == Some(id))
220 .cloned()
221 })
222 }
223
224 pub fn add_relation(&self, relation: Relation) {
226 let mut rels = self.relations.write().expect("relation lock poisoned");
227 rels.push(relation);
228 }
229
230 pub fn resolve_one_to_many(
234 &self,
235 from_type: &str,
236 from_field: &str,
237 parent_id: &str,
238 ) -> Vec<Value> {
239 let data = self.data.read().expect("data lock poisoned");
240 data.get(from_type)
241 .map(|docs| {
242 docs.iter()
243 .filter(|d| d.get(from_field).and_then(|v| v.as_str()) == Some(parent_id))
244 .cloned()
245 .collect()
246 })
247 .unwrap_or_default()
248 }
249
250 pub fn resolve_many_to_one(
254 &self,
255 to_type: &str,
256 to_field: &str,
257 foreign_key: &str,
258 ) -> Option<Value> {
259 let data = self.data.read().expect("data lock poisoned");
260 data.get(to_type).and_then(|docs| {
261 docs.iter()
262 .find(|d| d.get(to_field).and_then(|v| v.as_str()) == Some(foreign_key))
263 .cloned()
264 })
265 }
266}
267
268impl Default for RelationDataSource {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
282pub struct PageInfo {
283 pub has_next_page: bool,
285 pub has_previous_page: bool,
287 pub start_cursor: Option<String>,
289 pub end_cursor: Option<String>,
291}
292
293impl PageInfo {
294 pub fn new() -> Self {
295 Self {
296 has_next_page: false,
297 has_previous_page: false,
298 start_cursor: None,
299 end_cursor: None,
300 }
301 }
302
303 pub fn with_next(mut self, has_next: bool) -> Self {
304 self.has_next_page = has_next;
305 self
306 }
307
308 pub fn with_previous(mut self, has_prev: bool) -> Self {
309 self.has_previous_page = has_prev;
310 self
311 }
312}
313
314impl Default for PageInfo {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct Edge {
325 pub cursor: String,
327 pub node: Value,
329}
330
331impl Edge {
332 pub fn new(cursor: impl Into<String>, node: Value) -> Self {
333 Self {
334 cursor: cursor.into(),
335 node,
336 }
337 }
338
339 pub fn from_index(index: usize, node: Value) -> Self {
343 let cursor = encode_cursor(index);
344 Self::new(cursor, node)
345 }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct Connection {
353 pub edges: Vec<Edge>,
355 pub page_info: PageInfo,
357 pub total_count: usize,
359}
360
361impl Connection {
362 pub fn new(edges: Vec<Edge>, page_info: PageInfo, total_count: usize) -> Self {
363 Self {
364 edges,
365 page_info,
366 total_count,
367 }
368 }
369
370 pub fn empty() -> Self {
372 Self::new(Vec::new(), PageInfo::new(), 0)
373 }
374
375 pub fn nodes(&self) -> Vec<&Value> {
377 self.edges.iter().map(|e| &e.node).collect()
378 }
379}
380
381impl Default for Connection {
382 fn default() -> Self {
383 Self::empty()
384 }
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize)]
389pub struct PaginationArgs {
390 pub first: Option<usize>,
392 pub after: Option<String>,
394 pub last: Option<usize>,
396 pub before: Option<String>,
398}
399
400impl PaginationArgs {
401 pub fn new() -> Self {
402 Self {
403 first: None,
404 after: None,
405 last: None,
406 before: None,
407 }
408 }
409
410 pub fn with_first(mut self, n: usize) -> Self {
412 self.first = Some(n);
413 self
414 }
415
416 pub fn with_after(mut self, cursor: impl Into<String>) -> Self {
418 self.after = Some(cursor.into());
419 self
420 }
421
422 pub fn with_last(mut self, n: usize) -> Self {
424 self.last = Some(n);
425 self
426 }
427
428 pub fn with_before(mut self, cursor: impl Into<String>) -> Self {
430 self.before = Some(cursor.into());
431 self
432 }
433}
434
435impl Default for PaginationArgs {
436 fn default() -> Self {
437 Self::new()
438 }
439}
440
441fn encode_cursor(index: usize) -> String {
443 use std::fmt::Write;
444 let plain = format!("cursor:{}", index);
446 let bytes = plain.as_bytes();
447 let mut result = String::new();
448 const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
449 let mut i = 0;
450 while i < bytes.len() {
451 let b0 = bytes[i];
452 let b1 = if i + 1 < bytes.len() { bytes[i + 1] } else { 0 };
453 let b2 = if i + 2 < bytes.len() { bytes[i + 2] } else { 0 };
454
455 let _ = write!(
456 result,
457 "{}{}{}",
458 CHARS[(b0 >> 2) as usize] as char,
459 CHARS[((b0 << 4) & 0x30 | b1 >> 4) as usize] as char,
460 if i + 1 < bytes.len() {
461 CHARS[((b1 << 2) & 0x3C | b2 >> 6) as usize] as char
462 } else {
463 '='
464 }
465 );
466 let _ = write!(
467 result,
468 "{}",
469 if i + 2 < bytes.len() {
470 CHARS[(b2 & 0x3F) as usize] as char
471 } else {
472 '='
473 }
474 );
475 i += 3;
476 }
477 result
478}
479
480fn decode_cursor(cursor: &str) -> Option<usize> {
482 const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
483 let mut lookup = [0u8; 128];
484 for (i, &c) in CHARS.iter().enumerate() {
485 lookup[c as usize] = i as u8;
486 }
487 let bytes = cursor.as_bytes();
488 let mut decoded = Vec::new();
489 let mut i = 0;
490 while i < bytes.len() {
491 let c0 = lookup.get(bytes[i] as usize).copied().unwrap_or(0);
493 if i + 1 >= bytes.len() || bytes[i + 1] == b'=' {
495 break;
496 }
497 let c1 = lookup.get(bytes[i + 1] as usize).copied().unwrap_or(0);
498 let c2_pad = i + 2 >= bytes.len() || bytes[i + 2] == b'=';
500 let c2 = if !c2_pad {
501 lookup.get(bytes[i + 2] as usize).copied().unwrap_or(0)
502 } else {
503 0
504 };
505 let c3_pad = i + 3 >= bytes.len() || bytes[i + 3] == b'=';
507 let c3 = if !c3_pad {
508 lookup.get(bytes[i + 3] as usize).copied().unwrap_or(0)
509 } else {
510 0
511 };
512 decoded.push((c0 << 2) | (c1 >> 4));
514 if !c2_pad {
516 decoded.push(((c1 & 0x0F) << 4) | (c2 >> 2));
517 if !c3_pad {
519 decoded.push(((c2 & 0x03) << 6) | c3);
520 }
521 }
522 i += 4;
523 }
524 let plain = String::from_utf8(decoded).ok()?;
525 plain
526 .strip_prefix("cursor:")
527 .and_then(|s| s.parse::<usize>().ok())
528}
529
530pub fn paginate(nodes: Vec<Value>, args: &PaginationArgs) -> Connection {
534 let total_count = nodes.len();
535
536 if let Some(first) = args.first {
538 let start = match args.after.as_ref().and_then(|c| decode_cursor(c)) {
539 Some(idx) => idx + 1,
540 None => 0,
541 };
542
543 let end = (start + first).min(nodes.len());
544 let has_next_page = end < nodes.len();
545 let has_previous_page = start > 0;
546
547 if start >= nodes.len() {
548 return Connection::empty();
549 }
550
551 let slice = &nodes[start..end];
552 let edges: Vec<Edge> = slice
553 .iter()
554 .enumerate()
555 .map(|(i, node)| Edge::from_index(start + i, node.clone()))
556 .collect();
557
558 let start_cursor = edges.first().map(|e| e.cursor.clone());
559 let end_cursor = edges.last().map(|e| e.cursor.clone());
560
561 let page_info = PageInfo {
562 has_next_page,
563 has_previous_page,
564 start_cursor,
565 end_cursor,
566 };
567
568 return Connection::new(edges, page_info, total_count);
569 }
570
571 if let Some(last) = args.last {
573 let end = match args.before.as_ref().and_then(|c| decode_cursor(c)) {
574 Some(idx) => idx,
575 None => nodes.len(),
576 };
577
578 let start = end.saturating_sub(last);
579 let has_next_page = end < nodes.len();
580 let has_previous_page = start > 0;
581
582 if start >= end || end == 0 {
583 return Connection::empty();
584 }
585
586 let slice = &nodes[start..end];
587 let edges: Vec<Edge> = slice
588 .iter()
589 .enumerate()
590 .map(|(i, node)| Edge::from_index(start + i, node.clone()))
591 .collect();
592
593 let start_cursor = edges.first().map(|e| e.cursor.clone());
594 let end_cursor = edges.last().map(|e| e.cursor.clone());
595
596 let page_info = PageInfo {
597 has_next_page,
598 has_previous_page,
599 start_cursor,
600 end_cursor,
601 };
602
603 return Connection::new(edges, page_info, total_count);
604 }
605
606 let edges: Vec<Edge> = nodes
608 .iter()
609 .enumerate()
610 .map(|(i, node)| Edge::from_index(i, node.clone()))
611 .collect();
612 let page_info = PageInfo {
613 has_next_page: false,
614 has_previous_page: false,
615 start_cursor: edges.first().map(|e| e.cursor.clone()),
616 end_cursor: edges.last().map(|e| e.cursor.clone()),
617 };
618 Connection::new(edges, page_info, total_count)
619}
620
621#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
627pub enum MutationKind {
628 Create,
630 Update,
632 Delete,
634}
635
636#[derive(Debug, Clone, Serialize, Deserialize)]
640pub struct MutationInput {
641 pub kind: MutationKind,
643 pub type_name: String,
645 pub id: Option<String>,
647 pub data: Option<Value>,
649}
650
651impl MutationInput {
652 pub fn create(type_name: impl Into<String>, data: Value) -> Self {
653 Self {
654 kind: MutationKind::Create,
655 type_name: type_name.into(),
656 id: None,
657 data: Some(data),
658 }
659 }
660
661 pub fn update(type_name: impl Into<String>, id: impl Into<String>, data: Value) -> Self {
662 Self {
663 kind: MutationKind::Update,
664 type_name: type_name.into(),
665 id: Some(id.into()),
666 data: Some(data),
667 }
668 }
669
670 pub fn delete(type_name: impl Into<String>, id: impl Into<String>) -> Self {
671 Self {
672 kind: MutationKind::Delete,
673 type_name: type_name.into(),
674 id: Some(id.into()),
675 data: None,
676 }
677 }
678}
679
680#[derive(Debug, Clone, Serialize, Deserialize)]
682pub struct MutationResult {
683 pub success: bool,
685 pub affected: usize,
687 pub data: Option<Value>,
689 pub error: Option<String>,
691}
692
693impl MutationResult {
694 pub fn ok(data: Option<Value>) -> Self {
695 Self {
696 success: true,
697 affected: 1,
698 data,
699 error: None,
700 }
701 }
702
703 pub fn ok_many(affected: usize, data: Option<Value>) -> Self {
704 Self {
705 success: true,
706 affected,
707 data,
708 error: None,
709 }
710 }
711
712 pub fn err(message: impl Into<String>) -> Self {
713 Self {
714 success: false,
715 affected: 0,
716 data: None,
717 error: Some(message.into()),
718 }
719 }
720}
721
722pub type MutationHandlerFn = Box<dyn Fn(&MutationInput) -> MutationResult + Send + Sync>;
724
725pub struct MutationRegistry {
729 handlers: RwLock<HashMap<String, Arc<MutationHandlerFn>>>,
731}
732
733impl MutationRegistry {
734 pub fn new() -> Self {
735 Self {
736 handlers: RwLock::new(HashMap::new()),
737 }
738 }
739
740 pub fn register(&self, type_name: &str, kind: MutationKind, handler: MutationHandlerFn) {
742 let key = mutation_key(type_name, &kind);
743 let mut map = self.handlers.write().expect("handler lock poisoned");
744 map.insert(key, Arc::new(handler));
745 }
746
747 pub fn get(&self, type_name: &str, kind: &MutationKind) -> Option<Arc<MutationHandlerFn>> {
749 let key = mutation_key(type_name, kind);
750 self.handlers
751 .read()
752 .expect("handler lock poisoned")
753 .get(&key)
754 .cloned()
755 }
756
757 pub fn execute(&self, input: &MutationInput) -> MutationResult {
759 match self.get(&input.type_name, &input.kind) {
760 Some(handler) => handler(input),
761 None => MutationResult::err(format!(
762 "no mutation handler for {:?} on type '{}'",
763 input.kind, input.type_name
764 )),
765 }
766 }
767
768 pub fn len(&self) -> usize {
770 self.handlers.read().expect("handler lock poisoned").len()
771 }
772
773 pub fn is_empty(&self) -> bool {
775 self.len() == 0
776 }
777}
778
779impl Default for MutationRegistry {
780 fn default() -> Self {
781 Self::new()
782 }
783}
784
785fn mutation_key(type_name: &str, kind: &MutationKind) -> String {
787 let kind_str = match kind {
788 MutationKind::Create => "create",
789 MutationKind::Update => "update",
790 MutationKind::Delete => "delete",
791 };
792 format!("{}.{}", type_name, kind_str)
793}
794
795pub struct InMemoryStore {
799 data: RwLock<HashMap<String, Vec<Value>>>,
800 counters: RwLock<HashMap<String, u64>>,
802}
803
804impl InMemoryStore {
805 pub fn new() -> Self {
806 Self {
807 data: RwLock::new(HashMap::new()),
808 counters: RwLock::new(HashMap::new()),
809 }
810 }
811
812 fn next_id(&self, type_name: &str) -> String {
814 let mut counters = self.counters.write().expect("counter lock poisoned");
815 let counter = counters.entry(type_name.to_string()).or_insert(0);
816 *counter += 1;
817 counter.to_string()
818 }
819
820 pub fn create(&self, type_name: &str, mut data: Value) -> MutationResult {
822 let id = self.next_id(type_name);
823 if let Some(obj) = data.as_object_mut() {
825 obj.insert("id".to_string(), Value::String(id.clone()));
826 }
827 let mut store = self.data.write().expect("store lock poisoned");
828 store
829 .entry(type_name.to_string())
830 .or_default()
831 .push(data.clone());
832 MutationResult::ok(Some(data))
833 }
834
835 pub fn update(&self, type_name: &str, id: &str, patch: &Value) -> MutationResult {
837 let mut store = self.data.write().expect("store lock poisoned");
838 let docs = match store.get_mut(type_name) {
839 Some(d) => d,
840 None => return MutationResult::err(format!("type '{}' not found", type_name)),
841 };
842 let doc = docs
843 .iter_mut()
844 .find(|d| d.get("id").and_then(|v| v.as_str()) == Some(id));
845 match doc {
846 Some(doc) => {
847 if let (Some(obj), Some(patch_obj)) = (doc.as_object_mut(), patch.as_object()) {
849 for (k, v) in patch_obj {
850 obj.insert(k.clone(), v.clone());
851 }
852 }
853 MutationResult::ok(Some(doc.clone()))
854 }
855 None => MutationResult::err(format!(
856 "document with id '{}' not found in type '{}'",
857 id, type_name
858 )),
859 }
860 }
861
862 pub fn delete(&self, type_name: &str, id: &str) -> MutationResult {
864 let mut store = self.data.write().expect("store lock poisoned");
865 let docs = match store.get_mut(type_name) {
866 Some(d) => d,
867 None => return MutationResult::err(format!("type '{}' not found", type_name)),
868 };
869 let before = docs.len();
870 docs.retain(|d| d.get("id").and_then(|v| v.as_str()) != Some(id));
871 let after = docs.len();
872 if before == after {
873 MutationResult::err(format!(
874 "document with id '{}' not found in type '{}'",
875 id, type_name
876 ))
877 } else {
878 MutationResult::ok_many(1, None)
879 }
880 }
881
882 pub fn get_all(&self, type_name: &str) -> Vec<Value> {
884 self.data
885 .read()
886 .expect("store lock poisoned")
887 .get(type_name)
888 .cloned()
889 .unwrap_or_default()
890 }
891
892 pub fn find_by_id(&self, type_name: &str, id: &str) -> Option<Value> {
894 self.data
895 .read()
896 .expect("store lock poisoned")
897 .get(type_name)
898 .and_then(|docs| {
899 docs.iter()
900 .find(|d| d.get("id").and_then(|v| v.as_str()) == Some(id))
901 .cloned()
902 })
903 }
904
905 pub fn register_to(&self, type_name: &str, registry: &MutationRegistry) {
909 let tn = type_name.to_string();
913 registry.register(
914 type_name,
915 MutationKind::Create,
916 Box::new(move |input: &MutationInput| {
917 let _ = &tn;
918 match input.data {
919 Some(ref data) => {
920 MutationResult::ok(Some(data.clone()))
922 }
923 None => MutationResult::err("create mutation requires data"),
924 }
925 }),
926 );
927 }
928}
929
930impl Default for InMemoryStore {
931 fn default() -> Self {
932 Self::new()
933 }
934}
935
936#[derive(Debug, Clone, Serialize, Deserialize)]
942pub struct SubscriptionEvent {
943 pub topic: String,
945 pub payload: Value,
947 pub sequence: u64,
949}
950
951impl SubscriptionEvent {
952 pub fn new(topic: impl Into<String>, payload: Value, sequence: u64) -> Self {
953 Self {
954 topic: topic.into(),
955 payload,
956 sequence,
957 }
958 }
959}
960
961pub type SubscriptionId = u64;
963
964#[derive(Debug, Clone)]
966enum SubscriptionMessage {
967 Event(SubscriptionEvent),
968}
969
970pub struct SubscriptionHandle {
974 id: SubscriptionId,
975 topic: String,
976 receiver: std::sync::mpsc::Receiver<SubscriptionMessage>,
977 broker: Option<Arc<SubscriptionBrokerInner>>,
978}
979
980impl SubscriptionHandle {
981 pub fn id(&self) -> SubscriptionId {
983 self.id
984 }
985
986 pub fn topic(&self) -> &str {
988 &self.topic
989 }
990
991 pub fn try_recv(&self) -> Option<SubscriptionEvent> {
993 match self.receiver.try_recv() {
994 Ok(SubscriptionMessage::Event(e)) => Some(e),
995 Err(_) => None,
996 }
997 }
998
999 pub fn recv(&self) -> Option<SubscriptionEvent> {
1001 match self.receiver.recv() {
1002 Ok(SubscriptionMessage::Event(e)) => Some(e),
1003 Err(_) => None,
1004 }
1005 }
1006}
1007
1008impl Drop for SubscriptionHandle {
1009 fn drop(&mut self) {
1010 if let Some(broker) = self.broker.take() {
1012 broker.unsubscribe(self.id);
1013 }
1014 }
1015}
1016
1017type SubscriberEntry = (SubscriptionId, std::sync::mpsc::Sender<SubscriptionMessage>);
1019
1020struct SubscriptionBrokerInner {
1022 subscribers: Mutex<HashMap<String, Vec<SubscriberEntry>>>,
1024 next_id: Mutex<SubscriptionId>,
1026 sequence: Mutex<u64>,
1028}
1029
1030impl SubscriptionBrokerInner {
1031 fn new() -> Self {
1032 Self {
1033 subscribers: Mutex::new(HashMap::new()),
1034 next_id: Mutex::new(1),
1035 sequence: Mutex::new(0),
1036 }
1037 }
1038
1039 fn next_sequence(&self) -> u64 {
1040 let mut seq = self.sequence.lock().expect("seq lock poisoned");
1041 *seq += 1;
1042 *seq
1043 }
1044
1045 fn subscribe(
1046 &self,
1047 topic: &str,
1048 ) -> (
1049 SubscriptionId,
1050 std::sync::mpsc::Receiver<SubscriptionMessage>,
1051 ) {
1052 let (tx, rx) = std::sync::mpsc::channel();
1053 let id = {
1054 let mut next = self.next_id.lock().expect("id lock poisoned");
1055 let id = *next;
1056 *next += 1;
1057 id
1058 };
1059 let mut subs = self.subscribers.lock().expect("sub lock poisoned");
1060 subs.entry(topic.to_string()).or_default().push((id, tx));
1061 (id, rx)
1062 }
1063
1064 fn unsubscribe(&self, id: SubscriptionId) {
1065 let mut subs = self.subscribers.lock().expect("sub lock poisoned");
1066 for list in subs.values_mut() {
1067 list.retain(|(sub_id, _)| *sub_id != id);
1068 }
1069 subs.retain(|_, list| !list.is_empty());
1071 }
1072
1073 fn publish(&self, topic: &str, payload: Value) -> usize {
1074 let seq = self.next_sequence();
1075 let event = SubscriptionEvent::new(topic, payload, seq);
1076 let mut subs = self.subscribers.lock().expect("sub lock poisoned");
1077 let list = match subs.get_mut(topic) {
1078 Some(l) => l,
1079 None => return 0,
1080 };
1081 let mut delivered = 0;
1082 let mut to_remove = Vec::new();
1084 for (i, (_, sender)) in list.iter().enumerate() {
1085 match sender.send(SubscriptionMessage::Event(event.clone())) {
1086 Ok(_) => delivered += 1,
1087 Err(_) => to_remove.push(i),
1088 }
1089 }
1090 for i in to_remove.into_iter().rev() {
1092 list.remove(i);
1093 }
1094 delivered
1095 }
1096
1097 fn subscriber_count(&self, topic: &str) -> usize {
1098 let subs = self.subscribers.lock().expect("sub lock poisoned");
1099 subs.get(topic).map(|l| l.len()).unwrap_or(0)
1100 }
1101
1102 fn topic_count(&self) -> usize {
1103 let subs = self.subscribers.lock().expect("sub lock poisoned");
1104 subs.len()
1105 }
1106}
1107
1108pub struct SubscriptionBroker {
1112 inner: Arc<SubscriptionBrokerInner>,
1113}
1114
1115impl SubscriptionBroker {
1116 pub fn new() -> Self {
1117 Self {
1118 inner: Arc::new(SubscriptionBrokerInner::new()),
1119 }
1120 }
1121
1122 pub fn subscribe(&self, topic: &str) -> SubscriptionHandle {
1126 let (id, rx) = self.inner.subscribe(topic);
1127 SubscriptionHandle {
1128 id,
1129 topic: topic.to_string(),
1130 receiver: rx,
1131 broker: Some(self.inner.clone()),
1132 }
1133 }
1134
1135 pub fn publish(&self, topic: &str, payload: Value) -> usize {
1139 self.inner.publish(topic, payload)
1140 }
1141
1142 pub fn subscriber_count(&self, topic: &str) -> usize {
1144 self.inner.subscriber_count(topic)
1145 }
1146
1147 pub fn topic_count(&self) -> usize {
1149 self.inner.topic_count()
1150 }
1151}
1152
1153impl Default for SubscriptionBroker {
1154 fn default() -> Self {
1155 Self::new()
1156 }
1157}
1158
1159impl Clone for SubscriptionBroker {
1160 fn clone(&self) -> Self {
1161 Self {
1162 inner: self.inner.clone(),
1163 }
1164 }
1165}
1166
1167pub struct SchemaExtensions {
1175 pub relations: Vec<Relation>,
1177 pub mutations: Vec<(String, MutationKind)>,
1179 pub subscriptions: Vec<String>,
1181}
1182
1183impl SchemaExtensions {
1184 pub fn new() -> Self {
1185 Self {
1186 relations: Vec::new(),
1187 mutations: Vec::new(),
1188 subscriptions: Vec::new(),
1189 }
1190 }
1191
1192 pub fn with_relation(mut self, relation: Relation) -> Self {
1194 self.relations.push(relation);
1195 self
1196 }
1197
1198 pub fn with_mutation(mut self, type_name: impl Into<String>, kind: MutationKind) -> Self {
1200 self.mutations.push((type_name.into(), kind));
1201 self
1202 }
1203
1204 pub fn with_subscription(mut self, topic: impl Into<String>) -> Self {
1206 self.subscriptions.push(topic.into());
1207 self
1208 }
1209
1210 pub fn to_sdl(&self) -> String {
1212 let mut out = String::new();
1213
1214 if !self.relations.is_empty() {
1216 out.push_str("# Relations\n");
1217 for rel in &self.relations {
1218 out.push_str(&format!(
1219 "# {} {}.{} -> {} ({:?})\n",
1220 rel.name, rel.from_type, rel.from_field, rel.to_type, rel.kind
1221 ));
1222 }
1223 out.push('\n');
1224 }
1225
1226 if !self.mutations.is_empty() {
1228 out.push_str("type Mutation {\n");
1229 for (type_name, kind) in &self.mutations {
1230 let op = match kind {
1231 MutationKind::Create => {
1232 format!("create{type_name}(input: {type_name}Input!): {type_name}")
1233 }
1234 MutationKind::Update => {
1235 format!("update{type_name}(id: ID!, input: {type_name}Input!): {type_name}")
1236 }
1237 MutationKind::Delete => format!("delete{type_name}(id: ID!): Boolean!"),
1238 };
1239 out.push_str(&format!(" {}\n", op));
1240 }
1241 out.push_str("}\n\n");
1242 }
1243
1244 if !self.subscriptions.is_empty() {
1246 out.push_str("type Subscription {\n");
1247 for topic in &self.subscriptions {
1248 out.push_str(&format!(" {}: SubscriptionEvent!\n", topic));
1249 }
1250 out.push_str("}\n\n");
1251 out.push_str("type SubscriptionEvent {\n");
1252 out.push_str(" topic: String!\n");
1253 out.push_str(" payload: JSON!\n");
1254 out.push_str(" sequence: Int!\n");
1255 out.push_str("}\n");
1256 }
1257
1258 out
1259 }
1260}
1261
1262impl Default for SchemaExtensions {
1263 fn default() -> Self {
1264 Self::new()
1265 }
1266}
1267
1268#[cfg(test)]
1269mod tests {
1270 use super::*;
1271 use serde_json::json;
1272
1273 #[test]
1276 fn test_relation_new() {
1277 let rel = Relation::new(
1278 "userOrders",
1279 "User",
1280 "id",
1281 "Order",
1282 "user_id",
1283 RelationKind::OneToMany,
1284 );
1285 assert_eq!(rel.name, "userOrders");
1286 assert_eq!(rel.from_type, "User");
1287 assert_eq!(rel.from_field, "id");
1288 assert_eq!(rel.to_type, "Order");
1289 assert_eq!(rel.to_field, "user_id");
1290 assert_eq!(rel.kind, RelationKind::OneToMany);
1291 }
1292
1293 #[test]
1294 fn test_relation_one_to_many() {
1295 let rel = Relation::one_to_many("userOrders", "User", "Order");
1296 assert_eq!(rel.kind, RelationKind::OneToMany);
1297 assert_eq!(rel.from_type, "User");
1298 assert_eq!(rel.to_type, "Order");
1299 assert_eq!(rel.from_field, "id");
1300 assert_eq!(rel.to_field, "user_id");
1301 }
1302
1303 #[test]
1304 fn test_relation_many_to_one() {
1305 let rel = Relation::many_to_one("orderUser", "Order", "User");
1306 assert_eq!(rel.kind, RelationKind::ManyToOne);
1307 assert_eq!(rel.from_type, "Order");
1308 assert_eq!(rel.to_type, "User");
1309 assert_eq!(rel.from_field, "user_id");
1310 assert_eq!(rel.to_field, "id");
1311 }
1312
1313 #[test]
1314 fn test_relation_kind_serde() {
1315 let kind = RelationKind::ManyToMany;
1316 let json = serde_json::to_string(&kind).unwrap();
1317 let de: RelationKind = serde_json::from_str(&json).unwrap();
1318 assert_eq!(de, kind);
1319 }
1320
1321 #[test]
1324 fn test_resolver_registry_register_and_get() {
1325 let registry = ResolverRegistry::new();
1326 registry.register(
1327 "User",
1328 "orders",
1329 Box::new(|parent, _args| {
1330 let id = parent["id"].as_str().unwrap_or("");
1331 json!([{"id": "1", "user_id": id, "name": "order1"}])
1332 }),
1333 );
1334 assert_eq!(registry.len(), 1);
1335
1336 let parent = json!({"id": "u1"});
1337 let args = HashMap::new();
1338 let result = registry.resolve("User", "orders", &parent, &args);
1339 assert!(result.is_some());
1340 let result = result.unwrap();
1341 assert!(result.is_array());
1342 assert_eq!(result[0]["user_id"], "u1");
1343 }
1344
1345 #[test]
1346 fn test_resolver_registry_get_missing() {
1347 let registry = ResolverRegistry::new();
1348 assert!(registry.get("Unknown", "field").is_none());
1349 assert!(registry.is_empty());
1350 }
1351
1352 #[test]
1353 fn test_resolver_registry_multiple() {
1354 let registry = ResolverRegistry::new();
1355 registry.register("User", "orders", Box::new(|_, _| json!([{"id": "1"}])));
1356 registry.register("Order", "user", Box::new(|_, _| json!({"id": "u1"})));
1357 assert_eq!(registry.len(), 2);
1358
1359 let parent = json!({});
1360 let args = HashMap::new();
1361 assert!(registry.resolve("User", "orders", &parent, &args).is_some());
1362 assert!(registry.resolve("Order", "user", &parent, &args).is_some());
1363 assert!(registry.resolve("User", "user", &parent, &args).is_none());
1364 }
1365
1366 #[test]
1369 fn test_data_source_insert_and_get() {
1370 let ds = RelationDataSource::new();
1371 ds.insert("User", json!({"id": "1", "name": "Alice"}));
1372 ds.insert("User", json!({"id": "2", "name": "Bob"}));
1373
1374 let users = ds.get_all("User");
1375 assert_eq!(users.len(), 2);
1376 }
1377
1378 #[test]
1379 fn test_data_source_find_by_id() {
1380 let ds = RelationDataSource::new();
1381 ds.insert("User", json!({"id": "1", "name": "Alice"}));
1382 ds.insert("User", json!({"id": "2", "name": "Bob"}));
1383
1384 let user = ds.find_by_id("User", "2").unwrap();
1385 assert_eq!(user["name"], "Bob");
1386 assert!(ds.find_by_id("User", "999").is_none());
1387 }
1388
1389 #[test]
1390 fn test_data_source_resolve_one_to_many() {
1391 let ds = RelationDataSource::new();
1392 ds.insert("User", json!({"id": "u1", "name": "Alice"}));
1393 ds.insert_many(
1394 "Order",
1395 vec![
1396 json!({"id": "o1", "user_id": "u1", "total": 100}),
1397 json!({"id": "o2", "user_id": "u1", "total": 200}),
1398 json!({"id": "o3", "user_id": "u2", "total": 300}),
1399 ],
1400 );
1401
1402 let orders = ds.resolve_one_to_many("Order", "user_id", "u1");
1403 assert_eq!(orders.len(), 2);
1404 assert_eq!(orders[0]["id"], "o1");
1405 assert_eq!(orders[1]["id"], "o2");
1406 }
1407
1408 #[test]
1409 fn test_data_source_resolve_many_to_one() {
1410 let ds = RelationDataSource::new();
1411 ds.insert("User", json!({"id": "u1", "name": "Alice"}));
1412 ds.insert("User", json!({"id": "u2", "name": "Bob"}));
1413 ds.insert("Order", json!({"id": "o1", "user_id": "u1", "total": 100}));
1414
1415 let user = ds.resolve_many_to_one("User", "id", "u1").unwrap();
1416 assert_eq!(user["name"], "Alice");
1417
1418 let user2 = ds.resolve_many_to_one("User", "id", "u2").unwrap();
1419 assert_eq!(user2["name"], "Bob");
1420
1421 assert!(ds.resolve_many_to_one("User", "id", "u999").is_none());
1422 }
1423
1424 #[test]
1425 fn test_data_source_empty_type() {
1426 let ds = RelationDataSource::new();
1427 assert!(ds.get_all("Nonexistent").is_empty());
1428 assert!(ds.find_by_id("Nonexistent", "1").is_none());
1429 assert!(ds.resolve_one_to_many("Nonexistent", "fk", "1").is_empty());
1430 assert!(ds.resolve_many_to_one("Nonexistent", "id", "1").is_none());
1431 }
1432
1433 #[test]
1436 fn test_page_info_new() {
1437 let pi = PageInfo::new();
1438 assert!(!pi.has_next_page);
1439 assert!(!pi.has_previous_page);
1440 assert!(pi.start_cursor.is_none());
1441 assert!(pi.end_cursor.is_none());
1442 }
1443
1444 #[test]
1445 fn test_page_info_builder() {
1446 let pi = PageInfo::new().with_next(true).with_previous(false);
1447 assert!(pi.has_next_page);
1448 assert!(!pi.has_previous_page);
1449 }
1450
1451 #[test]
1452 fn test_edge_from_index() {
1453 let edge = Edge::from_index(5, json!({"id": "5"}));
1454 assert!(!edge.cursor.is_empty());
1455 assert_eq!(edge.node["id"], "5");
1456 }
1457
1458 #[test]
1459 fn test_connection_empty() {
1460 let conn = Connection::empty();
1461 assert!(conn.edges.is_empty());
1462 assert_eq!(conn.total_count, 0);
1463 assert!(conn.nodes().is_empty());
1464 }
1465
1466 #[test]
1467 fn test_cursor_encode_decode_roundtrip() {
1468 for i in [0, 1, 5, 10, 100, 999, 1000] {
1469 let cursor = encode_cursor(i);
1470 let decoded = decode_cursor(&cursor);
1471 assert_eq!(decoded, Some(i), "roundtrip failed for index {}", i);
1472 }
1473 }
1474
1475 #[test]
1476 fn test_decode_invalid_cursor() {
1477 assert!(decode_cursor("!!!invalid!!!").is_none());
1478 assert!(decode_cursor("").is_none());
1479 }
1480
1481 #[test]
1482 fn test_paginate_no_args_returns_all() {
1483 let nodes = vec![json!({"id": "1"}), json!({"id": "2"}), json!({"id": "3"})];
1484 let conn = paginate(nodes, &PaginationArgs::new());
1485 assert_eq!(conn.edges.len(), 3);
1486 assert_eq!(conn.total_count, 3);
1487 assert!(!conn.page_info.has_next_page);
1488 assert!(!conn.page_info.has_previous_page);
1489 }
1490
1491 #[test]
1492 fn test_paginate_first_n() {
1493 let nodes: Vec<Value> = (1..=10).map(|i| json!({"id": i.to_string()})).collect();
1494 let args = PaginationArgs::new().with_first(3);
1495 let conn = paginate(nodes, &args);
1496 assert_eq!(conn.edges.len(), 3);
1497 assert_eq!(conn.total_count, 10);
1498 assert!(conn.page_info.has_next_page);
1499 assert!(!conn.page_info.has_previous_page);
1500 }
1501
1502 #[test]
1503 fn test_paginate_first_after() {
1504 let nodes: Vec<Value> = (1..=10).map(|i| json!({"id": i.to_string()})).collect();
1505 let args1 = PaginationArgs::new().with_first(3);
1507 let conn1 = paginate(nodes.clone(), &args1);
1508 let after_cursor = conn1.page_info.end_cursor.unwrap();
1509
1510 let args2 = PaginationArgs::new().with_first(3).with_after(after_cursor);
1512 let conn2 = paginate(nodes, &args2);
1513 assert_eq!(conn2.edges.len(), 3);
1514 assert_eq!(conn2.edges[0].node["id"], "4");
1515 assert_eq!(conn2.edges[2].node["id"], "6");
1516 assert!(conn2.page_info.has_next_page);
1517 assert!(conn2.page_info.has_previous_page);
1518 }
1519
1520 #[test]
1521 fn test_paginate_first_beyond_end() {
1522 let nodes = vec![json!({"id": "1"}), json!({"id": "2"})];
1523 let args = PaginationArgs::new().with_first(10);
1524 let conn = paginate(nodes, &args);
1525 assert_eq!(conn.edges.len(), 2);
1526 assert_eq!(conn.total_count, 2);
1527 assert!(!conn.page_info.has_next_page);
1528 }
1529
1530 #[test]
1531 fn test_paginate_last_n() {
1532 let nodes: Vec<Value> = (1..=10).map(|i| json!({"id": i.to_string()})).collect();
1533 let args = PaginationArgs::new().with_last(3);
1534 let conn = paginate(nodes, &args);
1535 assert_eq!(conn.edges.len(), 3);
1536 assert_eq!(conn.edges[0].node["id"], "8");
1537 assert_eq!(conn.edges[2].node["id"], "10");
1538 assert!(!conn.page_info.has_next_page);
1539 assert!(conn.page_info.has_previous_page);
1540 }
1541
1542 #[test]
1543 fn test_paginate_empty_list() {
1544 let conn = paginate(Vec::new(), &PaginationArgs::new().with_first(5));
1545 assert_eq!(conn.edges.len(), 0);
1546 assert_eq!(conn.total_count, 0);
1547 }
1548
1549 #[test]
1550 fn test_paginate_after_beyond_end() {
1551 let nodes = vec![json!({"id": "1"}), json!({"id": "2"})];
1552 let cursor = encode_cursor(100);
1554 let args = PaginationArgs::new().with_first(5).with_after(cursor);
1555 let conn = paginate(nodes, &args);
1556 assert_eq!(conn.edges.len(), 0);
1557 }
1558
1559 #[test]
1560 fn test_connection_nodes() {
1561 let edges = vec![
1562 Edge::from_index(0, json!({"id": "1"})),
1563 Edge::from_index(1, json!({"id": "2"})),
1564 ];
1565 let conn = Connection::new(edges, PageInfo::new(), 2);
1566 let nodes = conn.nodes();
1567 assert_eq!(nodes.len(), 2);
1568 assert_eq!(nodes[0]["id"], "1");
1569 }
1570
1571 #[test]
1574 fn test_mutation_input_create() {
1575 let input = MutationInput::create("User", json!({"name": "Alice"}));
1576 assert_eq!(input.kind, MutationKind::Create);
1577 assert_eq!(input.type_name, "User");
1578 assert!(input.id.is_none());
1579 assert!(input.data.is_some());
1580 }
1581
1582 #[test]
1583 fn test_mutation_input_update() {
1584 let input = MutationInput::update("User", "1", json!({"name": "Bob"}));
1585 assert_eq!(input.kind, MutationKind::Update);
1586 assert_eq!(input.type_name, "User");
1587 assert_eq!(input.id, Some("1".to_string()));
1588 }
1589
1590 #[test]
1591 fn test_mutation_input_delete() {
1592 let input = MutationInput::delete("User", "1");
1593 assert_eq!(input.kind, MutationKind::Delete);
1594 assert!(input.data.is_none());
1595 }
1596
1597 #[test]
1598 fn test_mutation_result_ok() {
1599 let result = MutationResult::ok(Some(json!({"id": "1"})));
1600 assert!(result.success);
1601 assert_eq!(result.affected, 1);
1602 assert!(result.error.is_none());
1603 }
1604
1605 #[test]
1606 fn test_mutation_result_err() {
1607 let result = MutationResult::err("not found");
1608 assert!(!result.success);
1609 assert_eq!(result.affected, 0);
1610 assert_eq!(result.error, Some("not found".to_string()));
1611 }
1612
1613 #[test]
1616 fn test_mutation_registry_execute() {
1617 let registry = MutationRegistry::new();
1618 registry.register(
1619 "User",
1620 MutationKind::Create,
1621 Box::new(|input| MutationResult::ok(input.data.clone())),
1622 );
1623 let input = MutationInput::create("User", json!({"name": "Alice"}));
1624 let result = registry.execute(&input);
1625 assert!(result.success);
1626 assert_eq!(result.data.unwrap()["name"], "Alice");
1627 }
1628
1629 #[test]
1630 fn test_mutation_registry_no_handler() {
1631 let registry = MutationRegistry::new();
1632 let input = MutationInput::create("Unknown", json!({}));
1633 let result = registry.execute(&input);
1634 assert!(!result.success);
1635 assert!(result.error.unwrap().contains("no mutation handler"));
1636 }
1637
1638 #[test]
1639 fn test_mutation_registry_multiple() {
1640 let registry = MutationRegistry::new();
1641 registry.register(
1642 "User",
1643 MutationKind::Create,
1644 Box::new(|_| MutationResult::ok(None)),
1645 );
1646 registry.register(
1647 "User",
1648 MutationKind::Delete,
1649 Box::new(|_| MutationResult::ok_many(1, None)),
1650 );
1651 assert_eq!(registry.len(), 2);
1652
1653 let create_result = registry.execute(&MutationInput::create("User", json!({})));
1654 assert!(create_result.success);
1655
1656 let delete_result = registry.execute(&MutationInput::delete("User", "1"));
1657 assert!(delete_result.success);
1658 }
1659
1660 #[test]
1663 fn test_in_memory_store_create() {
1664 let store = InMemoryStore::new();
1665 let result = store.create("User", json!({"name": "Alice"}));
1666 assert!(result.success);
1667 let data = result.data.unwrap();
1668 assert_eq!(data["name"], "Alice");
1669 assert!(data["id"].is_string());
1670 let all = store.get_all("User");
1672 assert_eq!(all.len(), 1);
1673 }
1674
1675 #[test]
1676 fn test_in_memory_store_create_multiple() {
1677 let store = InMemoryStore::new();
1678 store.create("User", json!({"name": "Alice"}));
1679 store.create("User", json!({"name": "Bob"}));
1680 let all = store.get_all("User");
1681 assert_eq!(all.len(), 2);
1682 assert_eq!(all[0]["id"], "1");
1684 assert_eq!(all[1]["id"], "2");
1685 }
1686
1687 #[test]
1688 fn test_in_memory_store_update() {
1689 let store = InMemoryStore::new();
1690 store.create("User", json!({"name": "Alice"}));
1691 let result = store.update("User", "1", &json!({"name": "Alicia"}));
1692 assert!(result.success);
1693 let updated = store.find_by_id("User", "1").unwrap();
1694 assert_eq!(updated["name"], "Alicia");
1695 }
1696
1697 #[test]
1698 fn test_in_memory_store_update_missing() {
1699 let store = InMemoryStore::new();
1700 let result = store.update("User", "999", &json!({"name": "X"}));
1701 assert!(!result.success);
1702 assert!(result.error.unwrap().contains("not found"));
1703 }
1704
1705 #[test]
1706 fn test_in_memory_store_delete() {
1707 let store = InMemoryStore::new();
1708 store.create("User", json!({"name": "Alice"}));
1709 let result = store.delete("User", "1");
1710 assert!(result.success);
1711 assert_eq!(result.affected, 1);
1712 assert!(store.find_by_id("User", "1").is_none());
1713 }
1714
1715 #[test]
1716 fn test_in_memory_store_delete_missing() {
1717 let store = InMemoryStore::new();
1718 let result = store.delete("User", "999");
1719 assert!(!result.success);
1720 }
1721
1722 #[test]
1723 fn test_in_memory_store_find_by_id_missing_type() {
1724 let store = InMemoryStore::new();
1725 assert!(store.find_by_id("Nonexistent", "1").is_none());
1726 }
1727
1728 #[test]
1731 fn test_subscription_broker_subscribe_and_publish() {
1732 let broker = SubscriptionBroker::new();
1733 let handle = broker.subscribe("userCreated");
1734
1735 assert_eq!(broker.subscriber_count("userCreated"), 1);
1736 let delivered = broker.publish("userCreated", json!({"id": "1", "name": "Alice"}));
1737 assert_eq!(delivered, 1);
1738
1739 let event = handle.try_recv();
1740 assert!(event.is_some());
1741 let event = event.unwrap();
1742 assert_eq!(event.topic, "userCreated");
1743 assert_eq!(event.payload["name"], "Alice");
1744 assert!(event.sequence > 0);
1745 }
1746
1747 #[test]
1748 fn test_subscription_broker_multiple_subscribers() {
1749 let broker = SubscriptionBroker::new();
1750 let handle1 = broker.subscribe("topic1");
1751 let handle2 = broker.subscribe("topic1");
1752
1753 assert_eq!(broker.subscriber_count("topic1"), 2);
1754 let delivered = broker.publish("topic1", json!({"msg": "hello"}));
1755 assert_eq!(delivered, 2);
1756
1757 assert!(handle1.try_recv().is_some());
1758 assert!(handle2.try_recv().is_some());
1759 }
1760
1761 #[test]
1762 fn test_subscription_broker_no_subscribers() {
1763 let broker = SubscriptionBroker::new();
1764 let delivered = broker.publish("noSubs", json!({"msg": "hello"}));
1765 assert_eq!(delivered, 0);
1766 }
1767
1768 #[test]
1769 fn test_subscription_broker_unsubscribe_on_drop() {
1770 let broker = SubscriptionBroker::new();
1771 {
1772 let _handle = broker.subscribe("tempTopic");
1773 assert_eq!(broker.subscriber_count("tempTopic"), 1);
1774 } assert_eq!(broker.subscriber_count("tempTopic"), 0);
1777 }
1778
1779 #[test]
1780 fn test_subscription_broker_different_topics() {
1781 let broker = SubscriptionBroker::new();
1782 let handle1 = broker.subscribe("topicA");
1783 let handle2 = broker.subscribe("topicB");
1784
1785 broker.publish("topicA", json!({"a": 1}));
1786 broker.publish("topicB", json!({"b": 2}));
1787
1788 let event1 = handle1.try_recv().unwrap();
1789 assert_eq!(event1.payload["a"], 1);
1790
1791 let event2 = handle2.try_recv().unwrap();
1792 assert_eq!(event2.payload["b"], 2);
1793
1794 assert!(handle1.try_recv().is_none());
1796 assert!(handle2.try_recv().is_none());
1797 }
1798
1799 #[test]
1800 fn test_subscription_broker_sequence_increments() {
1801 let broker = SubscriptionBroker::new();
1802 let handle = broker.subscribe("seq");
1803
1804 broker.publish("seq", json!({}));
1805 broker.publish("seq", json!({}));
1806 broker.publish("seq", json!({}));
1807
1808 let e1 = handle.try_recv().unwrap();
1809 let e2 = handle.try_recv().unwrap();
1810 let e3 = handle.try_recv().unwrap();
1811
1812 assert!(e2.sequence > e1.sequence);
1813 assert!(e3.sequence > e2.sequence);
1814 }
1815
1816 #[test]
1817 fn test_subscription_broker_clone_shares_state() {
1818 let broker = SubscriptionBroker::new();
1819 let broker2 = broker.clone();
1820 let handle = broker2.subscribe("shared");
1821
1822 broker.publish("shared", json!({"x": 1}));
1823 assert!(handle.try_recv().is_some());
1824 }
1825
1826 #[test]
1827 fn test_subscription_handle_recv_blocking() {
1828 let broker = SubscriptionBroker::new();
1829 let handle = broker.subscribe("block");
1830
1831 let b = broker.clone();
1833 let thread = std::thread::spawn(move || {
1834 std::thread::sleep(std::time::Duration::from_millis(10));
1835 b.publish("block", json!({"delayed": true}));
1836 });
1837
1838 let event = handle.recv();
1839 assert!(event.is_some());
1840 assert_eq!(event.unwrap().payload["delayed"], true);
1841 thread.join().unwrap();
1842 }
1843
1844 #[test]
1845 fn test_subscription_broker_topic_count() {
1846 let broker = SubscriptionBroker::new();
1847 let _h1 = broker.subscribe("t1");
1848 let _h2 = broker.subscribe("t2");
1849 let _h3 = broker.subscribe("t3");
1850 assert_eq!(broker.topic_count(), 3);
1851 }
1852
1853 #[test]
1856 fn test_schema_extensions_new() {
1857 let ext = SchemaExtensions::new();
1858 assert!(ext.relations.is_empty());
1859 assert!(ext.mutations.is_empty());
1860 assert!(ext.subscriptions.is_empty());
1861 }
1862
1863 #[test]
1864 fn test_schema_extensions_builder() {
1865 let ext = SchemaExtensions::new()
1866 .with_relation(Relation::one_to_many("userOrders", "User", "Order"))
1867 .with_mutation("User", MutationKind::Create)
1868 .with_mutation("User", MutationKind::Delete)
1869 .with_subscription("userCreated");
1870
1871 assert_eq!(ext.relations.len(), 1);
1872 assert_eq!(ext.mutations.len(), 2);
1873 assert_eq!(ext.subscriptions.len(), 1);
1874 }
1875
1876 #[test]
1877 fn test_schema_extensions_to_sdl_contains_mutation() {
1878 let ext = SchemaExtensions::new()
1879 .with_mutation("User", MutationKind::Create)
1880 .with_mutation("User", MutationKind::Delete);
1881
1882 let sdl = ext.to_sdl();
1883 assert!(sdl.contains("type Mutation {"));
1884 assert!(sdl.contains("createUser"));
1885 assert!(sdl.contains("deleteUser"));
1886 }
1887
1888 #[test]
1889 fn test_schema_extensions_to_sdl_contains_subscription() {
1890 let ext = SchemaExtensions::new().with_subscription("userCreated");
1891 let sdl = ext.to_sdl();
1892 assert!(sdl.contains("type Subscription {"));
1893 assert!(sdl.contains("userCreated"));
1894 assert!(sdl.contains("SubscriptionEvent"));
1895 }
1896
1897 #[test]
1898 fn test_schema_extensions_to_sdl_contains_relation() {
1899 let ext = SchemaExtensions::new().with_relation(Relation::one_to_many(
1900 "userOrders",
1901 "User",
1902 "Order",
1903 ));
1904 let sdl = ext.to_sdl();
1905 assert!(sdl.contains("# Relations"));
1906 assert!(sdl.contains("userOrders"));
1907 }
1908
1909 #[test]
1910 fn test_schema_extensions_to_sdl_empty() {
1911 let ext = SchemaExtensions::new();
1912 let sdl = ext.to_sdl();
1913 assert!(sdl.is_empty());
1914 }
1915}