1use std::env;
8use std::path::{Path, PathBuf};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum LogLevel {
13 Quiet,
15 #[default]
17 Normal,
18 Verbose,
20}
21
22impl LogLevel {
23 fn parse(raw: Option<&str>) -> Self {
24 match raw.map(str::trim) {
25 Some("quiet") => Self::Quiet,
26 Some("verbose") => Self::Verbose,
27 _ => Self::Normal,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum ColorMode {
36 #[default]
38 Auto,
39 Always,
41 Never,
43}
44
45impl ColorMode {
46 fn parse(raw: Option<&str>) -> Self {
47 match raw.map(str::trim) {
48 Some("always") => Self::Always,
49 Some("never") => Self::Never,
50 _ => Self::Auto,
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
57pub struct Context {
58 dispatcher_version: Option<semver::Version>,
59 project_root: Option<PathBuf>,
60 config_path: Option<PathBuf>,
61 output_mode: crate::output::OutputMode,
62 log_level: LogLevel,
63 color: ColorMode,
64}
65
66impl Context {
67 #[must_use]
71 pub fn from_env() -> Self {
72 let dispatcher_version = env::var("READY_SET_DISPATCHER_VERSION")
73 .ok()
74 .and_then(|raw| semver::Version::parse(raw.trim()).ok());
75
76 let project_root = env::var_os("READY_SET_PROJECT_ROOT")
77 .filter(|s| !s.is_empty())
78 .map(PathBuf::from);
79
80 let config_path = env::var_os("READY_SET_CONFIG_PATH")
81 .filter(|s| !s.is_empty())
82 .map(PathBuf::from);
83
84 let output_mode =
85 crate::output::OutputMode::parse(env::var("READY_SET_OUTPUT").ok().as_deref());
86 let log_level = LogLevel::parse(env::var("READY_SET_LOG").ok().as_deref());
87 let color = ColorMode::parse(env::var("READY_SET_COLOR").ok().as_deref());
88
89 Self {
90 dispatcher_version,
91 project_root,
92 config_path,
93 output_mode,
94 log_level,
95 color,
96 }
97 }
98
99 #[must_use]
101 pub const fn default_for_tests() -> Self {
102 Self {
103 dispatcher_version: None,
104 project_root: None,
105 config_path: None,
106 output_mode: crate::output::OutputMode::Human,
107 log_level: LogLevel::Normal,
108 color: ColorMode::Auto,
109 }
110 }
111
112 #[must_use]
114 pub const fn dispatcher_version(&self) -> Option<&semver::Version> {
115 self.dispatcher_version.as_ref()
116 }
117
118 #[must_use]
120 pub fn project_root(&self) -> Option<&Path> {
121 self.project_root.as_deref()
122 }
123
124 #[must_use]
126 pub fn config_path(&self) -> Option<&Path> {
127 self.config_path.as_deref()
128 }
129
130 #[must_use]
132 pub const fn output_mode(&self) -> crate::output::OutputMode {
133 self.output_mode
134 }
135
136 #[must_use]
138 pub const fn log_level(&self) -> LogLevel {
139 self.log_level
140 }
141
142 #[must_use]
144 pub const fn color(&self) -> ColorMode {
145 self.color
146 }
147
148 pub fn project_root_or_cwd(&self) -> std::io::Result<PathBuf> {
155 if let Some(root) = &self.project_root {
156 return Ok(root.clone());
157 }
158 env::current_dir()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn parses_log_level_with_fallbacks() {
168 assert_eq!(LogLevel::parse(Some("quiet")), LogLevel::Quiet);
169 assert_eq!(LogLevel::parse(Some("verbose")), LogLevel::Verbose);
170 assert_eq!(LogLevel::parse(Some("normal")), LogLevel::Normal);
171 assert_eq!(LogLevel::parse(Some("bogus")), LogLevel::Normal);
173 assert_eq!(LogLevel::parse(None), LogLevel::Normal);
174 }
175
176 #[test]
177 fn parses_color_mode_with_fallbacks() {
178 assert_eq!(ColorMode::parse(Some("always")), ColorMode::Always);
179 assert_eq!(ColorMode::parse(Some("never")), ColorMode::Never);
180 assert_eq!(ColorMode::parse(Some("auto")), ColorMode::Auto);
181 assert_eq!(ColorMode::parse(Some("rainbow")), ColorMode::Auto);
182 assert_eq!(ColorMode::parse(None), ColorMode::Auto);
183 }
184}