codelens_core/config/
file.rs1use std::path::Path;
4
5use serde::Deserialize;
6
7use crate::error::{Error, Result};
8
9use super::{Config, FilterConfig, OutputConfig, OutputFormatType, SortBy};
10use crate::walker::WalkerConfig;
11
12#[derive(Debug, Deserialize, Default)]
14#[serde(default)]
15struct ConfigFile {
16 excludes: Option<String>,
18 includes: Option<String>,
20 exclude_files: Option<String>,
22 include_files: Option<String>,
24 exclude_dirs: Option<String>,
26 lang: Option<String>,
28 min_lines: Option<usize>,
30 max_lines: Option<usize>,
32 output: Option<String>,
34 output_file: Option<String>,
36 threads: Option<usize>,
38 depth: Option<usize>,
40 sort: Option<String>,
42 top: Option<usize>,
44 git_info: Option<bool>,
46 no_gitignore: Option<bool>,
48 no_smart_exclude: Option<bool>,
50 parallel: Option<bool>,
52 summary: Option<bool>,
54 verbose: Option<bool>,
56 quiet: Option<bool>,
58}
59
60pub fn load_config_file(path: &Path) -> Result<Config> {
62 let content = std::fs::read_to_string(path).map_err(|e| Error::FileRead {
63 path: path.to_path_buf(),
64 source: e,
65 })?;
66
67 let file: ConfigFile = toml::from_str(&content).map_err(|e| Error::ConfigParse {
68 path: path.to_path_buf(),
69 source: e,
70 })?;
71
72 Ok(Config {
73 walker: WalkerConfig {
74 threads: file.threads.unwrap_or_else(num_cpus::get),
75 use_gitignore: !file.no_gitignore.unwrap_or(false),
76 max_depth: file.depth,
77 ..Default::default()
78 },
79 filter: FilterConfig {
80 excludes: parse_comma_list(&file.excludes),
81 includes: parse_comma_list(&file.includes),
82 exclude_files: parse_comma_list(&file.exclude_files),
83 include_files: parse_comma_list(&file.include_files),
84 exclude_dirs: parse_comma_list(&file.exclude_dirs),
85 languages: parse_comma_list(&file.lang),
86 min_lines: file.min_lines,
87 max_lines: file.max_lines,
88 smart_exclude: !file.no_smart_exclude.unwrap_or(false),
89 ..Default::default()
90 },
91 output: OutputConfig {
92 format: parse_format(&file.output),
93 file: file.output_file.map(Into::into),
94 sort_by: parse_sort(&file.sort),
95 top_n: file.top,
96 show_git_info: file.git_info.unwrap_or(false),
97 summary_only: file.summary.unwrap_or(false),
98 verbose: file.verbose.unwrap_or(false),
99 quiet: file.quiet.unwrap_or(false),
100 },
101 })
102}
103
104fn parse_comma_list(s: &Option<String>) -> Vec<String> {
105 s.as_ref()
106 .map(|s| s.split(',').map(|p| p.trim().to_string()).collect())
107 .unwrap_or_default()
108}
109
110fn parse_format(s: &Option<String>) -> OutputFormatType {
111 match s.as_deref() {
112 Some("json") => OutputFormatType::Json,
113 Some("csv") => OutputFormatType::Csv,
114 Some("markdown") | Some("md") => OutputFormatType::Markdown,
115 Some("html") => OutputFormatType::Html,
116 _ => OutputFormatType::Console,
117 }
118}
119
120fn parse_sort(s: &Option<String>) -> SortBy {
121 match s.as_deref() {
122 Some("files") => SortBy::Files,
123 Some("code") => SortBy::Code,
124 Some("name") => SortBy::Name,
125 Some("size") => SortBy::Size,
126 _ => SortBy::Lines,
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use std::io::Write;
134 use tempfile::NamedTempFile;
135
136 #[test]
137 fn test_load_config_file() {
138 let mut file = NamedTempFile::new().unwrap();
139 writeln!(
140 file,
141 r#"
142 excludes = "*test*,*mock*"
143 lang = "rust,go"
144 output = "json"
145 threads = 4
146 depth = 5
147 git_info = true
148 "#
149 )
150 .unwrap();
151
152 let config = load_config_file(file.path()).unwrap();
153 assert_eq!(config.filter.excludes, vec!["*test*", "*mock*"]);
154 assert_eq!(config.filter.languages, vec!["rust", "go"]);
155 assert_eq!(config.output.format, OutputFormatType::Json);
156 assert_eq!(config.walker.threads, 4);
157 assert_eq!(config.walker.max_depth, Some(5));
158 assert!(config.output.show_git_info);
159 }
160
161 #[test]
162 fn test_parse_comma_list() {
163 assert_eq!(
164 parse_comma_list(&Some("a, b, c".to_string())),
165 vec!["a", "b", "c"]
166 );
167 assert!(parse_comma_list(&None).is_empty());
168 }
169}