1use std::path::{Path, PathBuf};
2use std::{ffi::OsString, fs};
3
4#[derive(clap::Parser)]
5#[command(author, version, about, long_about = None)]
6#[command(group(clap::ArgGroup::new("traversal").args(["recursive", "depth"])))]
7pub struct CLI {
8 #[arg(required = true)]
10 pub paths: Vec<PathBuf>,
11
12 #[arg(short, long)]
14 pub ignore: Vec<OsString>,
15
16 #[arg(long)]
18 pub only_ts: bool,
19
20 #[arg(short, long, default_value_t = 0)]
22 pub depth: usize,
23
24 #[arg(short, long)]
26 pub recursive: bool,
27}
28
29pub enum Entry {
30 Folder { path: PathBuf, entries: Vec<Entry> },
31 File(PathBuf),
32}
33
34pub fn write_files(root: &Path, entries: &Vec<Entry>) {
35 let mut buffer = Vec::new();
36
37 for entry in entries {
38 match entry {
39 Entry::File(path) => {
40 let os_str = path.file_stem().unwrap_or_default();
41 let name = os_str.to_str().unwrap_or_default();
42 buffer.push(format!("export * from './{}';", name));
43 }
44 Entry::Folder { path, entries } => {
45 let os_str = path.file_stem().unwrap_or_default();
46 let name = os_str.to_str().unwrap_or_default();
47 buffer.push(format!("export * from './{}';", name));
48 write_files(&path, entries);
49 }
50 }
51 }
52
53 if buffer.len() > 0 {
54 let mut output_path = root.to_owned();
55 output_path.push("index.ts");
56 fs::write(output_path, buffer.join("\n") + "\n").unwrap();
57 }
58}
59
60pub fn read_path(
61 root: &Path,
62 ignore: &Vec<OsString>,
63 only_ts: bool,
64 recursive: bool,
65 max_depth: usize,
66 depth: usize,
67) -> Vec<Entry> {
68 let rd = fs::read_dir(root).unwrap();
69
70 let output = rd
71 .filter_map(|r| r.ok())
72 .filter(|dir_entry| filter_dir_entry(dir_entry, only_ts, ignore))
73 .map(|entry| {
74 let path = entry.path();
75
76 if path.is_file() {
77 return Entry::File(path);
78 }
79
80 let entries = if recursive || depth + 1 <= max_depth {
81 read_path(&path, ignore, only_ts, recursive, max_depth, depth + 1)
82 } else {
83 Vec::new()
84 };
85
86 return Entry::Folder { path, entries };
87 })
88 .collect::<Vec<Entry>>();
89
90 return output;
91}
92
93fn filter_dir_entry(entry: &fs::DirEntry, only_ts: bool, ignore: &Vec<OsString>) -> bool {
94 let path = entry.path();
95 let name = path
96 .file_name()
97 .unwrap_or_default()
98 .to_str()
99 .unwrap_or_default();
100
101 let should_exclude = name.contains("index")
102 || ignore
103 .iter()
104 .any(|t| name.contains(t.to_str().unwrap_or_default()));
105
106 if path.is_file() {
107 let should_include = if let Some(extension) = path.extension() {
108 let allowed_extensions = if only_ts {
109 vec!["ts", "tsx"]
110 } else {
111 vec!["ts", "tsx", "js", "jsx"]
112 };
113
114 let extension = extension.to_str().unwrap_or_default();
115 allowed_extensions.iter().any(|ext| extension == *ext)
116 } else {
117 false
118 };
119 return !should_exclude && should_include;
120 }
121 return !should_exclude;
122}