lmn_core/command/
configure_template.rs1use crate::command::{Body, Command};
2use crate::config::error::ConfigError;
3use crate::execution::RunStats;
4use std::error::Error;
5use std::fs::File;
6use std::io::Write;
7use std::path::PathBuf;
8
9const TEMPLATE_ROOT_DIR: &str = ".templates";
12
13pub enum TemplateKind {
16 Request,
17 Response,
18}
19
20impl TemplateKind {
21 fn dir(&self) -> &'static str {
22 match self {
23 TemplateKind::Request => "requests",
24 TemplateKind::Response => "responses",
25 }
26 }
27}
28
29pub struct ConfigureTemplateCommand {
32 pub alias: String,
33 pub body: Option<Body>,
34 pub template_path: Option<PathBuf>,
35 pub kind: TemplateKind,
36}
37
38impl Command for ConfigureTemplateCommand {
39 async fn execute(self) -> Result<Option<RunStats>, Box<dyn Error>> {
40 let content: String = match (self.body, self.template_path) {
41 (Some(body), _) => body.into(),
42 (_, Some(path)) => {
43 let raw = std::fs::read_to_string(&path).map_err(|_| {
44 ConfigError::TemplateNotFound(path.to_string_lossy().into_owned())
45 })?;
46 serde_json::from_str::<serde_json::Value>(&raw)
47 .map_err(|_| ConfigError::InvalidFormat(path.to_string_lossy().into_owned()))?;
48 raw
49 }
50 (None, None) => return Err(Box::new(ConfigError::GeneralError)),
51 };
52
53 let mut file_name = PathBuf::from(&self.alias);
54 file_name.set_extension("json");
55 let full_file_alias = file_name
56 .to_str()
57 .ok_or_else(|| Box::<dyn Error>::from(ConfigError::InvalidFormat(self.alias)))?;
58
59 create_file(self.kind.dir(), full_file_alias.to_string(), content)
60 .map_err(|e| Box::new(e) as Box<dyn Error>)?;
61 Ok(None)
62 }
63}
64
65fn create_file(sub_dir: &str, file_name: String, content: String) -> Result<(), ConfigError> {
66 let dir = PathBuf::from(TEMPLATE_ROOT_DIR).join(sub_dir);
67 std::fs::create_dir_all(&dir).map_err(|_| ConfigError::Fs(file_name.clone()))?;
68
69 let file_path = dir.join(&file_name);
70 if file_path.exists() {
71 return Err(ConfigError::TemplateAlreadyExists(file_name));
72 }
73
74 let mut file = File::create(&file_path).map_err(|_| ConfigError::Fs(file_name.clone()))?;
75 file.write_all(content.as_bytes())
76 .map_err(|_| ConfigError::Fs(file_name))
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn request_kind_dir() {
85 assert_eq!(TemplateKind::Request.dir(), "requests");
86 }
87
88 #[test]
89 fn response_kind_dir() {
90 assert_eq!(TemplateKind::Response.dir(), "responses");
91 }
92
93 #[test]
94 fn create_file_writes_content() {
95 let file_name = "__test_create_file_writes.json".to_string();
96 let path = PathBuf::from(TEMPLATE_ROOT_DIR)
97 .join("requests")
98 .join(&file_name);
99 let _ = std::fs::remove_file(&path);
100
101 let result = create_file("requests", file_name.clone(), r#"{"ok":true}"#.to_string());
102 assert!(result.is_ok());
103 assert!(path.exists());
104 let _ = std::fs::remove_file(&path);
105 }
106
107 #[test]
108 fn create_file_rejects_duplicate() {
109 let file_name = "__test_create_file_duplicate.json".to_string();
110 let path = PathBuf::from(TEMPLATE_ROOT_DIR)
111 .join("requests")
112 .join(&file_name);
113 let _ = std::fs::remove_file(&path);
114
115 create_file("requests", file_name.clone(), "{}".to_string()).unwrap();
116 let result = create_file("requests", file_name.clone(), "{}".to_string());
117 assert!(matches!(result, Err(ConfigError::TemplateAlreadyExists(_))));
118 let _ = std::fs::remove_file(&path);
119 }
120}