mk_lib/schema/
use_npm.rs

1use std::fs::File;
2use std::io::BufReader;
3use std::path::Path;
4
5use anyhow::Context as _;
6use hashbrown::HashMap;
7use serde::Deserialize;
8
9use crate::defaults::default_node_package_manager;
10use crate::file::ToUtf8 as _;
11
12use super::Task;
13
14#[derive(Debug, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct NpmPackage {
17  /// The name of the package
18  pub name: String,
19
20  /// The version of the package
21  pub version: Option<String>,
22
23  /// The path to the package
24  pub scripts: Option<HashMap<String, String>>,
25
26  /// The package manager to use
27  pub package_manager: Option<String>,
28}
29
30// TODO: Make use of the work_dir field
31#[derive(Debug, Deserialize)]
32pub struct UseNpmArgs {
33  /// The package manager to use
34  #[serde(default)]
35  pub package_manager: Option<String>,
36
37  /// The working directory to run the command in
38  #[serde(default)]
39  pub work_dir: Option<String>,
40}
41
42#[derive(Debug, Deserialize)]
43#[serde(untagged)]
44pub enum UseNpm {
45  Bool(bool),
46  UseNpm(Box<UseNpmArgs>),
47}
48
49impl UseNpm {
50  pub fn capture(&self) -> anyhow::Result<HashMap<String, Task>> {
51    match self {
52      UseNpm::Bool(true) => self.capture_tasks(),
53      UseNpm::UseNpm(args) => args.capture_tasks(),
54      _ => Ok(HashMap::new()),
55    }
56  }
57
58  fn capture_tasks(&self) -> anyhow::Result<HashMap<String, Task>> {
59    UseNpmArgs {
60      package_manager: None,
61      work_dir: None,
62    }
63    .capture_tasks()
64  }
65}
66
67impl UseNpmArgs {
68  pub fn capture_tasks(&self) -> anyhow::Result<HashMap<String, Task>> {
69    let path = Path::new("package.json");
70    if !path.exists() || !path.is_file() {
71      return Err(anyhow::anyhow!("package.json does not exist"));
72    }
73
74    let file = File::open(path).context(format!("Failed to open file - {}", path.to_utf8()?))?;
75    let reader = BufReader::new(file);
76
77    let package: NpmPackage = serde_json::from_reader(reader)?;
78    let package_manager: &str = &self
79      .package_manager
80      .clone()
81      .unwrap_or_else(default_node_package_manager);
82
83    assert!(!package_manager.is_empty());
84
85    let tasks: HashMap<String, Task> = package
86      .scripts
87      .unwrap_or_default()
88      .into_iter()
89      .map(|(k, _)| (k.clone(), Task::String(format!("{package_manager} run {k}"))))
90      .collect();
91    Ok(tasks)
92  }
93}
94
95#[cfg(test)]
96mod tests {
97  use super::*;
98
99  #[test]
100  fn test_use_npm_1() -> anyhow::Result<()> {
101    let json = r#"{
102      "name": "test",
103      "version": "1.0.0",
104      "scripts": {
105        "build": "echo 'Building'",
106        "test": "echo 'Testing'"
107      }
108    }"#;
109    let package = serde_json::from_str::<NpmPackage>(json)?;
110    assert_eq!(package.name, "test");
111    assert_eq!(package.version, Some("1.0.0".to_string()));
112    assert_eq!(
113      package.scripts,
114      Some({
115        let mut map = HashMap::new();
116        map.insert("build".to_string(), "echo 'Building'".to_string());
117        map.insert("test".to_string(), "echo 'Testing'".to_string());
118        map
119      })
120    );
121    Ok(())
122  }
123
124  #[test]
125  fn test_use_npm_2() -> anyhow::Result<()> {
126    let yaml = "true";
127
128    let use_npm = serde_yaml::from_str::<UseNpm>(yaml)?;
129    if let UseNpm::Bool(value) = use_npm {
130      assert!(value);
131    } else {
132      panic!("Invalid value");
133    }
134
135    Ok(())
136  }
137
138  #[test]
139  fn test_use_npm_3() -> anyhow::Result<()> {
140    let yaml = "false";
141
142    let use_npm = serde_yaml::from_str::<UseNpm>(yaml)?;
143    if let UseNpm::Bool(value) = use_npm {
144      assert!(!value);
145    } else {
146      panic!("Invalid value");
147    }
148
149    Ok(())
150  }
151
152  #[test]
153  fn test_use_npm_4() -> anyhow::Result<()> {
154    let yaml = "
155      package_manager: npm
156    ";
157
158    let use_npm = serde_yaml::from_str::<UseNpm>(yaml)?;
159    if let UseNpm::UseNpm(args) = use_npm {
160      assert_eq!(args.package_manager, Some("npm".to_string()));
161    } else {
162      panic!("Invalid value");
163    }
164
165    Ok(())
166  }
167
168  #[test]
169  fn test_use_npm_5() -> anyhow::Result<()> {
170    let yaml = "
171      package_manager: yarn
172      work_dir: /path/to/dir
173    ";
174
175    let use_npm = serde_yaml::from_str::<UseNpm>(yaml)?;
176    if let UseNpm::UseNpm(args) = use_npm {
177      assert_eq!(args.package_manager, Some("yarn".to_string()));
178      assert_eq!(args.work_dir, Some("/path/to/dir".to_string()));
179    } else {
180      panic!("Invalid value");
181    }
182
183    Ok(())
184  }
185}