diff --git a/Cargo.lock b/Cargo.lock index 729e6c0..571b26d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,6 +425,8 @@ dependencies = [ "log", "semver", "semver-bump-trait", + "serde", + "serde_json", "slog", "slog-stdlog", "slog-term", @@ -597,6 +599,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "scopeguard" version = "1.2.0" @@ -638,6 +646,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" diff --git a/Cargo.toml b/Cargo.toml index c5b08a5..d35b24d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ glob = "0.3.1" log = "0.4.20" semver = "1.0.22" semver-bump-trait = "0.1.0" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.114" slog = "2.7.0" slog-stdlog = "4.1.1" slog-term = "2.9.0" diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 0fe9d05..df56024 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Parser, Subcommand}; -use slog::{o, Drain, Level}; +use slog::{error, o, Drain, Level}; mod version; @@ -24,7 +24,7 @@ pub enum RootSubcommands { } impl RootCommand { - pub async fn run(&self) -> Result<()> { + pub async fn run(&self) -> Result { // Adjust the verbosity. let log_level = match self.verbose { 0 => Level::Warning, @@ -43,10 +43,16 @@ impl RootCommand { let _log_guard = slog_stdlog::init().unwrap(); // Pass the command in. - match &self.command { - RootSubcommands::Version(cmd) => cmd.run(log).await?, + let result = match &self.command { + RootSubcommands::Version(cmd) => cmd.run(log.clone()).await, }; - Ok(()) + match result { + Ok(_) => Ok(0), + Err(err) => { + error!(log, "{}", err); + Err(err) + } + } } } diff --git a/src/commands/version.rs b/src/commands/version.rs index 24d8964..79bc3d2 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -1,43 +1,61 @@ +use crate::config::Config; use crate::trees::get_tree_skip; use crate::versions::VersionBump; use anyhow::Result; use clap::Parser; use conventional_commit::ConventionalCommit; -use git2::{Oid, Repository}; +use git2::Repository; use semver::Version; use semver_bump_trait::SemverBump; -use slog::{debug, info, warn}; +use slog::{debug, info}; use std::cmp::Ordering; -use std::collections::HashMap; use std::str::FromStr; use crate::tags::get_tag_map; /// Gets the current version based on commits. #[derive(Debug, Parser)] -pub struct VersionCommand {} +pub struct VersionCommand { + /// The directory to perform the search. + #[clap(short, long)] + directory: Option, + + /// The name of the package to use. + #[clap(short, long)] + package: Option, +} impl VersionCommand { pub async fn run(&self, log: slog::Logger) -> Result<()> { // Figure out the path we're searching and which one we found. - let current_dir = - "/home/dmoonfire/src/mfgames/mfgames-cil/src/MfGames.Nitride"; + let current_dir = &self.directory.clone(); + let current_dir = current_dir.as_ref().unwrap(); info!(log, "searching from {:?}", current_dir); // Load the repository so we can walk through it. let repo = Repository::discover(current_dir)?; let git_dir = &repo.workdir(); + let git_dir = git_dir.unwrap(); - if let Some(git_dir) = git_dir { - info!(log, "git root at {:?}", git_dir); - } else { - info!(log, "working with a bare repository"); - } + info!(log, "git root at {:?}", git_dir); + + // Load the configuration file. + let config_file = Config::get_git_config_file(git_dir); + + info!(log, "config at {:?}", config_file); + + let config = Config::load(&config_file)?; + + debug!(log, "config {:?}", config); + + // Get the settings. + let package_name = &self.package.clone(); + let package_name = package_name.as_ref().unwrap(); + let package = config.get_package(package_name)?; // Load a map of all commits that are pointed to by a tag. - let tag_prefix = "MfGames.Nitride-*"; - let tag_map: HashMap = - get_tag_map(&log, &repo, &tag_prefix)?; + let tag_prefix = package.tag_prefix; + let tag_map = get_tag_map(&log, &repo, &tag_prefix)?; // Figure out the head. let head = repo.head()?; diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..3b1e7a7 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,73 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::{ + collections::BTreeMap, + fs::File, + io::Read, + path::{Path, PathBuf}, +}; + +/// The top-level configuration for settings. This will typically be stored +/// at the repository root at `./.config/mfgames-conventional-commit.json`. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "$schema")] +pub enum Config { + #[serde( + rename = "https://mfgames.com/mfgames-conventional-commit/schemas/v0.0.json" + )] + Config0(Config0), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Config0 { + /// Settings for specific projects. + pub packages: BTreeMap, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PackageSettings { + /// The prefix of the Git tag to search for. + pub tag_prefix: String, +} + +impl Config { + pub fn get_git_config_file(git_dir: &Path) -> PathBuf { + let mut path = PathBuf::from(git_dir); + + path.push(".config"); + path.push("mfgames-conventional-commit.json"); + + path + } + + pub fn load(path: &PathBuf) -> Result { + let mut file = File::open(path)?; + let mut data = String::new(); + + file.read_to_string(&mut data)?; + + let config: Config = serde_json::from_str(&data) + .with_context(|| format!("cannot read config from {:?}", &path))?; + + Ok(config) + } + + pub fn get_package( + &self, + package_name: impl Into, + ) -> Result { + let package_name = package_name.into(); + let Config::Config0(config) = &self; + let config = config.clone(); + let packages = config.packages; + + let package = packages + .get(&package_name) + .context(format!("cannot find package {}", package_name))?; + + let package = package.clone(); + + Ok(package) + } +} diff --git a/src/main.rs b/src/main.rs index 64d5ffd..fbd4309 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use anyhow::Result; use clap::Parser; mod commands; +mod config; mod tags; mod trees; mod versions; diff --git a/src/trees.rs b/src/trees.rs index 1004815..c2d8f14 100644 --- a/src/trees.rs +++ b/src/trees.rs @@ -1,6 +1,6 @@ use anyhow::Result; use git2::{Commit, DiffOptions, Repository}; -use slog::{debug, warn, Logger}; +use slog::{debug, Logger}; use crate::versions::VersionBump;