rialoman 0.2.0

Rialo native toolchain manager
Documentation
//! Track and persist the currently selected release via `current.json`.
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{fs, path::PathBuf};

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

use crate::spec::ReleaseId;

#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct CurrentRelease {
    /// Channel of the active release.
    pub channel: String,
    /// Version component of the active release.
    pub version: String,
}

impl From<&ReleaseId> for CurrentRelease {
    fn from(id: &ReleaseId) -> Self {
        Self {
            channel: id.channel.as_str().to_owned(),
            version: id.version.clone(),
        }
    }
}

impl PartialEq<ReleaseId> for CurrentRelease {
    fn eq(&self, other: &ReleaseId) -> bool {
        self.channel == other.channel.as_str() && self.version == other.version
    }
}

impl PartialEq<CurrentRelease> for ReleaseId {
    fn eq(&self, other: &CurrentRelease) -> bool {
        other == self
    }
}

pub struct CurrentManager {
    path: PathBuf,
}

impl CurrentManager {
    pub fn new(path: PathBuf) -> Self {
        Self { path }
    }

    pub fn load(&self) -> Result<Option<CurrentRelease>> {
        if !self.path.exists() {
            return Ok(None);
        }

        let data = fs::read(&self.path).with_context(|| {
            format!("failed to read current release at {}", self.path.display())
        })?;
        let parsed = serde_json::from_slice(&data).with_context(|| {
            format!(
                "failed to parse current release JSON at {}",
                self.path.display()
            )
        })?;
        Ok(Some(parsed))
    }

    pub fn save(&self, id: &ReleaseId) -> Result<()> {
        let current: CurrentRelease = id.into();
        let data = serde_json::to_vec_pretty(&current)?;
        fs::write(&self.path, data).with_context(|| {
            format!(
                "failed to persist current release at {}",
                self.path.display()
            )
        })
    }

    pub fn clear(&self) -> Result<()> {
        if self.path.exists() {
            fs::remove_file(&self.path).with_context(|| {
                format!(
                    "failed to remove current release file at {}",
                    self.path.display()
                )
            })?;
        }
        Ok(())
    }
}