1use std::path::Path;
7
8use bynk_fmt::{FormatOptions, IndentStyle};
9use serde::Deserialize;
10
11#[derive(Debug, Deserialize, Default)]
12struct RawConfig {
13 #[serde(default)]
14 project: ProjectSection,
15 #[serde(default)]
16 paths: PathsSection,
17 #[serde(default)]
18 fmt: FmtSection,
19 #[serde(default)]
20 lsp: LspSection,
21}
22
23#[derive(Debug, Deserialize, Default, Clone)]
24struct ProjectSection {
25 #[serde(default)]
26 pub name: Option<String>,
27 #[serde(default)]
28 pub version: Option<String>,
29}
30
31#[derive(Debug, Deserialize, Clone)]
32struct PathsSection {
33 #[serde(default = "default_include")]
37 pub include: Vec<String>,
38 #[serde(default)]
41 #[allow(dead_code)]
42 pub exclude: Vec<String>,
43 #[serde(default = "default_out")]
44 pub out: String,
45}
46
47impl Default for PathsSection {
48 fn default() -> Self {
49 Self {
50 include: default_include(),
51 exclude: Vec::new(),
52 out: default_out(),
53 }
54 }
55}
56
57fn default_include() -> Vec<String> {
58 vec!["src".into()]
59}
60fn default_out() -> String {
61 "out".into()
62}
63
64#[derive(Debug, Deserialize, Clone)]
65struct FmtSection {
66 #[serde(default = "default_indent")]
67 pub indent: String,
68 #[serde(default)]
69 pub indent_width: Option<u8>,
70 #[serde(default = "default_max_line_width")]
71 pub max_line_width: u32,
72 #[serde(default = "default_trailing_comma")]
73 pub trailing_comma: bool,
74}
75
76impl Default for FmtSection {
77 fn default() -> Self {
78 Self {
79 indent: default_indent(),
80 indent_width: None,
81 max_line_width: default_max_line_width(),
82 trailing_comma: default_trailing_comma(),
83 }
84 }
85}
86
87fn default_indent() -> String {
88 "tab".into()
89}
90fn default_max_line_width() -> u32 {
91 100
92}
93fn default_trailing_comma() -> bool {
94 true
95}
96
97#[derive(Debug, Deserialize, Clone)]
98struct LspSection {
99 #[serde(default = "default_diagnostics_mode")]
100 pub diagnostics_mode: String,
101 #[serde(default = "default_diagnostics_debounce_ms")]
102 pub diagnostics_debounce_ms: u64,
103}
104
105impl Default for LspSection {
106 fn default() -> Self {
107 Self {
108 diagnostics_mode: default_diagnostics_mode(),
109 diagnostics_debounce_ms: default_diagnostics_debounce_ms(),
110 }
111 }
112}
113
114fn default_diagnostics_mode() -> String {
115 "live".into()
116}
117fn default_diagnostics_debounce_ms() -> u64 {
118 300
119}
120
121#[derive(Debug, Clone)]
123pub struct ProjectConfig {
124 #[allow(dead_code)]
125 pub project_name: Option<String>,
126 #[allow(dead_code)]
127 pub project_version: Option<String>,
128 pub src_dir: String,
129 #[allow(dead_code)]
130 pub out_dir: String,
131 pub indent: IndentStyle,
132 pub max_line_width: u32,
133 pub trailing_comma: bool,
134 #[allow(dead_code)]
135 pub diagnostics_mode: DiagnosticsMode,
136 pub diagnostics_debounce_ms: u64,
137}
138
139impl Default for ProjectConfig {
140 fn default() -> Self {
141 Self {
142 project_name: None,
143 project_version: None,
144 src_dir: "src".into(),
145 out_dir: "out".into(),
146 indent: IndentStyle::Tab,
147 max_line_width: 100,
148 trailing_comma: true,
149 diagnostics_mode: DiagnosticsMode::Live,
150 diagnostics_debounce_ms: 300,
151 }
152 }
153}
154
155impl ProjectConfig {
156 pub fn format_options(&self) -> FormatOptions {
157 FormatOptions {
158 indent: self.indent,
159 max_line_width: self.max_line_width,
160 trailing_comma: self.trailing_comma,
161 }
162 }
163}
164
165#[allow(dead_code)]
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum DiagnosticsMode {
168 Live,
169 OnSave,
170}
171
172pub fn load_config(root: &Path) -> Option<ProjectConfig> {
174 let path = root.join("bynk.toml");
175 let source = std::fs::read_to_string(&path).ok()?;
176 let raw: RawConfig = toml::from_str(&source).ok()?;
177 let indent = match raw.fmt.indent.as_str() {
178 "tab" => IndentStyle::Tab,
179 "spaces" => IndentStyle::Spaces(raw.fmt.indent_width.unwrap_or(2)),
180 _ => IndentStyle::Tab,
181 };
182 let diagnostics_mode = match raw.lsp.diagnostics_mode.as_str() {
183 "on_save" => DiagnosticsMode::OnSave,
184 _ => DiagnosticsMode::Live,
185 };
186 Some(ProjectConfig {
187 project_name: raw.project.name,
188 project_version: raw.project.version,
189 src_dir: raw
192 .paths
193 .include
194 .first()
195 .cloned()
196 .unwrap_or_else(|| "src".into()),
197 out_dir: raw.paths.out,
198 indent,
199 max_line_width: raw.fmt.max_line_width,
200 trailing_comma: raw.fmt.trailing_comma,
201 diagnostics_mode,
202 diagnostics_debounce_ms: raw.lsp.diagnostics_debounce_ms,
203 })
204}