Skip to main content

libwally/
lockfile.rs

1use std::collections::BTreeMap;
2use std::path::Path;
3use std::{
4    fs::read_to_string,
5    io::{self, BufWriter, Write},
6};
7
8use fs_err::File;
9use semver::Version;
10use serde::{Deserialize, Serialize};
11
12use crate::package_id;
13use crate::{
14    manifest::Manifest, package_id::PackageId, package_name::PackageName, resolution::Resolve,
15};
16
17pub const LOCKFILE_NAME: &str = "wally.lock";
18
19#[derive(Debug, Serialize, Deserialize)]
20pub struct Lockfile {
21    pub registry: String,
22
23    #[serde(rename = "package")]
24    pub packages: Vec<LockPackage>,
25}
26
27fn grab_dependencies(
28    package_id: &PackageId,
29    dependencies: &BTreeMap<
30        package_id::PackageId,
31        BTreeMap<std::string::String, package_id::PackageId>,
32    >,
33) -> Vec<(String, PackageId)> {
34    dependencies
35        .get(package_id)
36        .map(|dependencies| {
37            dependencies
38                .iter()
39                .map(|(key, value)| (key.clone(), value.clone()))
40                .collect()
41        })
42        .unwrap_or_else(Vec::new)
43}
44
45impl Lockfile {
46    pub fn from_manifest(manifest: &Manifest) -> Self {
47        Self {
48            registry: manifest.package.registry.clone(),
49            packages: Vec::new(),
50        }
51    }
52
53    pub fn from_resolve(resolve: &Resolve) -> Self {
54        let mut packages = Vec::new();
55
56        for package_id in &resolve.activated {
57            let dependencies = [
58                grab_dependencies(&package_id, &resolve.shared_dependencies),
59                grab_dependencies(&package_id, &resolve.server_dependencies),
60                grab_dependencies(&package_id, &resolve.dev_dependencies),
61            ]
62            .concat();
63
64            packages.push(LockPackage::Registry(RegistryLockPackage {
65                name: package_id.name().clone(),
66                version: package_id.version().clone(),
67                checksum: None,
68                dependencies,
69            }));
70        }
71
72        Self {
73            registry: "test".to_owned(),
74            packages,
75        }
76    }
77
78    pub fn load(project_path: &Path) -> anyhow::Result<Option<Self>> {
79        let lockfile_path = project_path.join(LOCKFILE_NAME);
80        let contents = match read_to_string(&lockfile_path) {
81            Ok(contents) => contents,
82            Err(err) => {
83                if err.kind() == io::ErrorKind::NotFound {
84                    return Ok(None);
85                } else {
86                    return Err(err.into());
87                }
88            }
89        };
90        Ok(Some(toml::from_str(&contents)?))
91    }
92
93    pub fn save(&self, project_path: &Path) -> anyhow::Result<()> {
94        let lockfile_path = project_path.join(LOCKFILE_NAME);
95        let serialized = toml::to_string(self)?;
96
97        let mut file = BufWriter::new(File::create(lockfile_path)?);
98        writeln!(file, "# This file is automatically @generated by Wally.")?;
99        writeln!(file, "# It is not intended for manual editing.")?;
100        write!(file, "{}", serialized)?;
101        file.flush()?;
102
103        Ok(())
104    }
105}
106
107#[derive(Debug, Serialize, Deserialize)]
108#[serde(untagged)]
109pub enum LockPackage {
110    Registry(RegistryLockPackage),
111    Git(GitLockPackage),
112}
113
114#[derive(Debug, Serialize, Deserialize)]
115pub struct RegistryLockPackage {
116    pub name: PackageName,
117    pub version: Version,
118    pub checksum: Option<String>,
119
120    #[serde(default)]
121    pub dependencies: Vec<(String, PackageId)>,
122}
123
124#[derive(Debug, Serialize, Deserialize)]
125pub struct GitLockPackage {
126    pub name: String,
127    pub rev: String,
128    pub commit: String,
129
130    #[serde(default)]
131    pub dependencies: Vec<PackageId>,
132}