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