Skip to main content

microvms_edges/control/
project.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Reading a project directory's dependency files (#74, #264).
3//!
4//! Which pair counts, and every refusal, is `microvms_app::control::project`. This asks the
5//! filesystem which names are present and reads the two files the rule names.
6
7use std::path::Path;
8
9use microvms_app::control::artifact::ProjectFiles;
10use microvms_app::control::project::project_ecosystem;
11use microvms_app::error::{Error, ErrorKind};
12
13/// The one manifest+lockfile pair in `dir`, read into [`ProjectFiles`], or the refusal
14/// [`project_ecosystem`] makes: no pair, a half pair, or two ecosystems.
15pub fn read_project_files(dir: impl AsRef<Path>) -> Result<ProjectFiles, Error> {
16    let dir = dir.as_ref();
17    let ecosystem = project_ecosystem(&dir.display().to_string(), |name| dir.join(name).is_file())?;
18    let read = |name: &str| {
19        let path = dir.join(name);
20        std::fs::read(&path).map_err(|error| {
21            Error::new(
22                ErrorKind::Precondition,
23                format!("could not read {}: {error}", path.display()),
24            )
25            .with_source(error)
26        })
27    };
28    Ok(ProjectFiles {
29        ecosystem,
30        manifest: read(ecosystem.manifest_name())?,
31        lockfile: read(ecosystem.lockfile_name())?,
32    })
33}
34
35#[cfg(test)]
36mod tests {
37    use microvms_app::control::artifact::Ecosystem;
38
39    use super::*;
40
41    /// **#74: the pair on disk is read whole**, the manifest and the lockfile under the
42    /// ecosystem's names, and nothing else in the directory.
43    #[test]
44    fn a_project_dir_with_one_pair_reads_both_files() {
45        let dir = tempfile::tempdir().expect("a temp dir");
46        std::fs::write(dir.path().join("pyproject.toml"), b"[project]").expect("writes");
47        std::fs::write(dir.path().join("uv.lock"), b"version = 1").expect("writes");
48        std::fs::write(dir.path().join(".env"), b"SECRET=1").expect("writes");
49
50        let files = read_project_files(dir.path()).expect("one pair, one ecosystem");
51        assert_eq!(files.ecosystem, Ecosystem::Uv);
52        assert_eq!(files.manifest, b"[project]");
53        assert_eq!(files.lockfile, b"version = 1");
54    }
55
56    /// The rule's refusals reach a caller through the read, naming the directory.
57    #[test]
58    fn a_project_dir_without_a_pair_is_refused_naming_it() {
59        let dir = tempfile::tempdir().expect("a temp dir");
60        std::fs::write(dir.path().join("pyproject.toml"), b"[project]").expect("writes");
61        let error = read_project_files(dir.path()).expect_err("no lockfile to key on");
62        assert_eq!(error.kind(), ErrorKind::Precondition);
63        let message = error.to_string();
64        assert!(message.contains("uv lock"), "{message}");
65        assert!(
66            message.contains(&dir.path().display().to_string()),
67            "{message}"
68        );
69    }
70}