miho 8.0.1

Repository management tools
mod cargo_toml;
mod package_json;
mod tauri_conf_json;

use super::Package;
use crate::agent::Agent;
use crate::dependency::{self, Target};
use anyhow::{Result, bail};
use cargo_toml::CargoToml;
use dependency::DependencyTree;
use globset::Glob;
use package_json::PackageJson;
use semver::Version;
use std::path::Path;
use strum::{EnumIter, IntoEnumIterator};
use tauri_conf_json::TauriConfJson;

pub(super) type ManifestBox = Box<dyn Handler + Send + Sync>;

pub const DEFAULT_VERSION: Version = Version::new(0, 0, 0);

trait Manifest {
  type Value;

  const FILENAME: &'static str;

  fn read(path: impl AsRef<Path>) -> Result<ManifestBox>;
  fn read_as_value(path: impl AsRef<Path>) -> Result<Self::Value>;
}

pub trait Handler {
  fn agent(&self) -> Agent;
  fn bump(&self, package: &Package, new_version: Version) -> Result<()>;
  fn name(&self) -> &str;
  fn update(&self, package: &Package, targets: &[Target]) -> Result<()>;
  fn version(&self) -> Result<Version>;

  fn dependency_tree(&self) -> DependencyTree {
    DependencyTree::new(self.agent())
  }
}

#[derive(Debug, EnumIter)]
pub enum ManifestKind {
  CargoToml,
  PackageJson,
  TauriConfJson,
}

impl ManifestKind {
  pub(crate) fn read<P: AsRef<Path>>(&self, path: P) -> Result<ManifestBox> {
    match self {
      ManifestKind::CargoToml => CargoToml::read(path),
      ManifestKind::PackageJson => PackageJson::read(path),
      ManifestKind::TauriConfJson => TauriConfJson::read(path),
    }
  }

  pub(crate) fn glob(&self) -> &str {
    match self {
      ManifestKind::CargoToml => "**/Cargo.toml",
      ManifestKind::PackageJson => "**/package.json",
      ManifestKind::TauriConfJson => "**/tauri.conf.json",
    }
  }
}

impl TryFrom<&Path> for ManifestKind {
  type Error = anyhow::Error;

  fn try_from(path: &Path) -> Result<Self> {
    for kind in ManifestKind::iter() {
      let glob = Glob::new(kind.glob())?.compile_matcher();
      if glob.is_match(path) {
        return Ok(kind);
      }
    }

    let path = path.to_string_lossy();
    bail!("invalid manifest:\n{path}");
  }
}

fn default_version() -> String {
  DEFAULT_VERSION.to_string()
}