barexp/
lib.rs

1//! # Barexp
2//!
3//! `barexp` is a library that automatically generates `mod.rs` files for your Rust project's subdirectories.
4//!
5//! ## Quick Start
6//!
7//! Add this to your `Cargo.toml`:
8//!
9//! ```toml
10//! [build-dependencies]
11//! barexp = "1.1.0"
12//! ```
13//!
14//! Then create a `build.rs` in your project root:
15//!
16//! ```rust
17//! fn main() {
18//!     barexp::build();
19//! }
20//! ```
21//!
22//! ## Examples
23//!
24//! Basic usage:
25//!
26//! ```rust
27//! // Your project's build.rs
28//! fn main() {
29//!     barexp::build();
30//! }
31//! ```
32//!
33//! ## Features
34//!
35//! - Recursively scans project subdirectories
36//! - Automatically generates `mod.rs` files
37//! - Re-exports all modules
38//! - Ignores hidden files and `target` directory
39
40
41use std::fs;
42use std::path::{Path, PathBuf};
43use walkdir::WalkDir;
44
45/// Represents a Rust module in the file system
46#[derive(Debug)]
47struct Module {
48    name: String,
49    path: PathBuf,
50    is_file: bool,
51}
52/// Optional function to generate mod.rs files for a given directory
53///
54/// This function is useful if you want to generate mod.rs files for a directory
55/// other than the default "src" directory.
56///
57/// # Arguments
58///
59/// * `src_dir` - The source directory to scan
60///
61/// # Examples
62///
63/// ```rust
64/// fn main() {
65///   barexp::generate_mod_files("src/services");
66/// }
67/// ```
68pub fn generate_mod_files(src_dir: &str) {
69    println!("cargo:rerun-if-changed={}", src_dir);
70
71    let src_path = Path::new(src_dir);
72
73    // Tüm Rust dosyalarını ve dizinlerini bul
74    for entry in WalkDir::new(src_dir)
75        .into_iter()
76        .filter_entry(|e| !is_hidden(e.path()))
77        .filter_map(|e| e.ok()) {
78
79        let path = entry.path();
80
81        // Root src dizini hariç, dizin içindeki mod.rs'i oluştur
82        if path.is_dir() && path != src_path && should_create_mod_rs(path) {
83            generate_mod_rs(path);
84        }
85    }
86}
87
88/// Internal function to check if a path is hidden
89///
90/// # Arguments
91///
92/// * `path` - The path to check
93///
94/// # Returns
95///
96/// * `true` if the path is hidden or is "target" directory
97/// * `false` otherwise
98fn is_hidden(path: &Path) -> bool {
99    path.file_name()
100        .and_then(|s| s.to_str())
101        .map(|s| s.starts_with('.') || s == "target")
102        .unwrap_or(false)
103}
104
105fn should_create_mod_rs(dir: &Path) -> bool {
106    // Dizinde en az bir .rs dosyası veya alt dizin varsa
107    fs::read_dir(dir).map_or(false, |entries| {
108        entries
109            .filter_map(Result::ok)
110            .any(|e| {
111                let p = e.path();
112                (p.is_file() && p.extension().map_or(false, |ext| ext == "rs") &&
113                    p.file_name().map_or(false, |name| name != "mod.rs")) ||
114                    (p.is_dir() && !is_hidden(&p))
115            })
116    })
117}
118
119fn collect_modules(dir: &Path) -> Vec<Module> {
120    let mut modules = Vec::new();
121
122    if let Ok(entries) = fs::read_dir(dir) {
123        for entry in entries.filter_map(Result::ok) {
124            let path = entry.path();
125
126            // Gizli dosyaları ve mod.rs'i atla
127            if is_hidden(&path) || path.file_name().map_or(false, |n| n == "mod.rs") {
128                continue;
129            }
130
131            if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {
132                // foo.rs -> mod foo
133                if let Some(name) = path.file_stem().and_then(|n| n.to_str()) {
134                    modules.push(Module {
135                        name: name.to_string(),
136                        path,
137                        is_file: true,
138                    });
139                }
140            } else if path.is_dir() {
141                // foo/mod.rs veya foo/lib.rs varsa -> mod foo
142                if path.join("mod.rs").exists() || path.join("lib.rs").exists() {
143                    if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
144                        modules.push(Module {
145                            name: name.to_string(),
146                            path,
147                            is_file: false,
148                        });
149                    }
150                }
151            }
152        }
153    }
154
155    modules
156}
157
158fn generate_mod_rs(dir: &Path) {
159    let modules = collect_modules(dir);
160
161    if modules.is_empty() {
162        return;
163    }
164
165    let mut content = String::new();
166
167    // Önce mod declarations
168    for module in &modules {
169        content.push_str(&format!("pub mod {};\n", module.name));
170    }
171
172    content.push('\n');
173
174    // Sonra re-exports
175    content.push_str("pub use self::{\n");
176    for module in &modules {
177        content.push_str(&format!("    {}::*,\n", module.name));
178    }
179    content.push_str("};\n");
180
181    // mod.rs dosyasını oluştur veya güncelle
182    let mod_path = dir.join("mod.rs");
183    fs::write(mod_path, content).unwrap();
184}
185
186/// Main function to generate mod.rs files
187///
188/// This function scans the given directory and its subdirectories
189/// to automatically generate mod.rs files.
190///
191/// # Arguments
192///
193/// * `src_dir` - The source directory to scan, typically "src"
194///
195/// # Examples
196///
197/// ```rust
198/// fn main() {
199///     barexp::build();
200/// }
201/// ```
202///
203/// # Panics
204///
205/// Will panic if:
206/// * The source directory doesn't exist
207/// * File system operations fail
208/// * Permission errors occur
209pub fn build() {
210    generate_mod_files("src");
211}