1use std::collections::HashMap;
5
6pub fn get_proto_package(s: &str) -> Option<&str> {
7 for logic_line in s.split(";") {
8 let line = logic_line.trim();
9 if !line.to_lowercase().starts_with("package ") {
10 continue;
11 }
12 let package_name = line["package ".len()..].trim();
13 return Some(package_name);
14 }
15 return None;
16}
17
18#[derive(Clone, Debug, Default, PartialEq)]
19pub struct ProtoModule {
20 file_name: Option<String>,
21 submodules: HashMap<String, ProtoModule>,
22}
23
24impl ProtoModule {
25 pub fn add_file(self: &mut Self, fname: &str) {
26 let parts: Vec<_> = fname.split(".").collect();
27 let mut current = self;
28 for (i, part) in parts.iter().enumerate() {
29 if i == parts.len() - 1 {
30 current.file_name = Some(fname.to_string());
31 continue;
32 }
33 let part = part.to_string();
34 if current.submodules.get(&part).is_none() {
35 current
36 .submodules
37 .insert(part.clone(), ProtoModule::default());
38 }
39 current = current.submodules.get_mut(&part).unwrap();
40 }
41 }
42
43 pub fn to_string(self: &Self) -> String {
44 return self.to_string_with_indent("");
45 }
46
47 fn to_string_with_indent(self: &Self, indent: &str) -> String {
48 let mut res = String::new();
49
50 if let Some(file_name) = &self.file_name {
51 res += format!("{}include!(\"{}\");\n", indent, file_name).as_str();
52 }
53
54 let mut keys: Vec<_> = self.submodules.keys().collect();
55 keys.sort();
56
57 for key in keys {
58 res += format!("{}pub mod {} {{\n", indent, key).as_str();
59
60 let m = self.submodules.get(key).unwrap();
61 res += m
62 .to_string_with_indent(format!("{} ", indent).as_str())
63 .as_str();
64
65 res += format!("{}}}\n", indent).as_str()
66 }
67
68 return res;
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use crate::*;
75
76 #[test]
77 fn test_get_proto_package() {
78 assert_eq!(get_proto_package(""), None);
79 assert_eq!(get_proto_package("asd"), None);
80 assert_eq!(get_proto_package("package asd"), Some("asd"));
81 assert_eq!(get_proto_package("package asd;"), Some("asd"));
82 assert_eq!(
83 get_proto_package(
84 "saldjhfsalkdjf;\
85 package \
86 asd;\
87 "
88 ),
89 Some("asd")
90 );
91 }
92
93 #[test]
94 fn test_add_file() {
95 let mut m = ProtoModule::default();
96 m.add_file("asd.rs");
97 let asd = "asd".to_string();
98 let mut expected_m = ProtoModule::default();
99 expected_m
100 .submodules
101 .insert(asd.clone(), ProtoModule::default());
102 expected_m.submodules.get_mut("asd").unwrap().file_name = Some("asd.rs".to_string());
103 assert_eq!(m, expected_m);
104
105 m.add_file("asd.sub.v1.rs");
106 let sub = "sub".to_string();
107 let v1 = "v1".to_string();
108 expected_m
109 .submodules
110 .get_mut("asd")
111 .unwrap()
112 .submodules
113 .insert(sub.clone(), ProtoModule::default());
114 expected_m
115 .submodules
116 .get_mut("asd")
117 .unwrap()
118 .submodules
119 .get_mut(&sub)
120 .unwrap()
121 .submodules
122 .insert(v1.clone(), ProtoModule::default());
123 expected_m
124 .submodules
125 .get_mut("asd")
126 .unwrap()
127 .submodules
128 .get_mut(&sub)
129 .unwrap()
130 .submodules
131 .get_mut(&v1)
132 .unwrap()
133 .file_name = Some("asd.sub.v1.rs".to_string());
134 assert_eq!(m, expected_m);
135 }
136
137 #[test]
138 fn test_to_string() {
139 let m = ProtoModule {
140 file_name: None,
141 submodules: [(
142 "asd".to_string(),
143 ProtoModule {
144 file_name: None,
145 submodules: [(
146 "v1".to_string(),
147 ProtoModule {
148 file_name: Some("asd.v1.rs".to_string()),
149 submodules: HashMap::default(),
150 },
151 )]
152 .iter()
153 .cloned()
154 .collect(),
155 },
156 )]
157 .iter()
158 .cloned()
159 .collect(),
160 };
161
162 let s = m.to_string();
163 let expected = "pub mod asd {
164 pub mod v1 {
165 include!(\"asd.v1.rs\");
166 }
167}
168";
169 assert_eq!(s, expected);
170 }
171}