binstalk_manifests/
crates_manifests.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use std::{
    collections::BTreeMap,
    fs,
    io::{self, Seek},
    path::Path,
};

use fs_lock::FileLock;
use miette::Diagnostic;
use thiserror::Error as ThisError;

use crate::{
    binstall_crates_v1::{Error as BinstallCratesV1Error, Records as BinstallCratesV1Records},
    cargo_crates_v1::{CratesToml, CratesTomlParseError},
    crate_info::CrateInfo,
    CompactString, Version,
};

#[derive(Debug, Diagnostic, ThisError)]
#[non_exhaustive]
pub enum ManifestsError {
    #[error("failed to parse binstall crates-v1 manifest: {0}")]
    #[diagnostic(transparent)]
    BinstallCratesV1(#[from] BinstallCratesV1Error),

    #[error("failed to parse cargo v1 manifest: {0}")]
    #[diagnostic(transparent)]
    CargoManifestV1(#[from] CratesTomlParseError),

    #[error("I/O error: {0}")]
    Io(#[from] io::Error),
}

pub struct Manifests {
    binstall: BinstallCratesV1Records,
    cargo_crates_v1: FileLock,
}

impl Manifests {
    pub fn open_exclusive(cargo_roots: &Path) -> Result<Self, ManifestsError> {
        // Read cargo_binstall_metadata
        let metadata_path = cargo_roots.join("binstall/crates-v1.json");
        fs::create_dir_all(metadata_path.parent().unwrap())?;

        let binstall = BinstallCratesV1Records::load_from_path(&metadata_path)?;

        // Read cargo_install_v1_metadata
        let manifest_path = cargo_roots.join(".crates.toml");

        let cargo_crates_v1 = fs::File::options()
            .read(true)
            .write(true)
            .create(true)
            .truncate(false)
            .open(manifest_path)
            .and_then(FileLock::new_exclusive)?;

        Ok(Self {
            binstall,
            cargo_crates_v1,
        })
    }

    fn rewind_cargo_crates_v1(&mut self) -> Result<(), ManifestsError> {
        self.cargo_crates_v1.rewind().map_err(ManifestsError::from)
    }

    /// `cargo-uninstall` can be called to uninstall crates,
    /// but it only updates .crates.toml.
    ///
    /// So here we will honour .crates.toml only.
    pub fn load_installed_crates(
        &mut self,
    ) -> Result<BTreeMap<CompactString, Version>, ManifestsError> {
        self.rewind_cargo_crates_v1()?;

        CratesToml::load_from_reader(&mut self.cargo_crates_v1)
            .and_then(CratesToml::collect_into_crates_versions)
            .map_err(ManifestsError::from)
    }

    pub fn update(mut self, metadata_vec: Vec<CrateInfo>) -> Result<(), ManifestsError> {
        self.rewind_cargo_crates_v1()?;

        CratesToml::append_to_file(&mut self.cargo_crates_v1, &metadata_vec)?;

        for metadata in metadata_vec {
            self.binstall.replace(metadata);
        }
        self.binstall.overwrite()?;

        Ok(())
    }
}