Skip to content

Commit

Permalink
Merge pull request #40 from danieleades/metadata-refactor
Browse files Browse the repository at this point in the history
refactor indexes
  • Loading branch information
Hirevo authored Apr 12, 2020
2 parents 424c860 + 681b10d commit 59abbfe
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 243 deletions.
2 changes: 1 addition & 1 deletion src/api/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ pub(crate) async fn put(mut req: Request<State>) -> Result<Response, Error> {
optional: dep.optional,
default_features: dep.default_features,
target: dep.target,
kind: dep.kind,
kind: dep.kind.unwrap_or(CrateDependencyKind::Normal),
registry: dep.registry,
package,
}
Expand Down
2 changes: 1 addition & 1 deletion src/config/index/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ pub struct CommandLineIndexConfig {

impl From<CommandLineIndexConfig> for CommandLineIndex {
fn from(config: CommandLineIndexConfig) -> CommandLineIndex {
CommandLineIndex { path: config.path }
CommandLineIndex::new(config.path)
}
}
176 changes: 49 additions & 127 deletions src/index/cli.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,74 @@
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::PathBuf;
use std::process::{Command, Stdio};

use semver::{Version, VersionReq};

use crate::error::{AlexError, Error};
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 {
/// The path of the crate index.
pub(crate) path: PathBuf,
repo: Repository,
tree: Tree,
}

impl CommandLineIndex {
/// Create a CommandLineIndex instance with the given path.
pub fn new<P: Into<PathBuf>>(path: P) -> Result<CommandLineIndex, Error> {
pub fn new<P: Into<PathBuf>>(path: P) -> CommandLineIndex {
let path = path.into();
Ok(CommandLineIndex { path })
let repo = Repository { path: path.clone() };
let tree = Tree::new(path);
CommandLineIndex { repo, tree }
}
}

impl Indexer for CommandLineIndex {
fn url(&self) -> Result<String, Error> {
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<CrateVersion, Error> {
self.tree.match_record(name, req)
}

fn compute_record_path(&self, name: &str) -> PathBuf {
match name.len() {
1 => self.path.join("1").join(&name),
2 => self.path.join("2").join(&name),
3 => self.path.join("3").join(&name[..1]).join(&name),
_ => self.path.join(&name[0..2]).join(&name[2..4]).join(&name),
}
fn all_records(&self, name: &str) -> Result<Vec<CrateVersion>, Error> {
self.tree.all_records(name)
}

fn latest_record(&self, name: &str) -> Result<CrateVersion, Error> {
self.tree.latest_record(name)
}

fn add_record(&self, record: CrateVersion) -> Result<(), Error> {
self.tree.add_record(record)
}

fn alter_record<F>(&self, name: &str, version: Version, func: F) -> Result<(), Error>
where
F: FnOnce(&mut CrateVersion),
{
self.tree.alter_record(name, version, func)
}
}

impl Indexer for CommandLineIndex {
#[derive(Debug, Clone, PartialEq)]
struct Repository {
path: PathBuf,
}

impl Repository {
fn url(&self) -> Result<String, Error> {
let output = Command::new("git")
.arg("remote")
Expand Down Expand Up @@ -82,116 +116,4 @@ impl Indexer for CommandLineIndex {

Ok(())
}

fn match_record(&self, name: &str, req: VersionReq) -> Result<CrateVersion, Error> {
let path = self.compute_record_path(name);
let file = fs::File::open(path).map_err(|err| match err.kind() {
io::ErrorKind::NotFound => Error::from(AlexError::CrateNotFound {
name: String::from(name),
}),
_ => Error::from(err),
})?;
let found = io::BufReader::new(file)
.lines()
.map(|line| Some(json::from_str(line.ok()?.as_str()).ok()?))
.flat_map(|ret: Option<CrateVersion>| ret.into_iter())
.filter(|krate| req.matches(&krate.vers))
.max_by(|k1, k2| k1.vers.cmp(&k2.vers));
Ok(found.ok_or_else(|| AlexError::CrateNotFound {
name: String::from(name),
})?)
}

fn all_records(&self, name: &str) -> Result<Vec<CrateVersion>, Error> {
let path = self.compute_record_path(name);
let reader = io::BufReader::new(fs::File::open(path)?);
reader
.lines()
.map(|line| Ok(json::from_str::<CrateVersion>(line?.as_str())?))
.collect()
}

fn latest_record(&self, name: &str) -> Result<CrateVersion, Error> {
let records = self.all_records(name)?;
Ok(records
.into_iter()
.max_by(|k1, k2| k1.vers.cmp(&k2.vers))
.expect("at least one version should exist"))
}

fn add_record(&self, record: CrateVersion) -> Result<(), Error> {
let path = self.compute_record_path(record.name.as_str());

if let Ok(file) = fs::File::open(&path) {
let reader = io::BufReader::new(file);
let records = reader
.lines()
.map(|line| Ok(json::from_str::<CrateVersion>(line?.as_str())?))
.collect::<Result<Vec<CrateVersion>, Error>>()?;
let latest = records
.into_iter()
.max_by(|k1, k2| k1.vers.cmp(&k2.vers))
.expect("at least one record should exist");
if record.vers <= latest.vers {
return Err(Error::from(AlexError::VersionTooLow {
krate: record.name,
hosted: latest.vers,
published: record.vers,
}));
}
} else {
let parent = path.parent().unwrap();
fs::create_dir_all(parent)?;
}

let mut file = fs::OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(path)?;
json::to_writer(&mut file, &record)?;
writeln!(file)?;
file.flush()?;

Ok(())
}

fn alter_record<F>(&self, name: &str, version: Version, func: F) -> Result<(), Error>
where
F: FnOnce(&mut CrateVersion),
{
let path = self.compute_record_path(name);
let file = fs::File::open(path.as_path()).map_err(|err| match err.kind() {
io::ErrorKind::NotFound => Error::from(AlexError::CrateNotFound {
name: String::from(name),
}),
_ => Error::from(err),
})?;
let mut krates: Vec<CrateVersion> = {
let mut out = Vec::new();
for line in io::BufReader::new(file).lines() {
let krate = json::from_str(line?.as_str())?;
out.push(krate);
}
out
};
let found = krates
.iter_mut()
.find(|krate| krate.vers == version)
.ok_or_else(|| {
Error::from(AlexError::CrateNotFound {
name: String::from(name),
})
})?;

func(found);

let lines = krates
.into_iter()
.map(|krate| json::to_string(&krate))
.collect::<Result<Vec<String>, _>>()?;
fs::write(path.as_path(), lines.join("\n") + "\n")?;

Ok(())
}
}
126 changes: 14 additions & 112 deletions src/index/git2.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use std::fs;
use std::io::{self, BufRead, Write};
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::Mutex;

use semver::{Version, VersionReq};

use crate::error::{AlexError, Error};
use crate::error::Error;
use crate::index::tree::Tree;
use crate::index::{CrateVersion, Indexer};

/// The 'git2' crate index management strategy type.
Expand All @@ -16,25 +15,17 @@ use crate::index::{CrateVersion, Indexer};
pub struct Git2Index {
/// The path of the crate index.
pub(crate) repo: Mutex<git2::Repository>,
tree: Tree,
}

impl Git2Index {
/// Create a Git2Index instance with the given path.
pub fn new<P: AsRef<Path>>(path: P) -> Result<Git2Index, Error> {
let repo = git2::Repository::open(path)?;
pub fn new<P: Into<PathBuf>>(path: P) -> Result<Git2Index, Error> {
let path = path.into();
let repo = git2::Repository::open(&path)?;
let repo = Mutex::new(repo);
Ok(Git2Index { repo })
}

fn compute_record_path(&self, name: &str) -> PathBuf {
let repo = self.repo.lock().unwrap();
let path = repo.path().parent().unwrap();
match name.len() {
1 => path.join("1").join(&name),
2 => path.join("2").join(&name),
3 => path.join("3").join(&name[..1]).join(&name),
_ => path.join(&name[0..2]).join(&name[2..4]).join(&name),
}
let tree = Tree::new(path);
Ok(Git2Index { repo, tree })
}
}

Expand Down Expand Up @@ -145,114 +136,25 @@ impl Indexer for Git2Index {
}

fn match_record(&self, name: &str, req: VersionReq) -> Result<CrateVersion, Error> {
let path = self.compute_record_path(name);
let file = fs::File::open(path).map_err(|err| match err.kind() {
io::ErrorKind::NotFound => Error::from(AlexError::CrateNotFound {
name: String::from(name),
}),
_ => Error::from(err),
})?;
let found = io::BufReader::new(file)
.lines()
.map(|line| Some(json::from_str(line.ok()?.as_str()).ok()?))
.flat_map(|ret: Option<CrateVersion>| ret.into_iter())
.filter(|krate| req.matches(&krate.vers))
.max_by(|k1, k2| k1.vers.cmp(&k2.vers));
Ok(found.ok_or_else(|| AlexError::CrateNotFound {
name: String::from(name),
})?)
self.tree.match_record(name, req)
}

fn all_records(&self, name: &str) -> Result<Vec<CrateVersion>, Error> {
let path = self.compute_record_path(name);
let reader = io::BufReader::new(fs::File::open(path)?);
reader
.lines()
.map(|line| Ok(json::from_str::<CrateVersion>(line?.as_str())?))
.collect()
self.tree.all_records(name)
}

fn latest_record(&self, name: &str) -> Result<CrateVersion, Error> {
let records = self.all_records(name)?;
Ok(records
.into_iter()
.max_by(|k1, k2| k1.vers.cmp(&k2.vers))
.expect("at least one version should exist"))
self.tree.latest_record(name)
}

fn add_record(&self, record: CrateVersion) -> Result<(), Error> {
let path = self.compute_record_path(record.name.as_str());

if let Ok(file) = fs::File::open(&path) {
let reader = io::BufReader::new(file);
let records = reader
.lines()
.map(|line| Ok(json::from_str::<CrateVersion>(line?.as_str())?))
.collect::<Result<Vec<CrateVersion>, Error>>()?;
let latest = records
.into_iter()
.max_by(|k1, k2| k1.vers.cmp(&k2.vers))
.expect("at least one record should exist");
if record.vers <= latest.vers {
return Err(Error::from(AlexError::VersionTooLow {
krate: record.name,
hosted: latest.vers,
published: record.vers,
}));
}
} else {
let parent = path.parent().unwrap();
fs::create_dir_all(parent)?;
}

let mut file = fs::OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(path)?;
json::to_writer(&mut file, &record)?;
writeln!(file)?;
file.flush()?;

Ok(())
self.tree.add_record(record)
}

fn alter_record<F>(&self, name: &str, version: Version, func: F) -> Result<(), Error>
where
F: FnOnce(&mut CrateVersion),
{
let path = self.compute_record_path(name);
let file = fs::File::open(path.as_path()).map_err(|err| match err.kind() {
io::ErrorKind::NotFound => Error::from(AlexError::CrateNotFound {
name: String::from(name),
}),
_ => Error::from(err),
})?;
let mut krates: Vec<CrateVersion> = {
let mut out = Vec::new();
for line in io::BufReader::new(file).lines() {
let krate = json::from_str(line?.as_str())?;
out.push(krate);
}
out
};
let found = krates
.iter_mut()
.find(|krate| krate.vers == version)
.ok_or_else(|| {
Error::from(AlexError::CrateNotFound {
name: String::from(name),
})
})?;

func(found);

let lines = krates
.into_iter()
.map(|krate| json::to_string(&krate))
.collect::<Result<Vec<String>, _>>()?;
fs::write(path.as_path(), lines.join("\n") + "\n")?;

Ok(())
self.tree.alter_record(name, version, func)
}
}
Loading

0 comments on commit 59abbfe

Please sign in to comment.