1use std::fmt;
16
17use serde::{Deserialize, Serialize};
18
19use super::super::string::StringId;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25#[serde(rename_all = "snake_case")]
26pub enum TypeOfContext {
27 Parameter,
29 Return,
31 Field,
33 Variable,
35 TypeParameter,
37 Constraint,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44#[derive(Default)]
45pub enum FfiConvention {
46 #[default]
48 C,
49 Cdecl,
51 Stdcall,
53 Fastcall,
55 System,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[serde(rename_all = "UPPERCASE")]
62#[derive(Default)]
63pub enum HttpMethod {
64 #[default]
66 Get,
67 Post,
69 Put,
71 Delete,
73 Patch,
75 Head,
77 Options,
79 All,
81}
82
83impl HttpMethod {
84 #[must_use]
86 pub const fn as_str(self) -> &'static str {
87 match self {
88 Self::Get => "GET",
89 Self::Post => "POST",
90 Self::Put => "PUT",
91 Self::Delete => "DELETE",
92 Self::Patch => "PATCH",
93 Self::Head => "HEAD",
94 Self::Options => "OPTIONS",
95 Self::All => "ALL",
96 }
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103#[derive(Default)]
104pub enum DbQueryType {
105 #[default]
107 Select,
108 Insert,
110 Update,
112 Delete,
114 Execute,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
120#[serde(rename_all = "snake_case")]
121pub enum TableWriteOp {
122 Insert,
124 Update,
126 Delete,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133#[derive(Default)]
134pub enum ExportKind {
135 #[default]
137 Direct,
138 Reexport,
140 Default,
142 Namespace,
144}
145
146#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
148#[serde(rename_all = "snake_case")]
149#[derive(Default)]
150pub enum MqProtocol {
151 #[default]
153 Kafka,
154 Sqs,
156 RabbitMq,
158 Nats,
160 Redis,
162 Other(StringId),
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
170#[serde(rename_all = "snake_case")]
171#[derive(Default)]
172pub enum LifetimeConstraintKind {
173 #[default]
175 Outlives,
176 TypeBound,
178 Reference,
180 Static,
182 HigherRanked,
184 TraitObject,
186 ImplTrait,
188 Elided,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
194#[serde(rename_all = "snake_case")]
195#[derive(Default)]
196pub enum MacroExpansionKind {
197 Derive,
199 Attribute,
201 #[default]
203 Declarative,
204 Function,
206 CfgGate,
208}
209
210#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
219#[serde(rename_all = "snake_case")]
220pub enum EdgeKind {
221 Defines,
224
225 Contains,
227
228 Calls {
231 argument_count: u8,
233 is_async: bool,
237 },
238
239 References,
241
242 Imports {
244 alias: Option<StringId>,
246 is_wildcard: bool,
248 },
249
250 Exports {
252 kind: ExportKind,
254 alias: Option<StringId>,
256 },
257
258 TypeOf {
269 context: Option<TypeOfContext>,
271 index: Option<u16>,
273 name: Option<StringId>,
275 },
276
277 Inherits,
280
281 Implements,
283
284 LifetimeConstraint {
293 constraint_kind: LifetimeConstraintKind,
295 },
296
297 TraitMethodBinding {
306 trait_name: StringId,
308 impl_type: StringId,
310 is_ambiguous: bool,
312 },
313
314 MacroExpansion {
321 expansion_kind: MacroExpansionKind,
323 is_verified: bool,
325 },
326
327 FfiCall {
330 convention: FfiConvention,
332 },
333
334 HttpRequest {
336 method: HttpMethod,
338 url: Option<StringId>,
340 },
341
342 GrpcCall {
344 service: StringId,
346 method: StringId,
348 },
349
350 WebAssemblyCall,
352
353 DbQuery {
355 query_type: DbQueryType,
357 table: Option<StringId>,
359 },
360
361 TableRead {
363 table_name: StringId,
365 schema: Option<StringId>,
367 },
368
369 TableWrite {
371 table_name: StringId,
373 schema: Option<StringId>,
375 operation: TableWriteOp,
377 },
378
379 TriggeredBy {
381 trigger_name: StringId,
383 schema: Option<StringId>,
385 },
386
387 MessageQueue {
390 protocol: MqProtocol,
392 topic: Option<StringId>,
394 },
395
396 WebSocket {
398 event: Option<StringId>,
400 },
401
402 GraphQLOperation {
404 operation: StringId,
406 },
407
408 ProcessExec {
410 command: StringId,
412 },
413
414 FileIpc {
416 path_pattern: Option<StringId>,
418 },
419
420 ProtocolCall {
423 protocol: StringId,
425 metadata: Option<StringId>,
427 },
428}
429
430impl EdgeKind {
431 #[inline]
433 #[must_use]
434 pub const fn is_call(&self) -> bool {
435 matches!(
436 self,
437 Self::Calls { .. }
438 | Self::FfiCall { .. }
439 | Self::HttpRequest { .. }
440 | Self::GrpcCall { .. }
441 | Self::WebAssemblyCall
442 )
443 }
444
445 #[inline]
447 #[must_use]
448 pub const fn is_structural(&self) -> bool {
449 matches!(self, Self::Defines | Self::Contains)
450 }
451
452 #[inline]
454 #[must_use]
455 pub const fn is_type_relation(&self) -> bool {
456 matches!(
457 self,
458 Self::Inherits | Self::Implements | Self::TypeOf { .. }
459 )
460 }
461
462 #[inline]
464 #[must_use]
465 pub const fn is_cross_boundary(&self) -> bool {
466 matches!(
467 self,
468 Self::FfiCall { .. }
469 | Self::HttpRequest { .. }
470 | Self::GrpcCall { .. }
471 | Self::WebAssemblyCall
472 | Self::DbQuery { .. }
473 | Self::TableRead { .. }
474 | Self::TableWrite { .. }
475 | Self::TriggeredBy { .. }
476 | Self::MessageQueue { .. }
477 | Self::WebSocket { .. }
478 | Self::GraphQLOperation { .. }
479 | Self::ProcessExec { .. }
480 | Self::FileIpc { .. }
481 | Self::ProtocolCall { .. }
482 )
483 }
484
485 #[inline]
487 #[must_use]
488 pub const fn is_async(&self) -> bool {
489 matches!(
490 self,
491 Self::MessageQueue { .. } | Self::WebSocket { .. } | Self::GraphQLOperation { .. }
492 )
493 }
494
495 #[inline]
500 #[must_use]
501 pub const fn is_rust_specific(&self) -> bool {
502 matches!(
503 self,
504 Self::LifetimeConstraint { .. }
505 | Self::TraitMethodBinding { .. }
506 | Self::MacroExpansion { .. }
507 )
508 }
509
510 #[inline]
512 #[must_use]
513 pub const fn is_lifetime_constraint(&self) -> bool {
514 matches!(self, Self::LifetimeConstraint { .. })
515 }
516
517 #[inline]
519 #[must_use]
520 pub const fn is_trait_method_binding(&self) -> bool {
521 matches!(self, Self::TraitMethodBinding { .. })
522 }
523
524 #[inline]
526 #[must_use]
527 pub const fn is_macro_expansion(&self) -> bool {
528 matches!(self, Self::MacroExpansion { .. })
529 }
530
531 #[must_use]
533 pub const fn tag(&self) -> &'static str {
534 match self {
535 Self::Defines => "defines",
536 Self::Contains => "contains",
537 Self::Calls { .. } => "calls",
538 Self::References => "references",
539 Self::Imports { .. } => "imports",
540 Self::Exports { .. } => "exports",
541 Self::TypeOf { .. } => "type_of",
542 Self::Inherits => "inherits",
543 Self::Implements => "implements",
544 Self::LifetimeConstraint { .. } => "lifetime_constraint",
545 Self::TraitMethodBinding { .. } => "trait_method_binding",
546 Self::MacroExpansion { .. } => "macro_expansion",
547 Self::FfiCall { .. } => "ffi_call",
548 Self::HttpRequest { .. } => "http_request",
549 Self::GrpcCall { .. } => "grpc_call",
550 Self::WebAssemblyCall => "web_assembly_call",
551 Self::DbQuery { .. } => "db_query",
552 Self::TableRead { .. } => "table_read",
553 Self::TableWrite { .. } => "table_write",
554 Self::TriggeredBy { .. } => "triggered_by",
555 Self::MessageQueue { .. } => "message_queue",
556 Self::WebSocket { .. } => "web_socket",
557 Self::GraphQLOperation { .. } => "graphql_operation",
558 Self::ProcessExec { .. } => "process_exec",
559 Self::FileIpc { .. } => "file_ipc",
560 Self::ProtocolCall { .. } => "protocol_call",
561 }
562 }
563
564 #[must_use]
569 pub const fn estimated_size(&self) -> usize {
570 match self {
575 Self::Defines
577 | Self::Contains
578 | Self::References
579 | Self::Inherits
580 | Self::Implements
581 | Self::WebAssemblyCall => 1,
582
583 Self::Calls { .. } | Self::MacroExpansion { .. } => 3,
586
587 Self::Imports { .. } | Self::Exports { .. } | Self::DbQuery { .. } => 7,
590
591 Self::FfiCall { .. } | Self::LifetimeConstraint { .. } => 2,
594
595 Self::TraitMethodBinding { .. }
598 | Self::TableRead { .. }
599 | Self::TriggeredBy { .. }
600 | Self::ProtocolCall { .. } => 10,
601
602 Self::HttpRequest { .. } | Self::WebSocket { .. } | Self::FileIpc { .. } => 6,
605
606 Self::GrpcCall { .. } => 9,
608
609 Self::TableWrite { .. } | Self::MessageQueue { .. } | Self::TypeOf { .. } => 11,
612
613 Self::GraphQLOperation { .. } | Self::ProcessExec { .. } => 5,
615 }
616 }
617}
618
619impl fmt::Display for EdgeKind {
620 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
621 f.write_str(self.tag())
622 }
623}
624
625impl Default for EdgeKind {
626 fn default() -> Self {
628 Self::Calls {
629 argument_count: 0,
630 is_async: false,
631 }
632 }
633}
634
635#[cfg(test)]
636mod tests {
637 use super::*;
638
639 fn calls() -> EdgeKind {
641 EdgeKind::Calls {
642 argument_count: 0,
643 is_async: false,
644 }
645 }
646
647 fn imports() -> EdgeKind {
649 EdgeKind::Imports {
650 alias: None,
651 is_wildcard: false,
652 }
653 }
654
655 fn exports() -> EdgeKind {
657 EdgeKind::Exports {
658 kind: ExportKind::Direct,
659 alias: None,
660 }
661 }
662
663 #[test]
664 fn test_edge_kind_tag() {
665 assert_eq!(calls().tag(), "calls");
666 assert_eq!(imports().tag(), "imports");
667 assert_eq!(exports().tag(), "exports");
668 assert_eq!(EdgeKind::Defines.tag(), "defines");
669 assert_eq!(
670 EdgeKind::HttpRequest {
671 method: HttpMethod::Get,
672 url: None
673 }
674 .tag(),
675 "http_request"
676 );
677 }
678
679 #[test]
680 fn test_edge_kind_display() {
681 assert_eq!(format!("{}", calls()), "calls");
682 assert_eq!(format!("{}", imports()), "imports");
683 assert_eq!(format!("{}", exports()), "exports");
684 assert_eq!(format!("{}", EdgeKind::Inherits), "inherits");
685 }
686
687 #[test]
688 fn test_is_call() {
689 assert!(calls().is_call());
690 assert!(
691 EdgeKind::Calls {
692 argument_count: 5,
693 is_async: true
694 }
695 .is_call()
696 );
697 assert!(
698 EdgeKind::FfiCall {
699 convention: FfiConvention::C
700 }
701 .is_call()
702 );
703 assert!(
704 EdgeKind::HttpRequest {
705 method: HttpMethod::Post,
706 url: None
707 }
708 .is_call()
709 );
710 assert!(!EdgeKind::Defines.is_call());
711 assert!(!EdgeKind::Inherits.is_call());
712 assert!(!imports().is_call());
713 assert!(!exports().is_call());
714 }
715
716 #[test]
717 fn test_is_structural() {
718 assert!(EdgeKind::Defines.is_structural());
719 assert!(EdgeKind::Contains.is_structural());
720 assert!(!calls().is_structural());
721 assert!(!imports().is_structural());
722 assert!(!exports().is_structural());
723 }
724
725 #[test]
726 fn test_is_type_relation() {
727 assert!(EdgeKind::Inherits.is_type_relation());
728 assert!(EdgeKind::Implements.is_type_relation());
729 assert!(
730 EdgeKind::TypeOf {
731 context: None,
732 index: None,
733 name: None,
734 }
735 .is_type_relation()
736 );
737 assert!(!calls().is_type_relation());
738 }
739
740 #[test]
741 fn test_is_cross_boundary() {
742 assert!(
743 EdgeKind::FfiCall {
744 convention: FfiConvention::C
745 }
746 .is_cross_boundary()
747 );
748 assert!(
749 EdgeKind::HttpRequest {
750 method: HttpMethod::Get,
751 url: None
752 }
753 .is_cross_boundary()
754 );
755 assert!(
756 EdgeKind::GrpcCall {
757 service: StringId::INVALID,
758 method: StringId::INVALID
759 }
760 .is_cross_boundary()
761 );
762 assert!(!calls().is_cross_boundary());
763 assert!(!imports().is_cross_boundary());
764 assert!(!exports().is_cross_boundary());
765 }
766
767 #[test]
768 fn test_is_async() {
769 assert!(
770 EdgeKind::MessageQueue {
771 protocol: MqProtocol::Kafka,
772 topic: None
773 }
774 .is_async()
775 );
776 assert!(EdgeKind::WebSocket { event: None }.is_async());
777 assert!(!calls().is_async());
778 assert!(
781 !EdgeKind::Calls {
782 argument_count: 0,
783 is_async: true
784 }
785 .is_async()
786 );
787 }
788
789 #[test]
790 fn test_default() {
791 assert_eq!(EdgeKind::default(), calls());
792 assert_eq!(HttpMethod::default(), HttpMethod::Get);
793 assert_eq!(FfiConvention::default(), FfiConvention::C);
794 assert_eq!(DbQueryType::default(), DbQueryType::Select);
795 assert_eq!(ExportKind::default(), ExportKind::Direct);
796 }
797
798 #[test]
799 fn test_http_method_as_str() {
800 assert_eq!(HttpMethod::Get.as_str(), "GET");
801 assert_eq!(HttpMethod::Post.as_str(), "POST");
802 assert_eq!(HttpMethod::Delete.as_str(), "DELETE");
803 assert_eq!(HttpMethod::All.as_str(), "ALL");
804 }
805
806 #[test]
807 fn test_calls_with_metadata() {
808 let sync_call = EdgeKind::Calls {
809 argument_count: 3,
810 is_async: false,
811 };
812 let async_call = EdgeKind::Calls {
813 argument_count: 0,
814 is_async: true,
815 };
816 assert_eq!(sync_call.tag(), "calls");
817 assert_eq!(async_call.tag(), "calls");
818 assert!(sync_call.is_call());
819 assert!(async_call.is_call());
820 assert_ne!(sync_call, async_call);
821 }
822
823 #[test]
824 fn test_imports_with_metadata() {
825 let simple = imports();
826 let aliased = EdgeKind::Imports {
827 alias: Some(StringId::new(42)),
828 is_wildcard: false,
829 };
830 let wildcard = EdgeKind::Imports {
831 alias: None,
832 is_wildcard: true,
833 };
834
835 assert_eq!(simple.tag(), "imports");
836 assert_eq!(aliased.tag(), "imports");
837 assert_eq!(wildcard.tag(), "imports");
838 assert_ne!(simple, aliased);
839 assert_ne!(simple, wildcard);
840 }
841
842 #[test]
843 fn test_exports_with_metadata() {
844 let direct = exports();
845 let reexport = EdgeKind::Exports {
846 kind: ExportKind::Reexport,
847 alias: None,
848 };
849 let default_export = EdgeKind::Exports {
850 kind: ExportKind::Default,
851 alias: None,
852 };
853 let namespace = EdgeKind::Exports {
854 kind: ExportKind::Namespace,
855 alias: Some(StringId::new(1)),
856 };
857
858 assert_eq!(direct.tag(), "exports");
859 assert_eq!(reexport.tag(), "exports");
860 assert_eq!(default_export.tag(), "exports");
861 assert_eq!(namespace.tag(), "exports");
862 assert_ne!(direct, reexport);
863 assert_ne!(direct, default_export);
864 }
865
866 #[test]
867 fn test_serde_calls_imports_exports() {
868 let calls = EdgeKind::Calls {
870 argument_count: 5,
871 is_async: true,
872 };
873 let json = serde_json::to_string(&calls).unwrap();
874 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
875 assert_eq!(calls, deserialized);
876 assert!(json.contains("\"calls\""));
877 assert!(json.contains("\"argument_count\":5"));
878 assert!(json.contains("\"is_async\":true"));
879
880 let imports = EdgeKind::Imports {
882 alias: Some(StringId::new(10)),
883 is_wildcard: false,
884 };
885 let json = serde_json::to_string(&imports).unwrap();
886 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
887 assert_eq!(imports, deserialized);
888
889 let exports = EdgeKind::Exports {
891 kind: ExportKind::Reexport,
892 alias: None,
893 };
894 let json = serde_json::to_string(&exports).unwrap();
895 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
896 assert_eq!(exports, deserialized);
897 }
898
899 #[test]
900 fn test_serde_complex_variants() {
901 let http = EdgeKind::HttpRequest {
903 method: HttpMethod::Post,
904 url: None,
905 };
906 let json = serde_json::to_string(&http).unwrap();
907 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
908 assert_eq!(http, deserialized);
909
910 let grpc = EdgeKind::GrpcCall {
912 service: StringId::new(1),
913 method: StringId::new(2),
914 };
915 let json = serde_json::to_string(&grpc).unwrap();
916 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
917 assert_eq!(grpc, deserialized);
918 }
919
920 #[test]
921 fn test_postcard_roundtrip_simple_enums() {
922 for conv in [
926 FfiConvention::C,
927 FfiConvention::Cdecl,
928 FfiConvention::Stdcall,
929 ] {
930 let bytes = postcard::to_allocvec(&conv).unwrap();
931 let deserialized: FfiConvention = postcard::from_bytes(&bytes).unwrap();
932 assert_eq!(conv, deserialized);
933 }
934
935 for method in [
937 HttpMethod::Get,
938 HttpMethod::Post,
939 HttpMethod::Delete,
940 HttpMethod::All,
941 ] {
942 let bytes = postcard::to_allocvec(&method).unwrap();
943 let deserialized: HttpMethod = postcard::from_bytes(&bytes).unwrap();
944 assert_eq!(method, deserialized);
945 }
946
947 for query in [
949 DbQueryType::Select,
950 DbQueryType::Insert,
951 DbQueryType::Update,
952 ] {
953 let bytes = postcard::to_allocvec(&query).unwrap();
954 let deserialized: DbQueryType = postcard::from_bytes(&bytes).unwrap();
955 assert_eq!(query, deserialized);
956 }
957
958 for kind in [
960 ExportKind::Direct,
961 ExportKind::Reexport,
962 ExportKind::Default,
963 ExportKind::Namespace,
964 ] {
965 let bytes = postcard::to_allocvec(&kind).unwrap();
966 let deserialized: ExportKind = postcard::from_bytes(&bytes).unwrap();
967 assert_eq!(kind, deserialized);
968 }
969 }
970
971 #[test]
972 fn test_edge_kind_json_compatibility() {
973 let kinds = [
976 calls(),
977 imports(),
978 exports(),
979 EdgeKind::Defines,
980 EdgeKind::HttpRequest {
981 method: HttpMethod::Get,
982 url: None,
983 },
984 EdgeKind::MessageQueue {
985 protocol: MqProtocol::Kafka,
986 topic: Some(StringId::new(1)),
987 },
988 ];
989
990 for kind in &kinds {
991 let json = serde_json::to_string(kind).unwrap();
993 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
994 assert_eq!(*kind, deserialized);
995
996 let bytes = postcard::to_allocvec(kind).unwrap();
998 let from_postcard: EdgeKind = postcard::from_bytes(&bytes).unwrap();
999 assert_eq!(*kind, from_postcard);
1000 }
1001 }
1002
1003 #[test]
1004 fn test_hash() {
1005 use std::collections::HashSet;
1006
1007 let mut set = HashSet::new();
1008 set.insert(calls());
1009 set.insert(imports());
1010 set.insert(exports());
1011 set.insert(EdgeKind::Defines);
1012 set.insert(EdgeKind::HttpRequest {
1013 method: HttpMethod::Get,
1014 url: None,
1015 });
1016
1017 assert!(set.contains(&calls()));
1018 assert!(set.contains(&imports()));
1019 assert!(set.contains(&exports()));
1020 assert!(!set.contains(&EdgeKind::Inherits));
1021 assert_eq!(set.len(), 5);
1022 }
1023
1024 #[test]
1025 fn test_ffi_convention_variants() {
1026 let conventions = [
1027 FfiConvention::C,
1028 FfiConvention::Cdecl,
1029 FfiConvention::Stdcall,
1030 FfiConvention::Fastcall,
1031 FfiConvention::System,
1032 ];
1033
1034 for conv in conventions {
1035 let edge = EdgeKind::FfiCall { convention: conv };
1036 assert!(edge.is_call());
1037 assert!(edge.is_cross_boundary());
1038 }
1039 }
1040
1041 #[test]
1042 fn test_mq_protocol_variants() {
1043 let protocols = [
1044 MqProtocol::Kafka,
1045 MqProtocol::Sqs,
1046 MqProtocol::RabbitMq,
1047 MqProtocol::Nats,
1048 MqProtocol::Redis,
1049 MqProtocol::Other(StringId::new(1)),
1050 ];
1051
1052 for proto in protocols {
1053 let edge = EdgeKind::MessageQueue {
1054 protocol: proto.clone(),
1055 topic: None,
1056 };
1057 assert!(edge.is_async());
1058 assert!(edge.is_cross_boundary());
1059 }
1060 }
1061
1062 #[test]
1063 fn test_export_kind_variants() {
1064 let kinds = [
1065 ExportKind::Direct,
1066 ExportKind::Reexport,
1067 ExportKind::Default,
1068 ExportKind::Namespace,
1069 ];
1070
1071 for kind in kinds {
1072 let edge = EdgeKind::Exports { kind, alias: None };
1073 assert_eq!(edge.tag(), "exports");
1074 assert!(!edge.is_call());
1075 assert!(!edge.is_structural());
1076 assert!(!edge.is_cross_boundary());
1077 }
1078 }
1079
1080 #[test]
1081 fn test_estimated_size() {
1082 assert_eq!(EdgeKind::Defines.estimated_size(), 1);
1084 assert_eq!(EdgeKind::Contains.estimated_size(), 1);
1085 assert_eq!(EdgeKind::References.estimated_size(), 1);
1086
1087 assert_eq!(calls().estimated_size(), 3);
1089
1090 assert_eq!(imports().estimated_size(), 7);
1092
1093 assert_eq!(exports().estimated_size(), 7);
1095
1096 assert_eq!(
1098 EdgeKind::LifetimeConstraint {
1099 constraint_kind: LifetimeConstraintKind::Outlives
1100 }
1101 .estimated_size(),
1102 2
1103 );
1104 assert_eq!(
1105 EdgeKind::MacroExpansion {
1106 expansion_kind: MacroExpansionKind::Derive,
1107 is_verified: true
1108 }
1109 .estimated_size(),
1110 3
1111 );
1112 assert_eq!(
1113 EdgeKind::TraitMethodBinding {
1114 trait_name: StringId::INVALID,
1115 impl_type: StringId::INVALID,
1116 is_ambiguous: false
1117 }
1118 .estimated_size(),
1119 10
1120 );
1121 }
1122
1123 #[test]
1126 fn test_lifetime_constraint_kind_variants() {
1127 let kinds = [
1128 LifetimeConstraintKind::Outlives,
1129 LifetimeConstraintKind::TypeBound,
1130 LifetimeConstraintKind::Reference,
1131 LifetimeConstraintKind::Static,
1132 LifetimeConstraintKind::HigherRanked,
1133 LifetimeConstraintKind::TraitObject,
1134 LifetimeConstraintKind::ImplTrait,
1135 LifetimeConstraintKind::Elided,
1136 ];
1137
1138 for constraint_kind in kinds {
1139 let edge = EdgeKind::LifetimeConstraint { constraint_kind };
1140 assert!(edge.is_rust_specific());
1141 assert!(edge.is_lifetime_constraint());
1142 assert!(!edge.is_call());
1143 assert!(!edge.is_structural());
1144 assert_eq!(edge.tag(), "lifetime_constraint");
1145 }
1146 }
1147
1148 #[test]
1149 fn test_macro_expansion_kind_variants() {
1150 let kinds = [
1151 MacroExpansionKind::Derive,
1152 MacroExpansionKind::Attribute,
1153 MacroExpansionKind::Declarative,
1154 MacroExpansionKind::Function,
1155 MacroExpansionKind::CfgGate,
1156 ];
1157
1158 for expansion_kind in kinds {
1159 let edge = EdgeKind::MacroExpansion {
1160 expansion_kind,
1161 is_verified: true,
1162 };
1163 assert!(edge.is_rust_specific());
1164 assert!(edge.is_macro_expansion());
1165 assert!(!edge.is_call());
1166 assert_eq!(edge.tag(), "macro_expansion");
1167 }
1168 }
1169
1170 #[test]
1171 fn test_trait_method_binding() {
1172 let edge = EdgeKind::TraitMethodBinding {
1173 trait_name: StringId::new(1),
1174 impl_type: StringId::new(2),
1175 is_ambiguous: false,
1176 };
1177
1178 assert!(edge.is_rust_specific());
1179 assert!(edge.is_trait_method_binding());
1180 assert!(!edge.is_call());
1181 assert_eq!(edge.tag(), "trait_method_binding");
1182
1183 let ambiguous = EdgeKind::TraitMethodBinding {
1185 trait_name: StringId::new(1),
1186 impl_type: StringId::new(2),
1187 is_ambiguous: true,
1188 };
1189 assert!(ambiguous.is_trait_method_binding());
1190 }
1191
1192 #[test]
1193 fn test_rust_specific_edges_serde() {
1194 let lifetime = EdgeKind::LifetimeConstraint {
1196 constraint_kind: LifetimeConstraintKind::HigherRanked,
1197 };
1198 let json = serde_json::to_string(&lifetime).unwrap();
1199 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
1200 assert_eq!(lifetime, deserialized);
1201
1202 let binding = EdgeKind::TraitMethodBinding {
1204 trait_name: StringId::new(10),
1205 impl_type: StringId::new(20),
1206 is_ambiguous: true,
1207 };
1208 let json = serde_json::to_string(&binding).unwrap();
1209 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
1210 assert_eq!(binding, deserialized);
1211
1212 let expansion = EdgeKind::MacroExpansion {
1214 expansion_kind: MacroExpansionKind::Derive,
1215 is_verified: false,
1216 };
1217 let json = serde_json::to_string(&expansion).unwrap();
1218 let deserialized: EdgeKind = serde_json::from_str(&json).unwrap();
1219 assert_eq!(expansion, deserialized);
1220 }
1221
1222 #[test]
1223 fn test_rust_specific_edges_postcard() {
1224 let edges = [
1225 EdgeKind::LifetimeConstraint {
1226 constraint_kind: LifetimeConstraintKind::Outlives,
1227 },
1228 EdgeKind::TraitMethodBinding {
1229 trait_name: StringId::new(5),
1230 impl_type: StringId::new(6),
1231 is_ambiguous: false,
1232 },
1233 EdgeKind::MacroExpansion {
1234 expansion_kind: MacroExpansionKind::Attribute,
1235 is_verified: true,
1236 },
1237 ];
1238
1239 for edge in edges {
1240 let bytes = postcard::to_allocvec(&edge).unwrap();
1241 let deserialized: EdgeKind = postcard::from_bytes(&bytes).unwrap();
1242 assert_eq!(edge, deserialized);
1243 }
1244 }
1245
1246 #[test]
1247 fn test_lifetime_constraint_kind_defaults() {
1248 assert_eq!(
1249 LifetimeConstraintKind::default(),
1250 LifetimeConstraintKind::Outlives
1251 );
1252 assert_eq!(
1253 MacroExpansionKind::default(),
1254 MacroExpansionKind::Declarative
1255 );
1256 }
1257}