Skip to content
This repository has been archived by the owner on Sep 26, 2023. It is now read-only.

Init repo list #24

Merged
merged 7 commits into from
Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 37 additions & 12 deletions git/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
extern crate git2;
pub extern crate git2;

use git2::{Error, Repository};
use std::path::Path;
use std::{collections::HashMap, fs, path::Path};

pub trait GitOps {
fn cat_file(&self, repo_path: &Path, reference: &str, filename: &str)
-> Result<Vec<u8>, Error>;
fn cat_file(
&self,
repo: &Repository,
reference: &str,
filename: &str,
) -> Result<Vec<u8>, Error>;
}

pub struct LibGitOps;
Expand All @@ -15,12 +19,10 @@ impl GitOps for LibGitOps {
/// point to and return it as a String.
fn cat_file(
&self,
repo_path: &Path,
repo: &Repository,
reference: &str,
filename: &str,
) -> Result<Vec<u8>, Error> {
let repo = Repository::open(repo_path)?;

let reference = repo.find_reference(reference)?;
let tree = reference.peel_to_tree()?;
let path = std::path::Path::new(filename);
Expand All @@ -30,28 +32,51 @@ impl GitOps for LibGitOps {
}
}

pub fn load_repos(root_path: &Path) -> HashMap<String, Repository> {
fs::read_dir(root_path)
.expect("Failed to read repos directory")
.filter_map(|entry| {
entry.ok().and_then(|e| {
let path = e.path();
if path.is_dir() {
let local_path = path.clone();
let repo_name = local_path
.file_stem()
.and_then(|name| name.to_os_string().into_string().ok());

repo_name.and_then(|name| {
Repository::open(path).ok().and_then(|repo| Some((name, repo)))
})
} else {
None
}
})
})
.collect()
}

#[cfg(test)]
mod tests {

extern crate tempdir;

use super::{LibGitOps, GitOps};
use super::{GitOps, LibGitOps};

use git2::Repository;
use std::fs;
use std::io::Write;
use std::path::Path;

fn git_cat_file(
repo_path: &Path,
repo_path: &Repository,
reference: &str,
filename: &str,
) -> Result<Vec<u8>, git2::Error> {
let gh = LibGitOps {};
gh.cat_file(repo_path, reference, filename)
}

fn git_cat_file_err(repo_path: &Path, reference: &str, filename: &str) -> git2::Error {
fn git_cat_file_err(repo_path: &Repository, reference: &str, filename: &str) -> git2::Error {
git_cat_file(repo_path, reference, filename).expect_err("should be an error")
}

Expand Down Expand Up @@ -96,7 +121,7 @@ mod tests {

pub fn with_repo<F>(file_contents: &str, file: &str, callback: F)
where
F: Fn(&Path),
F: Fn(&Repository),
{
let dir = tempdir::TempDir::new("testgitrepo").expect("can't create tmp dir");

Expand Down Expand Up @@ -124,7 +149,7 @@ mod tests {
})
.expect("can't do first commit");

callback(repo.path());
callback(&repo);
dir.close().expect("couldn't close the dir");
}

Expand Down
5 changes: 1 addition & 4 deletions handlers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ license = "MIT"

[dependencies]
git = { path = "../git" }
actix-web = "^0.7.18"
git2 = "^0.8.0"
serde = "^1.0.89"
serde_derive = "^1.0.89"
actix = "^0.7.9"

# When building for musl (ie. a static binary), we opt into the "vendored"
# feature flag of openssl-sys which compiles libopenssl statically for us.
Expand Down
130 changes: 50 additions & 80 deletions handlers/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,100 +1,70 @@
#[macro_use]
extern crate serde_derive;
use actix::dev::{MessageResponse, ResponseChannel};
use actix::{Actor, Context, Handler, Message};
use git::{git2::Repository, GitOps, LibGitOps};
use std::collections::HashMap;

use actix_web::{dev::Handler, Binary, FromRequest, HttpRequest, Path, Query};
use git::{LibGitOps, GitOps};
use std::path::PathBuf;

#[derive(Deserialize)]
pub struct PathParams {
repo: String,
pub struct CatFile {
pub repo_key: String,
pub reference: String,
pub filename: String,
}

#[derive(Deserialize)]
pub struct QueryParams {
reference: String,
file: String,
impl CatFile {
pub fn new(repo_key: String, reference: String, filename: String) -> CatFile {
CatFile {
repo_key,
filename,
reference,
}
}
}

pub struct RepoHandler {
pub repo_root: String,
git_ops: Box<GitOps>,
impl Message for CatFile {
type Result = CatFileResponse;
}

impl<S> Handler<S> for RepoHandler {
type Result = Binary;

fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
//TODO https://actix.rs/docs/errors/
let path_params = Path::<PathParams>::extract(req).expect("Wrong path params");
let query_params = Query::<QueryParams>::extract(req).expect("Wront query params");
let repo_path: PathBuf = [&self.repo_root, &path_params.repo].iter().collect();
let reference = format!("refs/{}", query_params.reference);
//TODO return proper content type depending on the content of the blob
self.git_ops
.cat_file(&repo_path, &reference, &query_params.file)
.map(Binary::from)
.expect("Can't cat file")
}
}
pub struct CatFileResponse(pub Result<Vec<u8>, String>);

impl RepoHandler {
pub fn new(repo_root: String) -> RepoHandler {
RepoHandler {
repo_root,
git_ops: Box::new(LibGitOps {}),
impl<A, M> MessageResponse<A, M> for CatFileResponse
where
A: Actor,
M: Message<Result = CatFileResponse>,
{
fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
if let Some(tx) = tx {
tx.send(self);
}
}
}

#[cfg(test)]
mod tests {

use super::RepoHandler;
use actix_web::http::StatusCode;
use actix_web::test;
use actix_web::{Binary, Body};
use git::GitOps;
use std::path;

struct TestGitOps {
res: Vec<u8>,
}
pub struct GitRepos {
repos: HashMap<String, Repository>,
ops: Box<GitOps>,
}

impl GitOps for TestGitOps {
fn cat_file(
&self,
_repo_path: &path::Path,
_reference: &str,
_filename: &str,
) -> Result<Vec<u8>, git2::Error> {
Ok(self.res.to_owned())
}
}
impl Actor for GitRepos {
type Context = Context<Self>;
}

fn bin_ref(body: &Body) -> &Binary {
match *body {
Body::Binary(ref bin) => bin,
_ => panic!(),
impl GitRepos {
pub fn new(repos: HashMap<String, Repository>) -> GitRepos {
GitRepos {
repos,
ops: Box::new(LibGitOps {}),
}
}
}

#[test]
fn it_returns_the_content_of_the_file_by_cat_file() {
let rp = RepoHandler {
repo_root: "not-used".to_string(),
git_ops: Box::new(TestGitOps {
res: b"hello".to_vec(),
}),
};
impl Handler<CatFile> for GitRepos {
type Result = CatFileResponse;

let resp = test::TestRequest::with_header("content-type", "application/json")
.param("repo", "client-config.git")
.uri("/repo/?reference=the-reference&file=the-file")
.run(&rp)
.expect("can't run test request");
assert_eq!(resp.status(), StatusCode::OK);
assert_eq!(bin_ref(resp.body()), &Binary::from_slice(b"hello"));
fn handle(&mut self, task: CatFile, _: &mut Self::Context) -> Self::Result {
CatFileResponse(match self.repos.get(&task.repo_key) {
Some(repo) => self
.ops
.cat_file(repo, &task.reference, &task.filename)
.map_err(|x| x.to_string()),
None => Err(format!("No repo found with name {}", &task.repo_key)),
})
}

}
6 changes: 4 additions & 2 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ edition = "2018"
license = "MIT"

[dependencies]
git = { path = "../git" }
handlers = { path = "../handlers" }
actix-web = "^0.7.18"
clap = "^2.32.0"
git2 = "^0.8.0"
listenfd = "^0.3.3"
futures = "^0.1.26"
serde = "^1.0.89"
serde_derive = "^1.0.89"

# When building for musl (ie. a static binary), we opt into the "vendored"
# feature flag of openssl-sys which compiles libopenssl statically for us.
Expand Down
Loading