sqry_core/relations/
identity.rs1use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum CallIdentityKind {
7 Instance,
9 Singleton,
11 SingletonClass,
13}
14
15impl Default for CallIdentityKind {
16 fn default() -> Self {
17 Self::Instance
18 }
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
23pub struct CallIdentityMetadata {
24 pub qualified: String,
26 pub simple: String,
28 pub namespace: Vec<String>,
30 pub method_kind: CallIdentityKind,
32 #[serde(default)]
34 pub receiver: Option<String>,
35}
36
37#[derive(Debug, Default)]
39pub struct CallIdentityBuilder {
40 namespace: Vec<String>,
41 simple: String,
42 kind: CallIdentityKind,
43 receiver: Option<String>,
44}
45
46impl CallIdentityBuilder {
47 #[must_use]
49 pub fn new(simple: impl Into<String>, kind: CallIdentityKind) -> Self {
50 Self {
51 namespace: Vec::new(),
52 simple: simple.into(),
53 kind,
54 receiver: None,
55 }
56 }
57
58 #[must_use]
60 pub fn with_namespace<I, S>(mut self, segments: I) -> Self
61 where
62 I: IntoIterator<Item = S>,
63 S: Into<String>,
64 {
65 self.namespace = segments.into_iter().map(Into::into).collect();
66 self
67 }
68
69 #[must_use]
71 pub fn push_namespace(mut self, segment: impl Into<String>) -> Self {
72 self.namespace.push(segment.into());
73 self
74 }
75
76 #[must_use]
78 pub fn with_receiver(mut self, receiver: impl Into<String>) -> Self {
79 self.receiver = Some(receiver.into());
80 self
81 }
82
83 #[must_use]
85 pub fn build(self) -> CallIdentityMetadata {
86 let qualified = build_qualified_name(&self.namespace, &self.simple, self.kind);
87 CallIdentityMetadata {
88 qualified,
89 simple: self.simple,
90 namespace: self.namespace,
91 method_kind: self.kind,
92 receiver: self.receiver,
93 }
94 }
95}
96
97fn build_qualified_name(namespace: &[String], simple: &str, kind: CallIdentityKind) -> String {
98 if namespace.is_empty() {
99 return simple.to_string();
100 }
101
102 let mut qualified = namespace.join("::");
103 qualified.push(method_separator(kind));
104 qualified.push_str(simple);
105 qualified
106}
107
108fn method_separator(kind: CallIdentityKind) -> char {
109 match kind {
110 CallIdentityKind::Instance => '#',
111 CallIdentityKind::Singleton | CallIdentityKind::SingletonClass => '.',
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::{CallIdentityBuilder, CallIdentityKind, build_qualified_name};
118
119 #[test]
120 fn qualified_name_for_instance_method() {
121 let namespace = vec![
122 "Admin".to_string(),
123 "Users".to_string(),
124 "Controller".to_string(),
125 ];
126 let qualified = build_qualified_name(&namespace, "show", CallIdentityKind::Instance);
127 assert_eq!(qualified, "Admin::Users::Controller#show");
128 }
129
130 #[test]
131 fn qualified_name_for_singleton_method() {
132 let namespace = vec!["User".to_string()];
133 let qualified =
134 build_qualified_name(&namespace, "authenticate", CallIdentityKind::Singleton);
135 assert_eq!(qualified, "User.authenticate");
136 }
137
138 #[test]
139 fn builder_emits_metadata() {
140 let metadata = CallIdentityBuilder::new("find", CallIdentityKind::Singleton)
141 .with_namespace(["User"])
142 .with_receiver("self")
143 .build();
144
145 assert_eq!(metadata.qualified, "User.find");
146 assert_eq!(metadata.simple, "find");
147 assert_eq!(metadata.namespace, vec!["User".to_string()]);
148 assert_eq!(metadata.method_kind, CallIdentityKind::Singleton);
149 assert_eq!(metadata.receiver.as_deref(), Some("self"));
150 }
151
152 #[test]
153 fn qualified_name_for_global_scope() {
154 let qualified = build_qualified_name(&[], "main", CallIdentityKind::Instance);
155 assert_eq!(qualified, "main");
156 }
157}