diff --git a/Cargo.toml b/Cargo.toml index 0eb5aaf7..50b6b532 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,7 +83,7 @@ mime_guess = { version = "2.0.1" } [features] # default = ["frontend", "sqlite"] # default = ["frontend", "mysql"] -default = ["frontend", "postgres"] +default = ["frontend", "postgres", "git2"] frontend = ["handlebars", "num-format", "bigdecimal", "cookie", "time"] mysql = ["diesel/mysql", "diesel_migrations/mysql"] sqlite = ["diesel/sqlite", "diesel_migrations/sqlite"] diff --git a/src/config/index/cli.rs b/src/config/index/cli.rs deleted file mode 100644 index 5c5d3b06..00000000 --- a/src/config/index/cli.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -use crate::index::cli::CommandLineIndex; - -/// The configuration struct for the 'command-line' index management strategy. -/// -/// ```toml -/// [index] -/// type = "command-line" # required -/// path = "crate-index" # required -/// ``` -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct CommandLineIndexConfig { - /// The path to the local index repository. - pub path: PathBuf, -} - -impl From for CommandLineIndex { - fn from(config: CommandLineIndexConfig) -> CommandLineIndex { - CommandLineIndex::new(config.path) - } -} diff --git a/src/config/index/git2.rs b/src/config/index/git2.rs deleted file mode 100644 index ae2c2449..00000000 --- a/src/config/index/git2.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::path::PathBuf; - -use serde::{Deserialize, Serialize}; - -use crate::index::git2::Git2Index; - -/// The configuration struct for the 'git2' index management strategy. -/// -/// ```toml -/// [index] -/// type = "git2" # required -/// path = "crate-index" # required -/// ``` -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct Git2IndexConfig { - /// The path to the local index repository. - pub path: PathBuf, -} - -impl From for Git2Index { - fn from(config: Git2IndexConfig) -> Git2Index { - Git2Index::new(config.path).expect("could not initialize the 'git2' index") - } -} diff --git a/src/config/index/mod.rs b/src/config/index/mod.rs deleted file mode 100644 index b864dfc6..00000000 --- a/src/config/index/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// The 'command-line' configuration. -pub mod cli; - -/// The 'git2' configuration. -#[cfg(feature = "git2")] -pub mod git2; - -use crate::config::index::cli::CommandLineIndexConfig; -use crate::index::Index; - -#[cfg(feature = "git2")] -use crate::config::index::git2::Git2IndexConfig; - -/// The configuration enum for index management strategies. -/// -/// ```toml -/// [index] -/// type = "<...>" # required, replace "<...>" by the selected strategy. -/// ``` -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type", rename_all = "kebab-case")] -pub enum IndexConfig { - /// The 'command-line' index management strategy (uses "git" shell command). - CommandLine(CommandLineIndexConfig), - /// The 'git2' index management strategy (uses [**`libgit2`**][libgit2]). - /// [libgit2]: https://libgit2.org - #[cfg(feature = "git2")] - Git2(Git2IndexConfig), -} - -impl From for Index { - fn from(config: IndexConfig) -> Index { - match config { - IndexConfig::CommandLine(config) => Index::CommandLine(config.into()), - #[cfg(feature = "git2")] - IndexConfig::Git2(config) => Index::Git2(config.into()), - } - } -} diff --git a/src/config/mod.rs b/src/config/mod.rs index 251f1fcb..89ddb350 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,11 +1,10 @@ +use std::convert::TryInto; use std::net; use serde::{Deserialize, Serialize}; /// Database configuration (`[database]` section). pub mod database; -/// Index management strategy configuration (`[index]` section). -pub mod index; /// Crate storage configuration (`[storage]` section). pub mod storage; /// Syntax-highlighting configurations (`[syntect.syntaxes]` and `[syntect.themes]` sections). @@ -20,9 +19,9 @@ use crate::storage::Storage; use crate::Repo; use crate::config::database::DatabaseConfig; -use crate::config::index::IndexConfig; use crate::config::storage::StorageConfig; use crate::config::syntect::{SyntectConfig, SyntectState}; +use crate::index::Config as IndexConfig; #[cfg(feature = "frontend")] pub use crate::config::frontend::*; @@ -72,7 +71,7 @@ pub struct State { impl From for State { fn from(config: Config) -> State { State { - index: config.index.into(), + index: config.index.try_into().unwrap(), storage: config.storage.into(), repo: Repo::new(&config.database), syntect: config.syntect.into(), diff --git a/src/index/cli.rs b/src/index/cli.rs deleted file mode 100644 index f8149d0c..00000000 --- a/src/index/cli.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::path::PathBuf; -use std::process::{Command, Stdio}; - -use semver::{Version, VersionReq}; - -use crate::error::Error; -use crate::index::tree::Tree; -use crate::index::{CrateVersion, Indexer}; - -/// The 'command-line' crate index management strategy type. -/// -/// It manages the crate index through the invocation of "git" shell commands. -#[derive(Debug, Clone, PartialEq)] -pub struct CommandLineIndex { - repo: Repository, - tree: Tree, -} - -impl CommandLineIndex { - /// Create a CommandLineIndex instance with the given path. - pub fn new>(path: P) -> CommandLineIndex { - let path = path.into(); - let repo = Repository { path: path.clone() }; - let tree = Tree::new(path); - CommandLineIndex { repo, tree } - } -} - -impl Indexer for CommandLineIndex { - fn url(&self) -> Result { - self.repo.url() - } - - fn refresh(&self) -> Result<(), Error> { - self.repo.refresh() - } - - fn commit_and_push(&self, msg: &str) -> Result<(), Error> { - self.repo.commit_and_push(msg) - } - - fn match_record(&self, name: &str, req: VersionReq) -> Result { - self.tree.match_record(name, req) - } - - fn all_records(&self, name: &str) -> Result, Error> { - self.tree.all_records(name) - } - - fn latest_record(&self, name: &str) -> Result { - self.tree.latest_record(name) - } - - fn add_record(&self, record: CrateVersion) -> Result<(), Error> { - self.tree.add_record(record) - } - - fn alter_record(&self, name: &str, version: Version, func: F) -> Result<(), Error> - where - F: FnOnce(&mut CrateVersion), - { - self.tree.alter_record(name, version, func) - } -} - -#[derive(Debug, Clone, PartialEq)] -struct Repository { - path: PathBuf, -} - -impl Repository { - fn url(&self) -> Result { - let output = Command::new("git") - .arg("remote") - .arg("get-url") - .arg("origin") - .stdout(Stdio::piped()) - .current_dir(self.path.canonicalize()?) - .output()?; - - Ok(String::from_utf8_lossy(output.stdout.as_slice()).into()) - } - - fn refresh(&self) -> Result<(), Error> { - Command::new("git") - .arg("pull") - .arg("--ff-only") - .current_dir(self.path.canonicalize()?) - .spawn()? - .wait()?; - - Ok(()) - } - - fn commit_and_push(&self, msg: &str) -> Result<(), Error> { - Command::new("git") - .arg("add") - .arg("--all") - .current_dir(&self.path) - .spawn()? - .wait()?; - Command::new("git") - .arg("commit") - .arg("-m") - .arg(msg) - .current_dir(&self.path) - .spawn()? - .wait()?; - Command::new("git") - .arg("push") - .arg("origin") - .arg("master") - .current_dir(&self.path) - .spawn()? - .wait()?; - - Ok(()) - } -} diff --git a/src/index/mod.rs b/src/index/mod.rs index 82c27f6f..2b764ef5 100644 --- a/src/index/mod.rs +++ b/src/index/mod.rs @@ -1,33 +1,60 @@ use semver::{Version, VersionReq}; +use std::convert::TryFrom; +use std::path::PathBuf; -/// Index management through `git` shell command invocations. -pub mod cli; mod models; mod tree; - -/// Index management using [**`libgit2`**][libgit2]. -/// [libgit2]: https://libgit2.org -#[cfg(feature = "git2")] -pub mod git2; +use tree::Tree; +mod repository; +use repository::Repository; +use serde::{Deserialize, Serialize}; pub use models::{CrateDependency, CrateDependencyKind, CrateVersion}; use crate::error::Error; -use crate::index::cli::CommandLineIndex; -#[cfg(feature = "git2")] -use crate::index::git2::Git2Index; +pub struct Index { + repo: Box, + tree: Tree, +} + +impl Index { + pub fn with_repo(path: PathBuf, repo: impl Repository + Send + Sync + 'static) -> Self { + let repo = Box::new(repo); + let tree = Tree::new(path); + Self { repo, tree } + } +} -/// The crate indexing management strategy type. +/// The configuration struct for the 'git2' index management strategy. /// -/// It represents which index management strategy is currently used. -pub enum Index { - /// Manages the crate index through the invocation of the "git" shell command. - CommandLine(CommandLineIndex), - /// Manages the crate index using [**`libgit2`**]. - /// [libgit2]: https://libgit2.org - #[cfg(feature = "git2")] - Git2(Git2Index), +/// ```toml +/// [index] +/// type = "git2" | "command-line" # required +/// path = "crate-index" # required +/// ``` +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type")] +#[serde(rename_all = "kebab-case")] +pub enum Config { + Git2 { path: PathBuf }, + CommandLine { path: PathBuf }, +} + +impl TryFrom for Index { + type Error = Error; + fn try_from(config: Config) -> Result { + match config { + Config::CommandLine { path } => { + let repo = repository::cli::Repo::new(path.clone()); + Ok(Self::with_repo(path, repo)) + } + Config::Git2 { path } => { + let repo = repository::git2::Repo::new(&path)?; + Ok(Self::with_repo(path, repo)) + } + } + } } /// The required trait that any crate index management type must implement. @@ -62,85 +89,61 @@ pub trait Indexer { impl Indexer for Index { fn url(&self) -> Result { - match self { - Index::CommandLine(idx) => idx.url(), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.url(), - } + self.repo.url() } - fn refresh(&self) -> Result<(), Error> { - match self { - Index::CommandLine(idx) => idx.refresh(), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.refresh(), - } - } - - fn commit_and_push(&self, msg: &str) -> Result<(), Error> { - match self { - Index::CommandLine(idx) => idx.commit_and_push(msg), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.commit_and_push(msg), - } + self.repo.refresh() } - fn all_records(&self, name: &str) -> Result, Error> { - match self { - Index::CommandLine(idx) => idx.all_records(name), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.all_records(name), - } + self.tree.all_records(name) } - fn latest_record(&self, name: &str) -> Result { - match self { - Index::CommandLine(idx) => idx.latest_record(name), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.latest_record(name), - } + self.tree.latest_record(name) } - fn match_record(&self, name: &str, req: VersionReq) -> Result { - match self { - Index::CommandLine(idx) => idx.match_record(name, req), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.match_record(name, req), - } + self.tree.match_record(name, req) + } + fn commit_and_push(&self, msg: &str) -> Result<(), Error> { + self.repo.commit_and_push(msg) } - fn add_record(&self, record: CrateVersion) -> Result<(), Error> { - match self { - Index::CommandLine(idx) => idx.add_record(record), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.add_record(record), - } + self.tree.add_record(record) } - fn alter_record(&self, name: &str, version: Version, func: F) -> Result<(), Error> where F: FnOnce(&mut CrateVersion), { - match self { - Index::CommandLine(idx) => idx.alter_record(name, version, func), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.alter_record(name, version, func), - } + self.tree.alter_record(name, version, func) } +} - fn yank_record(&self, name: &str, version: Version) -> Result<(), Error> { - match self { - Index::CommandLine(idx) => idx.yank_record(name, version), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.yank_record(name, version), +#[cfg(test)] +mod tests { + use super::Config; + #[test] + fn from_config() { + match toml::from_str( + r#" + type = "git2" + path = "crate-index" + "#, + ) + .unwrap() + { + Config::Git2 { .. } => (), + Config::CommandLine { .. } => panic!("deserialization failed!"), } - } - fn unyank_record(&self, name: &str, version: Version) -> Result<(), Error> { - match self { - Index::CommandLine(idx) => idx.unyank_record(name, version), - #[cfg(feature = "git2")] - Index::Git2(idx) => idx.unyank_record(name, version), + match toml::from_str( + r#" + type = "command-line" + path = "crate-index" + "#, + ) + .unwrap() + { + Config::Git2 { .. } => panic!("deserialization failed!"), + Config::CommandLine { .. } => (), } } } diff --git a/src/index/repository.rs b/src/index/repository.rs new file mode 100644 index 00000000..8a6923c2 --- /dev/null +++ b/src/index/repository.rs @@ -0,0 +1,12 @@ +pub mod cli; +#[cfg(feature = "git2")] +pub mod git2; +use crate::error::Error; + +pub trait Repository { + fn url(&self) -> Result; + + fn refresh(&self) -> Result<(), Error>; + + fn commit_and_push(&self, msg: &str) -> Result<(), Error>; +} diff --git a/src/index/repository/cli.rs b/src/index/repository/cli.rs new file mode 100644 index 00000000..5e5df86e --- /dev/null +++ b/src/index/repository/cli.rs @@ -0,0 +1,69 @@ +use std::path::PathBuf; +use std::process::{Command, Stdio}; + +use super::Repository; +use crate::error::Error; + +/// The 'command-line' crate index management strategy type. +/// +/// It manages the crate index through the invocation of "git" shell commands. +#[derive(Debug, Clone, PartialEq)] +pub struct Repo { + path: PathBuf, +} + +impl Repo { + pub fn new>(path: P) -> Self { + Self { path: path.into() } + } +} + +impl Repository for Repo { + fn url(&self) -> Result { + let output = Command::new("git") + .arg("remote") + .arg("get-url") + .arg("origin") + .stdout(Stdio::piped()) + .current_dir(self.path.canonicalize()?) + .output()?; + + Ok(String::from_utf8_lossy(output.stdout.as_slice()).into()) + } + + fn refresh(&self) -> Result<(), Error> { + Command::new("git") + .arg("pull") + .arg("--ff-only") + .current_dir(self.path.canonicalize()?) + .spawn()? + .wait()?; + + Ok(()) + } + + fn commit_and_push(&self, msg: &str) -> Result<(), Error> { + Command::new("git") + .arg("add") + .arg("--all") + .current_dir(&self.path) + .spawn()? + .wait()?; + Command::new("git") + .arg("commit") + .arg("-m") + .arg(msg) + .current_dir(&self.path) + .spawn()? + .wait()?; + Command::new("git") + .arg("push") + .arg("origin") + .arg("master") + .current_dir(&self.path) + .spawn()? + .wait()?; + + Ok(()) + } +} diff --git a/src/index/git2.rs b/src/index/repository/git2.rs similarity index 76% rename from src/index/git2.rs rename to src/index/repository/git2.rs index 918cfb2d..76e2c9aa 100644 --- a/src/index/git2.rs +++ b/src/index/repository/git2.rs @@ -1,31 +1,24 @@ -use std::path::PathBuf; -use std::sync::Mutex; - -use semver::{Version, VersionReq}; - +use super::Repository; use crate::error::Error; -use crate::index::tree::Tree; -use crate::index::{CrateVersion, Indexer}; +use git2; +use std::path::Path; +use std::sync::Mutex; /// The 'git2' crate index management strategy type. /// /// It manages the crate index using the [**`libgit2`**][libgit2] library. /// /// [libgit2]: https://libgit2.org -pub struct Git2Index { +pub struct Repo { /// The path of the crate index. - pub(crate) repo: Mutex, - tree: Tree, + repo: Mutex, } -impl Git2Index { - /// Create a Git2Index instance with the given path. - pub fn new>(path: P) -> Result { - let path = path.into(); - let repo = git2::Repository::open(&path)?; - let repo = Mutex::new(repo); - let tree = Tree::new(path); - Ok(Git2Index { repo, tree }) +impl Repo { + /// Create a Repository instance with the given path. + pub fn new>(path: P) -> Result { + let repo = Mutex::new(git2::Repository::open(path)?); + Ok(Self { repo }) } } @@ -69,7 +62,7 @@ where }) } -impl Indexer for Git2Index { +impl Repository for Repo { fn url(&self) -> Result { let repo = self.repo.lock().unwrap(); let remote = repo.find_remote("origin")?; @@ -134,27 +127,4 @@ impl Indexer for Git2Index { Ok(()) } - - fn match_record(&self, name: &str, req: VersionReq) -> Result { - self.tree.match_record(name, req) - } - - fn all_records(&self, name: &str) -> Result, Error> { - self.tree.all_records(name) - } - - fn latest_record(&self, name: &str) -> Result { - self.tree.latest_record(name) - } - - fn add_record(&self, record: CrateVersion) -> Result<(), Error> { - self.tree.add_record(record) - } - - fn alter_record(&self, name: &str, version: Version, func: F) -> Result<(), Error> - where - F: FnOnce(&mut CrateVersion), - { - self.tree.alter_record(name, version, func) - } }