1use clang::*;
2use serde_derive::{Deserialize, Serialize};
3use std::fs::{create_dir, File};
4use std::io::Read;
5use std::path::Path;
6use std::io::Write;
7use itertools::Itertools;
8use walkdir::WalkDir;
9#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct Package {
12 name: String,
13 version: String,
14 standard: String,
15 project_type: Option<String>,
16 repository: Option<String>,
17 owners: Vec<Owner>,
18 pub dependency: Option<Vec<Dependency>>,
19 dev_dependency: Option<Vec<Dependency>>,
20 description: Option<String>,
21 license: Option<String>,
22}
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Example {
25 exec_paths: Vec<String>,
26}
27impl Example {
28 pub fn new(path: &str) -> std::io::Result<Self> {
29 let mut exec_paths = Vec::new();
30 for file in WalkDir::new(format!("{}/examples", path)){
31 exec_paths.push(format!("{}", file?.path().display()));
32 }
33 Ok( Self { exec_paths } )
34 }
35 pub fn find(&self, name: &str) -> Option<String>{
36 let name = format!("examples/{}.cpp", name);
37 if self.exec_paths.contains(&name){
38 return Some(name);
39 }
40 None
41 }
42}
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
52pub enum ItemType{
53 FunctionDecl,
54 UsingDirective,
55 InclusionDirective,
56}
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
58pub struct Item {
59 name: Option<String>,
60 comment: Option<String>,
61 full_text: String,
62 kind: ItemType,
63}
64impl Item {
65 pub fn new(
66 name: Option<String>,
67 comment: Option<String>,
68 path: &str,
69 start: usize,
70 end: usize,
71 kind: ItemType,
72 ) -> std::result::Result<Self, std::io::Error> {
73 let mut file = File::open(path)?;
74 let mut content: Vec<u8> = Vec::new();
75 match file.read_to_end(&mut content) {
76 Ok(_) => (),
77 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
78 };
79 let full_text: String = match String::from_utf8(content[start..end].to_vec()) {
80 Ok(text) => text,
81 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
82 };
83 Ok(Self {
84 name,
85 comment,
86 full_text,
87 kind
88 })
89 }
90 pub fn get_type(&self) -> ItemType{
91 self.kind.clone()
92 }
93 pub fn get_text(&self) -> String{
94 self.full_text.clone()
95 }
96 pub fn get_name(&self) -> Option<String>{
97 self.name.clone()
98 }
99}
100#[derive(Debug, Serialize, Deserialize, Clone)]
101pub struct Test {
102 name: String,
103 entities: Vec<Item>,
104 dir: String,
105}
106impl Test {
107 pub fn from_file(path: &str, ident: &str) -> std::result::Result<Self, std::io::Error> {
109 let project = Project::from_file(path)?;
110 let clang = Clang::new().unwrap();
111 let index = Index::new(&clang, false, false);
112 let mut tests: Vec<std::io::Result<String>> = WalkDir::new(format!("{}/src",path)).into_iter().filter(|e|{
113 e.as_ref().unwrap().path().is_file()
114 }).map(|e| {
115 Ok(format!("{}", e?.path().display()))
116 }).collect();
117 tests.append(&mut WalkDir::new(format!("{}/tests",path)).into_iter().filter(|e|{
123 e.as_ref().unwrap().path().is_file()
124 }).map(|e| {
125 Ok(format!("{}", e?.path().display()))
126 }).collect());
127 let mut funcs = Vec::new();
128 for test in tests.iter(){
129 let test = match test{
130 Ok(t) => t,
131 Err(e) => return Err(std::io::Error::new(e.kind(), "error getting test")),
132 };
133 let tu = index
134 .parser(test)
135 .detailed_preprocessing_record(true)
136 .parse()
137 .unwrap();
138 let functions = tu
139 .get_entity()
140 .get_children()
141 .into_iter()
142 .filter(|e| {
143 e.get_kind() == EntityKind::FunctionDecl && e.get_comment() == Some(ident.to_string()) ||
144 e.get_kind() == EntityKind::InclusionDirective && !e.is_in_system_header()
145 || e.get_kind() == EntityKind::UsingDirective && !e.is_in_system_header()
146 })
147 .collect::<Vec<_>>();
148 for func in functions.iter() {
149 let range = func.get_range().unwrap();
152 funcs.push(Item::new(
153 func.get_display_name(),
154 func.get_comment(),
155 &format!(
156 "{}",
157 func.get_location()
158 .unwrap()
159 .get_file_location()
160 .file
161 .unwrap()
162 .get_path()
163 .as_path()
164 .display()
165 ),
166 range.get_start().get_file_location().offset as usize,
167 range.get_end().get_file_location().offset as usize,
168 match func.get_kind(){
169 EntityKind::FunctionDecl => ItemType::FunctionDecl,
170 EntityKind::UsingDirective => ItemType::UsingDirective,
171 _ => ItemType::InclusionDirective
172 }
173 )?);
174 }
175 }
176 Ok(Self {
177 name: project.get_name(),
178 entities: funcs.into_iter().unique().collect(),
179 dir: path.to_string(),
180 })
181 }
182 pub fn get_entities(&self) -> Vec<Item> {
184 self.entities.clone()
185 }
186 pub fn append(&mut self, second: &mut Vec<Item>) {
188 self.entities.append(second);
189 }
190 pub fn build_main(&self) -> std::result::Result<(), std::io::Error> {
192 let mut file = File::create(format!("{}/target/test_{}.cpp", self.dir, self.name))?;
193 let mut open: bool = false;
194 let mut funcs = Vec::new();
195 for item in self.entities.iter(){
196 match item.get_type(){
197 ItemType::InclusionDirective => {
198 file.write_all(format!("{}\n", item.get_text()).as_bytes())?
199 },
200 ItemType::UsingDirective => {
201 file.write_all(format!("{};\n", item.get_text()).as_bytes())?
202 },
203 ItemType::FunctionDecl => {
204 file.write_all(format!("int {};\n", item.get_name().unwrap()).as_bytes())?;
205 funcs.push(item);
206 },
207 }
208 }
209 for func in funcs.iter(){
210 if !open{
211 open = true;
212 file.write_all(b"int main(){\n")?;
213 }
214 file.write_all(format!("\t{};\n", func.get_name().unwrap()).as_bytes())?;
215 }
216 file.write_all(b"}\n")?;
217 Ok(())
218 }
219}
220#[derive(Debug, Serialize, Deserialize, Clone)]
221pub struct Dependency {
222 name: String,
223 version: String,
224 url: Option<String>,
225}
226impl Dependency {
227 pub fn new(name: String, version: String, url: Option<String>) -> Self {
228 Self { name, version, url }
229 }
230 pub fn get_name(&self) -> String {
231 self.name.clone()
232 }
233 pub fn get_version(&self) -> String {
234 self.version.clone()
235 }
236 pub fn get_url(&self) -> Option<String> {
237 self.url.clone()
238 }
239}
240#[derive(Debug, Serialize, Deserialize, Clone)]
241pub struct Owner {
242 name: String,
243 email: String,
244}
245#[derive(Debug, Serialize, Deserialize)]
246pub struct Project {
247 package: Package,
248 examples: Option<Vec<Example>>,
249 tests: Option<Test>,
250}
251impl Project {
252 pub fn new(name: String, project_type: Option<String>, owners: Option<Vec<Owner>>) -> Self {
253 let mut own = Vec::new();
254 match owners {
255 Some(mut owns) => own.append(&mut owns),
256 None => own.push(Owner::new(whoami::username())),
257 }
258 Self {
259 package: Package::new(
260 name,
261 "0.1.0".to_string(),
262 "c++17".to_string(),
263 project_type,
264 own,
265 None,
266 ),
267 examples: None,
268 tests: None,
269 }
270 }
271 pub fn get_package(&self) -> Package {
272 self.package.clone()
273 }
274 pub fn get_dependencies(&self) -> Option<Vec<Dependency>> {
275 match &self.package.dependency {
276 Some(dep) => Some(dep.clone()),
277 None => None,
278 }
279 }
280 pub fn get_type(&self) -> String {
281 self.package.project_type.as_ref().unwrap().clone()
282 }
283 pub fn get_version(&self) -> String {
284 self.package.version.clone()
285 }
286 pub fn get_name(&self) -> String {
287 self.package.name.clone()
288 }
289 pub fn get_standard(&self) -> String {
290 self.package.standard.clone()
291 }
292 pub fn from_file(path: &str) -> std::io::Result<Self> {
293 let mut file = match File::open(format!("{}/{}", path, "build.toml")) {
294 Ok(file) => file,
295 Err(e) => return Err(e),
296 };
297 if !Path::new("target/").exists() {
298 match create_dir("target") {
299 Ok(()) => (),
300 Err(e) => return Err(e),
301 }
302 }
303 let mut content = String::new();
304 match file.read_to_string(&mut content) {
305 Ok(_) => (),
306 Err(e) => return Err(e),
307 }
308 Ok(Self {
309 package: toml::from_str(content.as_str()).unwrap(),
310 examples: None,
311 tests: None,
312 })
313 }
314}
315
316impl Package {
317 pub fn new(
318 name: String,
319 version: String,
320 standard: String,
321 project_type: Option<String>,
322 owners: Vec<Owner>,
323 repository: Option<String>,
324 ) -> Self {
325 Self {
326 name,
327 version,
328 standard,
329 project_type,
330 repository,
331 owners,
332 description: None,
333 license: None,
334 dependency: None,
335 dev_dependency: None,
336 }
337 }
338 pub fn get_name(&self) -> String{
339 self.name.clone()
340 }
341 pub fn get_version(&self) -> String{
342 self.version.clone()
343 }
344 pub fn get_description(&self) -> Option<String>{
345 match &self.description {
346 Some(desc) => Some(desc.clone()),
347 None => None
348 }
349 }
350}
351impl PartialEq for Package {
352 fn eq(&self, other: &Self) -> bool {
353 if self.version == other.version && self.name == other.name {
354 return true;
355 }
356 false
357 }
358}
359impl Owner {
360 pub fn new(name: String) -> Self {
361 Self {
362 name,
363 email: "unset".to_string(),
364 }
365 }
366}