binstalk_manifests/
crates_manifests.rs

1use std::{
2    collections::BTreeMap,
3    fs,
4    io::{self, Seek},
5    path::Path,
6};
7
8use fs_lock::FileLock;
9use miette::Diagnostic;
10use thiserror::Error as ThisError;
11
12use crate::{
13    binstall_crates_v1::{Error as BinstallCratesV1Error, Records as BinstallCratesV1Records},
14    cargo_crates_v1::{CratesToml, CratesTomlParseError},
15    crate_info::CrateInfo,
16    helpers::create_if_not_exist,
17    CompactString, Version,
18};
19
20#[derive(Debug, Diagnostic, ThisError)]
21#[non_exhaustive]
22pub enum ManifestsError {
23    #[error("failed to parse binstall crates-v1 manifest: {0}")]
24    #[diagnostic(transparent)]
25    BinstallCratesV1(#[from] BinstallCratesV1Error),
26
27    #[error("failed to parse cargo v1 manifest: {0}")]
28    #[diagnostic(transparent)]
29    CargoManifestV1(#[from] CratesTomlParseError),
30
31    #[error("I/O error: {0}")]
32    Io(#[from] io::Error),
33}
34
35pub struct Manifests {
36    binstall: BinstallCratesV1Records,
37    cargo_crates_v1: FileLock,
38}
39
40impl Manifests {
41    pub fn open_exclusive(cargo_roots: &Path) -> Result<Self, ManifestsError> {
42        // Read cargo_binstall_metadata
43        let metadata_path = cargo_roots.join("binstall/crates-v1.json");
44        fs::create_dir_all(metadata_path.parent().unwrap())?;
45
46        let binstall = BinstallCratesV1Records::load_from_path(&metadata_path)?;
47
48        // Read cargo_install_v1_metadata
49        let manifest_path = cargo_roots.join(".crates.toml");
50
51        let cargo_crates_v1 = create_if_not_exist(&manifest_path)?;
52
53        Ok(Self {
54            binstall,
55            cargo_crates_v1,
56        })
57    }
58
59    fn rewind_cargo_crates_v1(&mut self) -> Result<(), ManifestsError> {
60        self.cargo_crates_v1.rewind().map_err(ManifestsError::from)
61    }
62
63    /// `cargo-uninstall` can be called to uninstall crates,
64    /// but it only updates .crates.toml.
65    ///
66    /// So here we will honour .crates.toml only.
67    pub fn load_installed_crates(
68        &mut self,
69    ) -> Result<BTreeMap<CompactString, Version>, ManifestsError> {
70        self.rewind_cargo_crates_v1()?;
71
72        CratesToml::load_from_reader(&mut self.cargo_crates_v1)
73            .and_then(CratesToml::collect_into_crates_versions)
74            .map_err(ManifestsError::from)
75    }
76
77    pub fn update(mut self, metadata_vec: Vec<CrateInfo>) -> Result<(), ManifestsError> {
78        self.rewind_cargo_crates_v1()?;
79
80        CratesToml::append_to_file(&mut self.cargo_crates_v1, &metadata_vec)?;
81
82        for metadata in metadata_vec {
83            self.binstall.replace(metadata);
84        }
85        self.binstall.overwrite()?;
86
87        Ok(())
88    }
89}