Skip to content

Commit 0459cb8

Browse files
committed
csm env create - determine env name to use
1 parent 9fbe02d commit 0459cb8

1 file changed

Lines changed: 154 additions & 2 deletions

File tree

src/env.rs

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
use crate::csmrc::Config;
22

3+
use log::{debug, error};
4+
use serde::Deserialize;
5+
use std::io::{Error, ErrorKind};
6+
use std::path::Component;
7+
use std::process;
8+
39
#[derive(Debug, clap::Subcommand)]
410
pub enum Subcommand {
511
/// Create an environment
@@ -29,7 +35,153 @@ pub struct CreateArgs {
2935
name: Option<String>,
3036
}
3137

38+
/// Contains the fields we need from a parsed `robotmk-env.yml` file.
39+
#[derive(Deserialize)]
40+
struct RobotmkEnv {
41+
/// The name of the environment
42+
name: Option<String>,
43+
}
44+
45+
/// Attempt to parse a robotmk-env.yaml in the current directory.
46+
fn parse_robotmk_env_yaml() -> Result<RobotmkEnv, std::io::Error> {
47+
// TODO: Should we handle .yml too?
48+
let contents = std::fs::read_to_string("robotmk-env.yaml")?;
49+
serde_yaml_ng::from_str(&contents).map_err(|e| Error::new(ErrorKind::InvalidData, e))
50+
}
51+
52+
pub fn determine_env_name(args: CreateArgs) -> Option<String> {
53+
// If someone gave an explicit --name, use that first.
54+
if let Some(name) = args.name {
55+
debug!("Using '{}' as env name, given by CLI argument", name);
56+
return Some(name);
57+
}
58+
59+
// Fallback 1: Look for a name key in robotmk-env.yaml
60+
// We ignore errors from parse_robotmk_env_yaml() here, we'll fall back
61+
// below if we can't parse it for some reason
62+
if let Ok(env) = parse_robotmk_env_yaml()
63+
&& let Some(name) = env.name
64+
{
65+
debug!("Using '{}' as env name, found in robotmk-env.yaml", name);
66+
return Some(name);
67+
}
68+
69+
// Fallback 2: Current directory name
70+
match std::env::current_dir() {
71+
Err(e) => {
72+
debug!("Could not determine current directory: {}", e);
73+
None
74+
}
75+
Ok(pathbuf) => match pathbuf.components().next_back() {
76+
Some(Component::Normal(s)) => match s.to_str().map(String::from) {
77+
Some(name) => {
78+
debug!(
79+
"Using '{}' as env name, taken from current directory name",
80+
name
81+
);
82+
Some(name)
83+
}
84+
_ => None, // Likely could not convert path name to utf-8
85+
},
86+
_ => None, // In theory, I think this should never happen
87+
},
88+
}
89+
}
90+
3291
pub fn run(config: Config, subcommand: Subcommand) {
33-
println!("{:?}", config);
34-
println!("{:?}", subcommand);
92+
match subcommand {
93+
Subcommand::Create(args) => {
94+
let Some(env_name) = determine_env_name(args) else {
95+
error!("No environment name could be determined. You can specify one with --name");
96+
process::exit(1); // TODO: Probably better to return Result and let main() do this.
97+
};
98+
println!("env: {}", env_name);
99+
}
100+
_ => {
101+
println!("{:?}", config);
102+
println!("{:?}", subcommand);
103+
}
104+
}
105+
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
use super::*;
110+
use std::env;
111+
use std::fs;
112+
113+
/// Run a test in a temporary directory with an optional robotmk-env.yaml in it
114+
fn run_in_temp_dir<F>(dir_name: &str, yaml_content: Option<&str>, test_fn: F)
115+
where
116+
F: FnOnce(),
117+
{
118+
let temp_dir = env::temp_dir().join(dir_name);
119+
fs::create_dir_all(&temp_dir).unwrap();
120+
121+
if let Some(content) = yaml_content {
122+
let yaml_path = temp_dir.join("robotmk-env.yaml");
123+
fs::write(&yaml_path, content).unwrap();
124+
}
125+
126+
let original_dir = env::current_dir().unwrap();
127+
env::set_current_dir(&temp_dir).unwrap();
128+
129+
test_fn();
130+
131+
env::set_current_dir(original_dir).unwrap();
132+
fs::remove_dir_all(&temp_dir).unwrap();
133+
}
134+
135+
#[test]
136+
fn test_determine_env_name_with_cli_arg() {
137+
let args = CreateArgs {
138+
name: Some("test-env".to_string()),
139+
};
140+
141+
let result = determine_env_name(args);
142+
assert_eq!(result, Some("test-env".to_string()));
143+
}
144+
145+
#[test]
146+
fn test_determine_env_name_cli_arg_overrides_yaml() {
147+
run_in_temp_dir("csm_test_override", Some("name: yaml-env-name"), || {
148+
let args = CreateArgs {
149+
name: Some("cli-override".to_string()),
150+
};
151+
152+
let result = determine_env_name(args);
153+
assert_eq!(result, Some("cli-override".to_string()));
154+
});
155+
}
156+
157+
#[test]
158+
fn test_determine_env_name_robotmk_env_yaml() {
159+
// (dir_name, yaml, expected)
160+
let test_cases = vec![
161+
(
162+
"valid_yaml",
163+
Some("name: yaml-env-name\nother_field: value"),
164+
"yaml-env-name",
165+
),
166+
(
167+
"yaml_no_name",
168+
Some("other_field: value\nyet_another: field"),
169+
"yaml_no_name",
170+
),
171+
(
172+
"invalid_yaml",
173+
Some("invalid: yaml: content: \"unclosed"),
174+
"invalid_yaml",
175+
),
176+
("no_yaml", None, "no_yaml"),
177+
];
178+
179+
for (dir_name, yaml, expected) in test_cases {
180+
run_in_temp_dir(dir_name, yaml, || {
181+
let args = CreateArgs { name: None };
182+
let result = determine_env_name(args);
183+
assert_eq!(result.unwrap(), expected, "Failed case: {}", dir_name);
184+
});
185+
}
186+
}
35187
}

0 commit comments

Comments
 (0)