1use std::collections::HashMap;
3
4#[derive(Debug, Clone)]
5pub struct EducationalAdvice {
6 pub why_bad: String,
7 pub how_to_fix: String,
8 pub example_bad: Option<String>,
9 pub example_good: Option<String>,
10 pub rust_docs_link: Option<String>,
11 pub best_practice_tip: Option<String>,
12}
13
14pub struct EducationalAdvisor {
15 advice_db: HashMap<String, EducationalAdvice>,
16 lang: String,
17}
18
19impl EducationalAdvisor {
20 pub fn new(lang: &str) -> Self {
21 let mut advisor = Self {
22 advice_db: HashMap::new(),
23 lang: lang.to_string(),
24 };
25 advisor.initialize_advice_database();
26 advisor
27 }
28
29 pub fn get_advice(&self, rule_name: &str) -> Option<&EducationalAdvice> {
30 self.advice_db.get(rule_name)
31 }
32
33 fn initialize_advice_database(&mut self) {
34 self.add_advice("terrible-naming", self.create_terrible_naming_advice());
36 self.add_advice(
37 "meaningless-naming",
38 self.create_meaningless_naming_advice(),
39 );
40 self.add_advice(
41 "hungarian-notation",
42 self.create_hungarian_notation_advice(),
43 );
44 self.add_advice(
45 "abbreviation-abuse",
46 self.create_abbreviation_abuse_advice(),
47 );
48
49 self.add_advice("deep-nesting", self.create_deep_nesting_advice());
51 self.add_advice("god-function", self.create_god_function_advice());
52 self.add_advice("long-function", self.create_long_function_advice());
53
54 self.add_advice("magic-number", self.create_magic_number_advice());
56 self.add_advice("commented-code", self.create_commented_code_advice());
57 self.add_advice("dead-code", self.create_dead_code_advice());
58
59 self.add_advice("unwrap-abuse", self.create_unwrap_abuse_advice());
61 self.add_advice("string-abuse", self.create_string_abuse_advice());
62 self.add_advice("unnecessary-clone", self.create_unnecessary_clone_advice());
63 self.add_advice("iterator-abuse", self.create_iterator_abuse_advice());
64
65 self.add_advice("println-debugging", self.create_println_debugging_advice());
67 self.add_advice("panic-abuse", self.create_panic_abuse_advice());
68 self.add_advice("todo-comment", self.create_todo_comment_advice());
69
70 self.add_advice("file-too-long", self.create_file_too_long_advice());
72 self.add_advice("unordered-imports", self.create_unordered_imports_advice());
73 self.add_advice(
74 "deep-module-nesting",
75 self.create_deep_module_nesting_advice(),
76 );
77 }
78
79 fn add_advice(&mut self, rule_name: &str, advice: EducationalAdvice) {
80 self.advice_db.insert(rule_name.to_string(), advice);
81 }
82
83 fn create_terrible_naming_advice(&self) -> EducationalAdvice {
84 if self.lang == "zh-CN" {
85 EducationalAdvice {
86 why_bad: "糟糕的变量命名会严重影响代码可读性,让其他开发者(包括未来的你)难以理解代码意图。".to_string(),
87 how_to_fix: "使用描述性的、有意义的变量名,清楚地表达变量的用途和含义。".to_string(),
88 example_bad: Some("let d = get_user_data();".to_string()),
89 example_good: Some("let user_profile = get_user_data();".to_string()),
90 rust_docs_link: Some("https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html".to_string()),
91 best_practice_tip: Some("变量名应该是自文档化的,读代码的人应该能从名字就理解变量的用途。".to_string()),
92 }
93 } else {
94 EducationalAdvice {
95 why_bad: "Poor variable naming severely impacts code readability, making it difficult for other developers (including future you) to understand the code's intent.".to_string(),
96 how_to_fix: "Use descriptive, meaningful variable names that clearly express the variable's purpose and meaning.".to_string(),
97 example_bad: Some("let d = get_user_data();".to_string()),
98 example_good: Some("let user_profile = get_user_data();".to_string()),
99 rust_docs_link: Some("https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html".to_string()),
100 best_practice_tip: Some("Variable names should be self-documenting - readers should understand the purpose from the name alone.".to_string()),
101 }
102 }
103 }
104
105 fn create_meaningless_naming_advice(&self) -> EducationalAdvice {
106 if self.lang == "zh-CN" {
107 EducationalAdvice {
108 why_bad: "使用 foo、bar、data、temp 等占位符命名会让代码失去表达力,增加维护成本。"
109 .to_string(),
110 how_to_fix: "根据变量的实际用途选择具体的、有意义的名称。".to_string(),
111 example_bad: Some("let data = process_foo(bar);".to_string()),
112 example_good: Some(
113 "let processed_orders = process_customer_orders(raw_orders);".to_string(),
114 ),
115 rust_docs_link: Some(
116 "https://rust-lang.github.io/api-guidelines/naming.html".to_string(),
117 ),
118 best_practice_tip: Some(
119 "避免使用通用词汇,选择能准确描述数据性质的词汇。".to_string(),
120 ),
121 }
122 } else {
123 EducationalAdvice {
124 why_bad: "Using placeholder names like foo, bar, data, temp makes code lose expressiveness and increases maintenance cost.".to_string(),
125 how_to_fix: "Choose specific, meaningful names based on the variable's actual purpose.".to_string(),
126 example_bad: Some("let data = process_foo(bar);".to_string()),
127 example_good: Some("let processed_orders = process_customer_orders(raw_orders);".to_string()),
128 rust_docs_link: Some("https://rust-lang.github.io/api-guidelines/naming.html".to_string()),
129 best_practice_tip: Some("Avoid generic words, choose words that accurately describe the nature of the data.".to_string()),
130 }
131 }
132 }
133
134 fn create_hungarian_notation_advice(&self) -> EducationalAdvice {
135 if self.lang == "zh-CN" {
136 EducationalAdvice {
137 why_bad:
138 "匈牙利命名法在现代编程语言中已经过时,Rust 的类型系统已经提供了类型安全保障。"
139 .to_string(),
140 how_to_fix: "使用描述性名称而不是类型前缀,让 Rust 的类型系统处理类型检查。"
141 .to_string(),
142 example_bad: Some("let strUserName: String = get_name();".to_string()),
143 example_good: Some("let user_name: String = get_name();".to_string()),
144 rust_docs_link: Some(
145 "https://rust-lang.github.io/api-guidelines/naming.html".to_string(),
146 ),
147 best_practice_tip: Some(
148 "Rust 的强类型系统使得类型前缀变得多余,专注于语义而非类型。".to_string(),
149 ),
150 }
151 } else {
152 EducationalAdvice {
153 why_bad: "Hungarian notation is outdated in modern programming languages, Rust's type system already provides type safety guarantees.".to_string(),
154 how_to_fix: "Use descriptive names instead of type prefixes, let Rust's type system handle type checking.".to_string(),
155 example_bad: Some("let strUserName: String = get_name();".to_string()),
156 example_good: Some("let user_name: String = get_name();".to_string()),
157 rust_docs_link: Some("https://rust-lang.github.io/api-guidelines/naming.html".to_string()),
158 best_practice_tip: Some("Rust's strong type system makes type prefixes redundant, focus on semantics rather than types.".to_string()),
159 }
160 }
161 }
162
163 fn create_abbreviation_abuse_advice(&self) -> EducationalAdvice {
164 if self.lang == "zh-CN" {
165 EducationalAdvice {
166 why_bad: "过度缩写会让代码变得难以理解,特别是对新团队成员或几个月后的自己。"
167 .to_string(),
168 how_to_fix: "使用完整的、清晰的单词,现代编辑器的自动补全让长名称不再是问题。"
169 .to_string(),
170 example_bad: Some("let usr_mgr = UserMgr::new();".to_string()),
171 example_good: Some("let user_manager = UserManager::new();".to_string()),
172 rust_docs_link: Some(
173 "https://rust-lang.github.io/api-guidelines/naming.html".to_string(),
174 ),
175 best_practice_tip: Some(
176 "清晰胜过简洁,代码被阅读的次数远超过被编写的次数。".to_string(),
177 ),
178 }
179 } else {
180 EducationalAdvice {
181 why_bad: "Excessive abbreviations make code hard to understand, especially for new team members or yourself months later.".to_string(),
182 how_to_fix: "Use complete, clear words. Modern editors' auto-completion makes long names no longer a problem.".to_string(),
183 example_bad: Some("let usr_mgr = UserMgr::new();".to_string()),
184 example_good: Some("let user_manager = UserManager::new();".to_string()),
185 rust_docs_link: Some("https://rust-lang.github.io/api-guidelines/naming.html".to_string()),
186 best_practice_tip: Some("Clarity over brevity - code is read far more often than it's written.".to_string()),
187 }
188 }
189 }
190
191 fn create_deep_nesting_advice(&self) -> EducationalAdvice {
192 if self.lang == "zh-CN" {
193 EducationalAdvice {
194 why_bad: "深层嵌套增加了代码的认知复杂度,使得逻辑难以跟踪和调试。".to_string(),
195 how_to_fix: "使用早期返回、提取函数、或者 Rust 的 ? 操作符来减少嵌套层级。".to_string(),
196 example_bad: Some("if condition1 {\n if condition2 {\n if condition3 {\n // deep logic\n }\n }\n}".to_string()),
197 example_good: Some("if !condition1 { return; }\nif !condition2 { return; }\nif !condition3 { return; }\n// logic here".to_string()),
198 rust_docs_link: Some("https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html".to_string()),
199 best_practice_tip: Some("保持嵌套层级在 3 层以内,使用卫语句和早期返回。".to_string()),
200 }
201 } else {
202 EducationalAdvice {
203 why_bad: "Deep nesting increases cognitive complexity, making logic hard to follow and debug.".to_string(),
204 how_to_fix: "Use early returns, extract functions, or Rust's ? operator to reduce nesting levels.".to_string(),
205 example_bad: Some("if condition1 {\n if condition2 {\n if condition3 {\n // deep logic\n }\n }\n}".to_string()),
206 example_good: Some("if !condition1 { return; }\nif !condition2 { return; }\nif !condition3 { return; }\n// logic here".to_string()),
207 rust_docs_link: Some("https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html".to_string()),
208 best_practice_tip: Some("Keep nesting levels within 3, use guard clauses and early returns.".to_string()),
209 }
210 }
211 }
212
213 fn create_unwrap_abuse_advice(&self) -> EducationalAdvice {
214 if self.lang == "zh-CN" {
215 EducationalAdvice {
216 why_bad: "过度使用 unwrap() 会导致程序在遇到错误时直接崩溃,无法优雅地处理异常情况。".to_string(),
217 how_to_fix: "使用 match、if let、或者 ? 操作符来正确处理 Option 和 Result 类型。".to_string(),
218 example_bad: Some("let value = some_option.unwrap();".to_string()),
219 example_good: Some("let value = some_option.unwrap_or_default();\n// or\nlet value = some_option?;".to_string()),
220 rust_docs_link: Some("https://doc.rust-lang.org/book/ch09-00-error-handling.html".to_string()),
221 best_practice_tip: Some("只在你确定不会失败的情况下使用 unwrap(),并添加注释说明原因。".to_string()),
222 }
223 } else {
224 EducationalAdvice {
225 why_bad: "Excessive use of unwrap() causes programs to crash directly when encountering errors, unable to handle exceptions gracefully.".to_string(),
226 how_to_fix: "Use match, if let, or the ? operator to properly handle Option and Result types.".to_string(),
227 example_bad: Some("let value = some_option.unwrap();".to_string()),
228 example_good: Some("let value = some_option.unwrap_or_default();\n// or\nlet value = some_option?;".to_string()),
229 rust_docs_link: Some("https://doc.rust-lang.org/book/ch09-00-error-handling.html".to_string()),
230 best_practice_tip: Some("Only use unwrap() when you're certain it won't fail, and add comments explaining why.".to_string()),
231 }
232 }
233 }
234
235 fn create_string_abuse_advice(&self) -> EducationalAdvice {
236 if self.lang == "zh-CN" {
237 EducationalAdvice {
238 why_bad:
239 "不必要的 String 分配会增加内存使用和性能开销,特别是在只需要读取的场景中。"
240 .to_string(),
241 how_to_fix: "在只需要读取字符串的地方使用 &str,只在需要拥有所有权时使用 String。"
242 .to_string(),
243 example_bad: Some("fn process_name(name: String) -> String".to_string()),
244 example_good: Some("fn process_name(name: &str) -> String".to_string()),
245 rust_docs_link: Some(
246 "https://doc.rust-lang.org/book/ch04-03-slices.html".to_string(),
247 ),
248 best_practice_tip: Some(
249 "优先使用 &str 作为函数参数,这样可以接受 String 和 &str 两种类型。"
250 .to_string(),
251 ),
252 }
253 } else {
254 EducationalAdvice {
255 why_bad: "Unnecessary String allocations increase memory usage and performance overhead, especially in read-only scenarios.".to_string(),
256 how_to_fix: "Use &str where you only need to read strings, use String only when you need ownership.".to_string(),
257 example_bad: Some("fn process_name(name: String) -> String".to_string()),
258 example_good: Some("fn process_name(name: &str) -> String".to_string()),
259 rust_docs_link: Some("https://doc.rust-lang.org/book/ch04-03-slices.html".to_string()),
260 best_practice_tip: Some("Prefer &str as function parameters, this way you can accept both String and &str types.".to_string()),
261 }
262 }
263 }
264
265 fn create_println_debugging_advice(&self) -> EducationalAdvice {
266 if self.lang == "zh-CN" {
267 EducationalAdvice {
268 why_bad: "遗留的 println! 调试语句会污染输出,在生产环境中可能泄露敏感信息。".to_string(),
269 how_to_fix: "使用 log 库进行日志记录,或者使用 dbg! 宏进行临时调试(记得删除)。".to_string(),
270 example_bad: Some("println!(\"Debug: {:?}\", sensitive_data);".to_string()),
271 example_good: Some("log::debug!(\"Processing data: {:?}\", data);\n// or for temporary debugging:\ndbg!(&data);".to_string()),
272 rust_docs_link: Some("https://docs.rs/log/latest/log/".to_string()),
273 best_practice_tip: Some("使用条件编译 #[cfg(debug_assertions)] 来确保调试代码不会进入生产环境。".to_string()),
274 }
275 } else {
276 EducationalAdvice {
277 why_bad: "Leftover println! debug statements pollute output and may leak sensitive information in production.".to_string(),
278 how_to_fix: "Use the log crate for logging, or use the dbg! macro for temporary debugging (remember to remove).".to_string(),
279 example_bad: Some("println!(\"Debug: {:?}\", sensitive_data);".to_string()),
280 example_good: Some("log::debug!(\"Processing data: {:?}\", data);\n// or for temporary debugging:\ndbg!(&data);".to_string()),
281 rust_docs_link: Some("https://docs.rs/log/latest/log/".to_string()),
282 best_practice_tip: Some("Use conditional compilation #[cfg(debug_assertions)] to ensure debug code doesn't reach production.".to_string()),
283 }
284 }
285 }
286
287 fn create_file_too_long_advice(&self) -> EducationalAdvice {
288 if self.lang == "zh-CN" {
289 EducationalAdvice {
290 why_bad: "过长的文件难以导航和维护,违反了单一职责原则,增加了代码的复杂性。".to_string(),
291 how_to_fix: "将大文件拆分成多个小模块,每个模块负责特定的功能领域。".to_string(),
292 example_bad: Some("// 一个包含 2000 行代码的 main.rs 文件".to_string()),
293 example_good: Some("// 拆分为:\n// - user_management.rs\n// - data_processing.rs\n// - api_handlers.rs\n// - main.rs (只包含启动逻辑)".to_string()),
294 rust_docs_link: Some("https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html".to_string()),
295 best_practice_tip: Some("保持文件在 500-1000 行以内,超过时考虑按功能拆分模块。".to_string()),
296 }
297 } else {
298 EducationalAdvice {
299 why_bad: "Overly long files are hard to navigate and maintain, violate the single responsibility principle, and increase code complexity.".to_string(),
300 how_to_fix: "Split large files into multiple small modules, each responsible for specific functional areas.".to_string(),
301 example_bad: Some("// A main.rs file containing 2000 lines of code".to_string()),
302 example_good: Some("// Split into:\n// - user_management.rs\n// - data_processing.rs\n// - api_handlers.rs\n// - main.rs (startup logic only)".to_string()),
303 rust_docs_link: Some("https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html".to_string()),
304 best_practice_tip: Some("Keep files within 500-1000 lines, consider splitting by functionality when exceeded.".to_string()),
305 }
306 }
307 }
308
309 fn create_god_function_advice(&self) -> EducationalAdvice {
311 EducationalAdvice {
312 why_bad: "Functions that do too much violate the single responsibility principle and are hard to test and maintain.".to_string(),
313 how_to_fix: "Break down large functions into smaller, focused functions that each do one thing well.".to_string(),
314 example_bad: Some("fn process_everything() { /* 100+ lines of mixed logic */ }".to_string()),
315 example_good: Some("fn process_data() {\n let validated = validate_input();\n let processed = transform_data(validated);\n save_result(processed);\n}".to_string()),
316 rust_docs_link: Some("https://doc.rust-lang.org/book/ch03-03-how-functions-work.html".to_string()),
317 best_practice_tip: Some("Keep functions under 20-30 lines and focused on a single task.".to_string()),
318 }
319 }
320
321 fn create_long_function_advice(&self) -> EducationalAdvice {
322 EducationalAdvice {
323 why_bad: "Long functions are harder to understand, test, and maintain.".to_string(),
324 how_to_fix: "Extract logical blocks into separate functions with descriptive names."
325 .to_string(),
326 example_bad: None,
327 example_good: None,
328 rust_docs_link: Some(
329 "https://doc.rust-lang.org/book/ch03-03-how-functions-work.html".to_string(),
330 ),
331 best_practice_tip: Some(
332 "If you can't see the entire function on your screen, it's probably too long."
333 .to_string(),
334 ),
335 }
336 }
337
338 fn create_magic_number_advice(&self) -> EducationalAdvice {
339 EducationalAdvice {
340 why_bad: "Magic numbers make code hard to understand and maintain.".to_string(),
341 how_to_fix: "Replace magic numbers with named constants that explain their purpose."
342 .to_string(),
343 example_bad: Some("if age > 18 { /* ... */ }".to_string()),
344 example_good: Some(
345 "const LEGAL_AGE: u32 = 18;\nif age > LEGAL_AGE { /* ... */ }".to_string(),
346 ),
347 rust_docs_link: Some(
348 "https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants"
349 .to_string(),
350 ),
351 best_practice_tip: Some(
352 "Use const declarations for values that have semantic meaning.".to_string(),
353 ),
354 }
355 }
356
357 fn create_commented_code_advice(&self) -> EducationalAdvice {
358 EducationalAdvice {
359 why_bad: "Commented-out code clutters the codebase and creates confusion about what's actually used.".to_string(),
360 how_to_fix: "Remove commented code - version control systems preserve history.".to_string(),
361 example_bad: Some("// let old_logic = process_old_way();\nlet new_logic = process_new_way();".to_string()),
362 example_good: Some("let new_logic = process_new_way();".to_string()),
363 rust_docs_link: None,
364 best_practice_tip: Some("Trust your version control system - delete dead code instead of commenting it out.".to_string()),
365 }
366 }
367
368 fn create_dead_code_advice(&self) -> EducationalAdvice {
369 EducationalAdvice {
370 why_bad: "Dead code increases maintenance burden and can confuse developers."
371 .to_string(),
372 how_to_fix: "Remove unused functions, variables, and imports regularly.".to_string(),
373 example_bad: None,
374 example_good: None,
375 rust_docs_link: None,
376 best_practice_tip: Some(
377 "Use #[allow(dead_code)] only temporarily during development.".to_string(),
378 ),
379 }
380 }
381
382 fn create_unnecessary_clone_advice(&self) -> EducationalAdvice {
383 EducationalAdvice {
384 why_bad: "Unnecessary clones waste memory and CPU cycles.".to_string(),
385 how_to_fix: "Use references when possible, clone only when you need ownership."
386 .to_string(),
387 example_bad: Some("let copied = original.clone();\nprocess(&copied);".to_string()),
388 example_good: Some("process(&original);".to_string()),
389 rust_docs_link: Some(
390 "https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html".to_string(),
391 ),
392 best_practice_tip: Some(
393 "Understand Rust's borrowing rules to minimize unnecessary allocations."
394 .to_string(),
395 ),
396 }
397 }
398
399 fn create_iterator_abuse_advice(&self) -> EducationalAdvice {
400 EducationalAdvice {
401 why_bad: "Manual loops are often less efficient and expressive than iterator chains.".to_string(),
402 how_to_fix: "Use iterator methods like map, filter, fold instead of manual loops when appropriate.".to_string(),
403 example_bad: Some("let mut result = Vec::new();\nfor item in items {\n if item > 0 {\n result.push(item * 2);\n }\n}".to_string()),
404 example_good: Some("let result: Vec<_> = items.iter()\n .filter(|&&x| x > 0)\n .map(|&x| x * 2)\n .collect();".to_string()),
405 rust_docs_link: Some("https://doc.rust-lang.org/book/ch13-02-iterators.html".to_string()),
406 best_practice_tip: Some("Iterator chains are often more efficient due to lazy evaluation and compiler optimizations.".to_string()),
407 }
408 }
409
410 fn create_panic_abuse_advice(&self) -> EducationalAdvice {
411 EducationalAdvice {
412 why_bad: "Excessive panics make programs unreliable and hard to debug in production.".to_string(),
413 how_to_fix: "Use Result types for recoverable errors, reserve panics for truly unrecoverable situations.".to_string(),
414 example_bad: Some("if input.is_empty() { panic!(\"Input cannot be empty!\"); }".to_string()),
415 example_good: Some("if input.is_empty() { return Err(\"Input cannot be empty\".into()); }".to_string()),
416 rust_docs_link: Some("https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html".to_string()),
417 best_practice_tip: Some("Panics should be used for programming errors, not for expected error conditions.".to_string()),
418 }
419 }
420
421 fn create_todo_comment_advice(&self) -> EducationalAdvice {
422 EducationalAdvice {
423 why_bad: "Too many TODO comments indicate incomplete or poorly planned code.".to_string(),
424 how_to_fix: "Either implement the TODOs or create proper issue tracking for future work.".to_string(),
425 example_bad: Some("// TODO: implement this\n// TODO: fix this bug\n// TODO: optimize this".to_string()),
426 example_good: Some("// Create GitHub issues for planned improvements\n// Implement critical functionality before committing".to_string()),
427 rust_docs_link: None,
428 best_practice_tip: Some("Use TODO sparingly and always with a specific plan for resolution.".to_string()),
429 }
430 }
431
432 fn create_unordered_imports_advice(&self) -> EducationalAdvice {
433 EducationalAdvice {
434 why_bad: "Unordered imports make it hard to find and manage dependencies.".to_string(),
435 how_to_fix: "Use rustfmt to automatically sort imports, or sort them manually by: std, external crates, local modules.".to_string(),
436 example_bad: Some("use my_crate::module;\nuse std::collections::HashMap;\nuse serde::Serialize;".to_string()),
437 example_good: Some("use std::collections::HashMap;\n\nuse serde::Serialize;\n\nuse my_crate::module;".to_string()),
438 rust_docs_link: Some("https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html".to_string()),
439 best_practice_tip: Some("Configure your editor to run rustfmt on save to maintain consistent formatting.".to_string()),
440 }
441 }
442
443 fn create_deep_module_nesting_advice(&self) -> EducationalAdvice {
444 EducationalAdvice {
445 why_bad: "Deep module nesting makes code navigation difficult and indicates poor architecture.".to_string(),
446 how_to_fix: "Flatten module structure, use re-exports to maintain clean public APIs.".to_string(),
447 example_bad: Some("mod a { mod b { mod c { mod d { /* code */ } } } }".to_string()),
448 example_good: Some("mod handlers;\nmod models;\nmod utils;\n\npub use handlers::*;".to_string()),
449 rust_docs_link: Some("https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html".to_string()),
450 best_practice_tip: Some("Keep module nesting to 2-3 levels maximum, use re-exports for convenience.".to_string()),
451 }
452 }
453}