Skip to content

Commit f08b360

Browse files
committed
feat: env-based root path selection & extract config constants
- Use APP_ENV to switch between current_dir and current_exe as root path - Move config/report file names to constants - Update error messages and output for better user experience
1 parent 8d9f7a8 commit f08b360

File tree

5 files changed

+58
-27
lines changed

5 files changed

+58
-27
lines changed

src/config/constants.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub const CONFIG_FILE_NAME: &str = "config.json";
2+
3+
pub const REPORT_FILE_NAME: &str = "report.txt";
4+
5+
pub const CATEGORY_ORDER: &[&str] = &["feat", "fix", "docs", "style", "refactor", "test", "chore"];

src/config/error.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::config::constants::CONFIG_FILE_NAME;
2+
13
#[derive(Debug)]
24
pub enum ConfigError {
35
FileNotFound,
@@ -13,13 +15,15 @@ impl std::fmt::Display for ConfigError {
1315
ConfigError::FileNotFound => {
1416
write!(
1517
f,
16-
"\nPlease make sure there is a config.json file in the program directory."
18+
"\nPlease make sure there is a {} file in the program directory.",
19+
CONFIG_FILE_NAME
1720
)
1821
}
1922
ConfigError::ParseError => {
2023
write!(
2124
f,
22-
"\nFailed to parse config.json, please check the file format."
25+
"\nFailed to parse {}, please check the file format.",
26+
CONFIG_FILE_NAME
2327
)
2428
}
2529
ConfigError::InvalidConfig(msg) => {

src/config/mod.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
1+
pub mod constants;
12
mod error;
23
mod schema;
34
mod store;
45
mod types;
56

7+
use constants::CONFIG_FILE_NAME;
68
use error::ConfigError;
79
use schema::ConfigFile;
8-
use std::path::Path;
10+
use std::path::PathBuf;
11+
912
pub use store::{init, is_chinese, print_config};
1013
pub use types::Config;
1114

12-
const CONFIG_FILE_PATH: &str = "config.json";
15+
fn find_config_file(root_path: &PathBuf) -> Option<PathBuf> {
16+
let config_file_path = root_path.join(CONFIG_FILE_NAME);
17+
if config_file_path.exists() {
18+
return Some(config_file_path);
19+
}
20+
21+
// 3. If both are not found, return None
22+
None
23+
}
1324

1425
/// Initialize configuration from file or use default settings
15-
pub fn init_config() -> Result<Config, ConfigError> {
16-
if !Path::new(CONFIG_FILE_PATH).exists() {
17-
return Err(ConfigError::FileNotFound);
18-
}
26+
pub fn init_config(root_path: &PathBuf) -> Result<Config, ConfigError> {
27+
let config_path = find_config_file(root_path).ok_or(ConfigError::FileNotFound)?;
1928

20-
ConfigFile::from_file(CONFIG_FILE_PATH)
29+
ConfigFile::from_file(config_path.to_str().unwrap())
2130
.map_err(|_e| ConfigError::ParseError)
2231
.and_then(|file_config| {
2332
let config = Config::new_from_file(&file_config)?;

src/main.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod config;
22
mod i18n;
33
mod utils;
44

5-
use std::collections::HashMap;
5+
use std::{collections::HashMap, path::PathBuf};
66

77
use config::{init_config, print_config};
88
use i18n::t;
@@ -15,12 +15,12 @@ use utils::{
1515
save_report::save_report_markdown,
1616
};
1717

18-
fn run() -> Result<(), Box<dyn std::error::Error>> {
18+
fn run(root_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
1919
// Initialize global configuration.
2020
// After initialization, the config is stored in a global OnceLock singleton,
2121
// and can be accessed from other modules via `config::store::global()`,
2222
// such as in the i18n module.
23-
let config = init_config()?;
23+
let config = init_config(&root_path)?;
2424

2525
print_config();
2626

@@ -68,8 +68,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
6868
.push(log_info);
6969
}
7070

71-
// save_report_markdown(&result, "report.txt").expect(t("report_gen_error"));
72-
save_report_markdown(&result, "report.txt")?;
71+
save_report_markdown(&result, &root_path)?;
7372

7473
exit_on_keypress(Some(t("press_to_exit")));
7574
}
@@ -78,13 +77,24 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
7877
}
7978

8079
fn main() {
81-
// Print the current working directory
82-
// When the program crashes, it can help locate the cause
80+
// Get the APP_ENV environment variable from the startup command (see Makefile.toml).
81+
// Since the working directories differ between development and production,
82+
// we need to handle them separately.
83+
let env = std::env::var("APP_ENV").unwrap_or_else(|_| "production".to_string());
84+
85+
let root_path = if env == "development" {
86+
std::env::current_dir().unwrap()
87+
} else {
88+
std::env::current_exe()
89+
.unwrap()
90+
.parent()
91+
.unwrap()
92+
.to_path_buf()
93+
};
94+
95+
// Print the root path to help locate issues if the program crashes.
8396
println!("");
84-
println!(
85-
"Current working directory: \n{}",
86-
&std::env::current_dir().unwrap().display()
87-
);
97+
println!("\nProgram root directory:\n{}", root_path.display());
8898

8999
// Using `?` to propagate errors to `main` causes Rust's default error output
90100
// (via the `std::process::Termination` trait) to use the Debug format (`{:?}`)
@@ -93,7 +103,7 @@ fn main() {
93103
//
94104
// Wrapping the main logic in a separate `run` function and handling errors
95105
// explicitly with `eprintln!` ensures that the Display output is used.
96-
if let Err(e) = run() {
106+
if let Err(e) = run(root_path) {
97107
eprintln!("{}", e); // Use Display format to output the error
98108
std::process::exit(1);
99109
}

src/utils/save_report.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
use crate::config::constants::{CATEGORY_ORDER, REPORT_FILE_NAME};
12
use crate::i18n::t;
23
use crate::utils::format_log::LogInfo;
34
use std::collections::HashMap;
45
use std::fs::File;
56
use std::io::Write;
6-
use std::path::Path;
7-
8-
const CATEGORY_ORDER: &[&str] = &["feat", "fix", "docs", "style", "refactor", "test", "chore"];
7+
use std::path::{Path, PathBuf};
98

109
fn get_category_label(type_name: &str) -> &str {
1110
match type_name {
@@ -23,7 +22,7 @@ fn get_category_label(type_name: &str) -> &str {
2322
/// Save Markdown report
2423
pub fn save_report_markdown(
2524
result: &HashMap<String, HashMap<String, Vec<LogInfo>>>,
26-
path: &str,
25+
root_path: &PathBuf,
2726
) -> std::io::Result<()> {
2827
// Check if result is empty
2928
if result.is_empty() {
@@ -53,10 +52,14 @@ pub fn save_report_markdown(
5352
md.push('\n');
5453
}
5554

56-
let mut file = File::create(Path::new(path))?;
55+
let output_path = root_path.join(REPORT_FILE_NAME);
56+
let mut file = File::create(Path::new(&output_path))?;
5757
file.write_all(md.as_bytes())?;
5858

59-
println!("{}", t("report_saved").replace("{}", path));
59+
println!(
60+
"{}",
61+
t("report_saved").replace("{}", output_path.to_str().unwrap())
62+
);
6063
println!("");
6164

6265
Ok(())

0 commit comments

Comments
 (0)