1use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
10pub struct Relations {
11 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub any: Option<Vec<Relation>>,
14
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub all: Option<Vec<Relation>>,
18
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub none: Option<Vec<Relation>>,
22}
23
24impl Relations {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn any(mut self, relation: Relation) -> Self {
32 self.any.get_or_insert_with(Vec::new).push(relation);
33 self
34 }
35
36 pub fn all(mut self, relation: Relation) -> Self {
38 self.all.get_or_insert_with(Vec::new).push(relation);
39 self
40 }
41
42 pub fn none(mut self, relation: Relation) -> Self {
44 self.none.get_or_insert_with(Vec::new).push(relation);
45 self
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.any.is_none() && self.all.is_none() && self.none.is_none()
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
56pub struct Relation {
57 pub kind: RelationKind,
59
60 pub target: RelationTarget,
62
63 #[serde(default)]
65 pub transitive: bool,
66
67 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub max_depth: Option<u32>,
70
71 #[serde(default)]
73 pub scope: TraversalScope,
74
75 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub r#as: Option<String>,
78}
79
80impl Relation {
81 pub fn new(kind: RelationKind, target: RelationTarget) -> Self {
83 Self {
84 kind,
85 target,
86 transitive: false,
87 max_depth: None,
88 scope: TraversalScope::default(),
89 r#as: None,
90 }
91 }
92
93 pub fn transitive(mut self) -> Self {
95 self.transitive = true;
96 self
97 }
98
99 pub fn with_max_depth(mut self, depth: u32) -> Self {
101 self.max_depth = Some(depth);
102 self
103 }
104
105 pub fn with_scope(mut self, scope: TraversalScope) -> Self {
107 self.scope = scope;
108 self
109 }
110
111 pub fn bind(mut self, var: impl Into<String>) -> Self {
113 self.r#as = Some(var.into());
114 self
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
120pub struct RelationTarget {
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub kind: Option<TargetKind>,
124
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub r#match: Option<TargetMatch>,
128}
129
130impl RelationTarget {
131 pub fn new() -> Self {
133 Self {
134 kind: None,
135 r#match: None,
136 }
137 }
138
139 pub fn kind(mut self, kind: TargetKind) -> Self {
141 self.kind = Some(kind);
142 self
143 }
144
145 pub fn name(mut self, name: impl Into<String>) -> Self {
147 self.r#match = Some(TargetMatch {
148 name: Some(name.into()),
149 ..Default::default()
150 });
151 self
152 }
153}
154
155impl Default for RelationTarget {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
163pub enum TargetKind {
164 Function,
166 Struct,
168 Enum,
170 Trait,
172 Impl,
174 Mod,
176 Const,
178 Static,
180 TypeAlias,
182}
183
184#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
186pub struct TargetMatch {
187 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub name: Option<String>,
190
191 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub pattern: Option<String>,
194
195 #[serde(default, skip_serializing_if = "Option::is_none")]
197 pub regex: Option<String>,
198
199 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub attributes: Option<Vec<String>>,
202}
203
204#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
206pub enum TraversalScope {
207 #[default]
209 Workspace,
210 All,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
223pub enum RelationKind {
224 Calls,
226 CalledBy,
228 TypeReferences,
230 TypeReferencedBy,
232 Implements,
234 ImplementedBy,
236 Contains,
238 ContainedBy,
240}
241
242impl RelationKind {
243 pub fn reverse(&self) -> Self {
245 match self {
246 Self::Calls => Self::CalledBy,
247 Self::CalledBy => Self::Calls,
248 Self::TypeReferences => Self::TypeReferencedBy,
249 Self::TypeReferencedBy => Self::TypeReferences,
250 Self::Implements => Self::ImplementedBy,
251 Self::ImplementedBy => Self::Implements,
252 Self::Contains => Self::ContainedBy,
253 Self::ContainedBy => Self::Contains,
254 }
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_relations_builder() {
264 let rels = Relations::new()
265 .any(Relation::new(
266 RelationKind::Calls,
267 RelationTarget::new()
268 .kind(TargetKind::Function)
269 .name("unwrap"),
270 ))
271 .none(Relation::new(
272 RelationKind::TypeReferences,
273 RelationTarget::new()
274 .kind(TargetKind::Struct)
275 .name("Database"),
276 ));
277
278 assert!(rels.any.is_some());
279 assert!(rels.none.is_some());
280 assert!(rels.all.is_none());
281 }
282
283 #[test]
284 fn test_relation_transitive() {
285 let rel = Relation::new(RelationKind::CalledBy, RelationTarget::new())
286 .transitive()
287 .with_max_depth(10)
288 .with_scope(TraversalScope::Workspace);
289
290 assert!(rel.transitive);
291 assert_eq!(rel.max_depth, Some(10));
292 assert_eq!(rel.scope, TraversalScope::Workspace);
293 }
294
295 #[test]
296 fn test_relation_kind_reverse() {
297 assert_eq!(RelationKind::Calls.reverse(), RelationKind::CalledBy);
298 assert_eq!(
299 RelationKind::TypeReferences.reverse(),
300 RelationKind::TypeReferencedBy
301 );
302 assert_eq!(
303 RelationKind::Implements.reverse(),
304 RelationKind::ImplementedBy
305 );
306 }
307}