Skip to main content

zoi_core/
pin.rs

1use std::fs::{self, File};
2use std::io::{self, Read, Write};
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7/// Represents a package pinned to a specific version from a specific source.
8#[derive(Serialize, Deserialize, Debug, Clone)]
9pub struct PinnedPackage {
10    /// The source of the package (e.g. its PURL or name).
11    pub source: String,
12    /// The specific version the package is pinned to.
13    pub version: String
14}
15
16/// Returns the path to the `pinned.json` file in the user's Zoi directory.
17fn get_pinned_json_path() -> Result<PathBuf, io::Error> {
18    let zoi_dir =
19        crate::utils::get_user_state_dir().map_err(io::Error::other)?;
20    if !zoi_dir.exists() {
21        fs::create_dir_all(&zoi_dir)?;
22    }
23    Ok(zoi_dir.join("pinned.json"))
24}
25
26/// Loads the list of pinned packages from `pinned.json`.
27///
28/// # Errors
29///
30/// Returns an `io::Error` if the home directory cannot be found, if creating
31/// the `.zoi` directory fails, or if reading the `pinned.json` file fails.
32pub fn get_pinned_packages() -> Result<Vec<PinnedPackage>, io::Error> {
33    let path = get_pinned_json_path()?;
34    if !path.exists() {
35        return Ok(Vec::new());
36    }
37
38    let mut file = File::open(path)?;
39    let mut contents = String::new();
40    file.read_to_string(&mut contents)?;
41
42    let packages: Vec<PinnedPackage> =
43        serde_json::from_str(&contents).unwrap_or_else(|_| Vec::new());
44    Ok(packages)
45}
46
47/// Saves the list of pinned packages to `pinned.json`.
48///
49/// # Errors
50///
51/// Returns an `io::Error` if the home directory cannot be found, if creating
52/// the `.zoi` directory fails, or if writing to `pinned.json` fails.
53pub fn write_pinned_packages(
54    packages: &[PinnedPackage]
55) -> Result<(), io::Error> {
56    let path = get_pinned_json_path()?;
57    let mut file = File::create(path)?;
58    let contents = serde_json::to_string_pretty(packages)?;
59    file.write_all(contents.as_bytes())?;
60    Ok(())
61}
62
63/// Retrieves the pinned version for a given source, if it exists.
64///
65/// # Errors
66///
67/// Returns an `io::Error` if loading the pinned packages fails.
68pub fn get_pinned_version(source: &str) -> Result<Option<String>, io::Error> {
69    let pinned_packages = get_pinned_packages()?;
70    Ok(pinned_packages
71        .iter()
72        .find(|p| p.source == source)
73        .map(|p| p.version.clone()))
74}
75
76/// Checks if a source has a pinned version.
77///
78/// # Errors
79///
80/// Returns an `io::Error` if loading the pinned packages fails.
81pub fn is_pinned(source: &str) -> Result<bool, io::Error> {
82    let pinned_packages = get_pinned_packages()?;
83    Ok(pinned_packages.iter().any(|p| p.source == source))
84}