1use serde::Serialize;
2
3use crate::model::{SourceRange, Visibility};
4
5#[derive(Debug, Clone, Serialize)]
6pub struct FuncEntry {
7 pub name: String,
8 pub source_range: SourceRange,
9 pub visibility: Option<Visibility>,
10 pub signature: Option<String>,
11 pub docstring: Option<String>,
12 #[serde(rename = "async")]
13 pub is_async: bool,
14}
15
16#[derive(Debug, Clone, Serialize)]
17pub struct ClassEntry {
18 pub name: String,
19 pub source_range: SourceRange,
20 pub visibility: Option<Visibility>,
21 pub signature: Option<String>,
22 pub docstring: Option<String>,
23}
24
25#[derive(Debug, Clone, Serialize)]
26pub struct ObjectEntry {
27 pub name: String,
28 pub source_range: SourceRange,
29 pub visibility: Option<Visibility>,
30 pub signature: Option<String>,
31 pub docstring: Option<String>,
32}
33
34#[derive(Debug, Clone, Serialize)]
35pub struct InspectOutput {
36 pub funcs: Vec<FuncEntry>,
37 pub classes: Vec<ClassEntry>,
38 pub objects: Vec<ObjectEntry>,
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::model::{LineColumn, SourceRange, Visibility};
45 use serde_json;
46
47 fn sample_source_range() -> SourceRange {
48 SourceRange {
49 byte_start: 0,
50 byte_end: 5,
51 start: LineColumn { line: 1, column: 0 },
52 end: LineColumn { line: 1, column: 5 },
53 }
54 }
55
56 #[test]
57 fn inspect_output_empty_serializes() {
58 let output = InspectOutput {
59 funcs: vec![],
60 classes: vec![],
61 objects: vec![],
62 };
63 let json = serde_json::to_string(&output).unwrap();
64 assert_eq!(json, "{\"funcs\":[],\"classes\":[],\"objects\":[]}");
65 }
66
67 #[test]
68 fn func_entry_has_required_keys() {
69 let entry = FuncEntry {
70 name: "f".into(),
71 source_range: sample_source_range(),
72 visibility: None,
73 signature: None,
74 docstring: None,
75 is_async: false,
76 };
77 let val: serde_json::Value = serde_json::to_value(&entry).unwrap();
78 let obj = val.as_object().unwrap();
79 let expected_keys = [
80 "name",
81 "source_range",
82 "async",
83 "visibility",
84 "signature",
85 "docstring",
86 ];
87 for key in &expected_keys {
88 assert!(obj.contains_key(*key), "missing key: {key}");
89 }
90 }
91
92 #[test]
93 fn func_entry_async_field_renamed() {
94 let entry = FuncEntry {
95 name: "g".into(),
96 source_range: sample_source_range(),
97 visibility: None,
98 signature: None,
99 docstring: None,
100 is_async: true,
101 };
102 let val: serde_json::Value = serde_json::to_value(&entry).unwrap();
103 assert_eq!(val["async"], true);
104 assert!(val.get("is_async").is_none());
105 }
106
107 #[test]
108 fn class_entry_serialization() {
109 let entry = ClassEntry {
110 name: "MyClass".into(),
111 source_range: sample_source_range(),
112 visibility: Some(Visibility::Public),
113 signature: Some("class MyClass".into()),
114 docstring: Some("a class".into()),
115 };
116 let val: serde_json::Value = serde_json::to_value(&entry).unwrap();
117 assert_eq!(val["name"], "MyClass");
118 assert_eq!(val["visibility"], "public");
119 assert_eq!(val["signature"], "class MyClass");
120 assert_eq!(val["docstring"], "a class");
121 assert!(val["source_range"].is_object());
122 }
123
124 #[test]
125 fn object_entry_serialization() {
126 let entry = ObjectEntry {
127 name: "obj".into(),
128 source_range: sample_source_range(),
129 visibility: Some(Visibility::Private),
130 signature: None,
131 docstring: None,
132 };
133 let val: serde_json::Value = serde_json::to_value(&entry).unwrap();
134 assert_eq!(val["name"], "obj");
135 assert_eq!(val["visibility"], "private");
136 assert!(val["signature"].is_null());
137 assert!(val["docstring"].is_null());
138 }
139
140 #[test]
141 fn inspect_output_serde_roundtrip() {
142 let output = InspectOutput {
143 funcs: vec![FuncEntry {
144 name: "fn1".into(),
145 source_range: sample_source_range(),
146 visibility: Some(Visibility::Public),
147 signature: None,
148 docstring: None,
149 is_async: false,
150 }],
151 classes: vec![ClassEntry {
152 name: "Cls".into(),
153 source_range: sample_source_range(),
154 visibility: None,
155 signature: None,
156 docstring: None,
157 }],
158 objects: vec![ObjectEntry {
159 name: "obj".into(),
160 source_range: sample_source_range(),
161 visibility: None,
162 signature: None,
163 docstring: None,
164 }],
165 };
166 let json = serde_json::to_string(&output).unwrap();
167 let val: serde_json::Value = serde_json::from_str(&json).unwrap();
168 assert_eq!(val["funcs"][0]["name"], "fn1");
169 assert_eq!(val["classes"][0]["name"], "Cls");
170 assert_eq!(val["objects"][0]["name"], "obj");
171 }
172
173 #[test]
174 fn inspect_output_json_keys() {
175 let output = InspectOutput {
176 funcs: vec![],
177 classes: vec![],
178 objects: vec![],
179 };
180 let val: serde_json::Value = serde_json::to_value(&output).unwrap();
181 let keys: std::collections::HashSet<&str> = val
182 .as_object()
183 .unwrap()
184 .keys()
185 .map(|s| s.as_str())
186 .collect();
187 let expected: std::collections::HashSet<&str> =
188 ["funcs", "classes", "objects"].into_iter().collect();
189 assert_eq!(keys, expected);
190 }
191}