Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New user command #10

Merged
merged 24 commits into from
May 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
805af5c
Framework for 'user new' command added
bookdude13 May 17, 2016
d58b457
Adding users works. Needs error checking
bookdude13 May 18, 2016
de4a8fd
User new implemented
bookdude13 May 18, 2016
39bb46c
Renamed to new_user
bookdude13 May 19, 2016
8106664
Removed redundant main function
bookdude13 May 20, 2016
a5aecbc
Added Ramith's suggestions except name reference instead of borrow
bookdude13 May 20, 2016
e4914fd
Rearranged order of structs
bookdude13 May 20, 2016
f58397d
Added comment
bookdude13 May 20, 2016
e993b45
Made comment doc comment
bookdude13 May 20, 2016
662f5ab
Generalized reading and writing protonfile, cleaned up new_user
bookdude13 May 20, 2016
2503573
Added find_user function
bookdude13 May 21, 2016
5f43427
Trim string read from file
bookdude13 May 21, 2016
58dbcd8
Make utils available publicly for tests
bookdude13 May 21, 2016
3dde31b
Added test for new_user
bookdude13 May 21, 2016
8d0f652
Added test with nonexistent protonfile. Tweaked other test
bookdude13 May 22, 2016
64977f8
Added test for bad key path. Formatting
bookdude13 May 22, 2016
3ebcdf7
Deriving PartialEq and Eq for Project and User
bookdude13 May 22, 2016
b8fe35d
Removed unnecessary clone and debug print statement
bookdude13 May 22, 2016
f945f02
Renamed find_user to user_exists. Added duplicate user check to add_user
bookdude13 May 22, 2016
89d8daa
Fixed duplicate user check
bookdude13 May 22, 2016
7c4610b
Added duplicate user tests. Fixed comment. Removed unnecessary map_err
bookdude13 May 22, 2016
f73b10b
Removed backing out of directories at end of tests
bookdude13 May 23, 2016
a29dd14
Removed unused import
bookdude13 May 23, 2016
3e63976
Tweaked error message
bookdude13 May 23, 2016
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
19 changes: 14 additions & 5 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ pub enum Error {
Git(git2::Error),
JsonDecode(json::DecoderError),
FolderNotEmpty(String, usize),
ArgumentNotFound,
InvalidPublicKey(String),
LoadProjectError,
DuplicateUser(String, String),
TodoErr,
}

Expand All @@ -19,7 +21,9 @@ impl error::Error for Error {
Error::Git(_) => "Libgit2 error occurred",
Error::JsonDecode(_) => "Json decoding error occurred",
Error::FolderNotEmpty(_, _) => "Root folder was not empty",
Error::ArgumentNotFound => "Argument not found/matched",
Error::InvalidPublicKey(_) => "Invalid public key",
Error::LoadProjectError => "Loading project failed",
Error::DuplicateUser(_, _) => "User already exists",
Error::TodoErr => "Todo",
}
}
Expand All @@ -30,7 +34,9 @@ impl error::Error for Error {
Error::Git(ref err) => Some(err),
Error::JsonDecode(ref err) => Some(err),
Error::FolderNotEmpty(_, _) => None,
Error::ArgumentNotFound => None,
Error::InvalidPublicKey(_) => None,
Error::LoadProjectError => None,
Error::DuplicateUser(_, _) => None,
Error::TodoErr => None,
}
}
Expand All @@ -47,8 +53,11 @@ impl fmt::Display for Error {
"Json decoding error occurred: {}", error::Error::description(err)),
Error::FolderNotEmpty(ref root, count) => write!(f,
"{} was not empty: {} files exist", root, count),
Error::ArgumentNotFound => write!(f,
"Argument not found. Did you type it correctly?"),
Error::InvalidPublicKey(ref key) => write!(f,
"Public key '{}' is invalid", key),
Error::LoadProjectError => write!(f, "Loading project failed"),
Error::DuplicateUser(ref key, ref user) => write!(f,
"Duplicate user '{}' or key '{}'", user, key),
Error::TodoErr => write!(f, "TodoErr"),
}
}
Expand Down
18 changes: 4 additions & 14 deletions src/init.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//! This module initializes a project.

use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::path::Path;

use rustc_serialize::json;
use git2::{Oid, Repository, Signature};

use utils;
use Error;
use project_types::Project;
use Project;

/// Initializes a new project at root. The root must either not exist, or must
/// be an empty directory. This will
Expand Down Expand Up @@ -57,17 +56,8 @@ fn folder_not_empty(root: &Path, count: usize) -> Error {
///
/// Impure.
fn make_protonfile(root: &Path) -> Result<(), Error> {
// Create content
let project = Project::empty();
let pretty_json = json::as_pretty_json(&project);

// Make path
let mut path = PathBuf::from(root);
path.push("Protonfile.json");

File::create(&path)
.and_then(|mut protonfile| write!(protonfile, "{}\n", pretty_json))
.map_err(Error::Io)
utils::write_protonfile(&project, Some(root))
}

/// Initializes a git repository at root.
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
extern crate git2;
extern crate rustc_serialize;

pub mod utils;
mod init;
mod user;
mod project_types;
mod error;

// Re-exports
pub use error::*;
pub use init::*;
pub use user::*;
pub use project_types::*;
8 changes: 2 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Command-line interface for Proton

Usage:
./proton init <folder>
./proton new-user <folder> <public-key> <name>
./proton new-user <name> <public-key>
./proton (-h | --help)

Options:
Expand All @@ -33,7 +33,6 @@ struct Args {
}

fn main() {

let args: Args = Docopt::new(USAGE)
.and_then(|d| d.decode())
.unwrap_or_else(|e| e.exit());
Expand Down Expand Up @@ -61,11 +60,8 @@ fn run_init(args: Args) -> Result<(), Error> {
}

fn run_new_user(args: Args) -> Result<(), Error> {
let folder = args.arg_folder.unwrap();
let public_key = args.arg_public_key.unwrap();
let public_key_path = Path::new(&public_key);
let name = args.arg_name.unwrap();
println!("{}, {}, {}", folder, public_key, name);
Ok(())
//proton_cli::add_user(folder, public_key_path, name)
proton_cli::new_user(&public_key_path, name)
}
73 changes: 66 additions & 7 deletions src/project_types.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,83 @@

use Error;

/// Structure to represent a Proton Project.
/// This is what will be written to a Protonfile at the project root.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be right next to Project. Move User down below pub struct Project and above impl Project.

#[derive(Debug, RustcEncodable, RustcDecodable)]
#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub struct Project {
pub name: String,
pub users: Vec<User>,
}

#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub struct User {
pub name: String,
pub public_key: String,
}

impl Project {
/// Creates an empty project
pub fn empty() -> Project {
Project {
name: "New Project".to_owned(),
users: Vec::new(),
}
}
}

impl PartialEq for Project {
fn eq(&self, other: &Project) -> bool {
self.name == other.name
/// Finds a user with the given public key
/// Returns the user if found, else None
fn find_user_by_public_key(&self, pub_key: &str) -> Option<&User> {
for u in &self.users {
if u.public_key == pub_key {
return Some(u);
}
}
None::<&User>
}

/// Finds a user with the given name
/// Returns the user if found, else None
fn find_user_by_name(&self, name: &str) -> Option<&User> {
for u in &self.users {
if u.name == name {
return Some(u);
}
}
None::<&User>
}

/// Finds a user in the users vector
/// Returns true if found, else false
pub fn user_exists(&self, user: &User) -> bool {
for u in &self.users {
if user == u {
return true;
}
}
return false;
}

/// Adds a user to the project
pub fn add_user(&self, name: &str, pub_key: &str) -> Result<Project, Error> {

let user = User {
name: name.to_string(),
public_key: pub_key.to_string(),
};

if self.find_user_by_name(name).is_some() ||
self.find_user_by_public_key(pub_key).is_some() {

Err(self.duplicate_user(pub_key, name))
} else {
let mut new_project = self.clone();
new_project.users.push(user);
Ok(new_project)
}
}

fn duplicate_user(&self, pub_key: &str, name: &str) -> Error {
Error::DuplicateUser(pub_key.to_string(), name.to_string())
}
}

impl Eq for Project {
}
22 changes: 22 additions & 0 deletions src/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! This module manages project users

use std::path::Path;

use Error;
use utils;


/// Creates a new user for the project in the current directory.
/// Assumes the current directory contains a Protonfile.json file.
///
/// 1. Read the new user's public key from the file path given
/// 2. Add the user's name and public key to the protonfile
///
/// Impure.
pub fn new_user<P: AsRef<Path>>(public_key_path: P, name: String) -> Result<(), Error> {
let pub_key = try!(utils::file_as_string(public_key_path));
let project = try!(utils::read_protonfile(None::<P>));
let new_project = try!(project.add_user(&name, &pub_key));
utils::write_protonfile(&new_project, None::<P>)
}

52 changes: 52 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use rustc_serialize::json;

use project_types::Project;
use error::Error;


/// Reads a Project from a Protonfile.
/// Wraps any errors in proton_cli::Error
/// Assumes Protonfile.json resides in the current directory
/// unless a path to the Protonfile is given
pub fn read_protonfile<P: AsRef<Path>>(pf_path: Option<P>) -> Result<Project, Error> {
let protonfile_path = build_protonfile_path(pf_path);
let protonfile = try!(file_as_string(&protonfile_path));
json::decode(&protonfile).map_err(Error::JsonDecode)
}

/// Saves a Project to a Protonfile.
/// Assumes the Protonfile is in the current directory
/// unless a path to the Protonfile is given
pub fn write_protonfile<P: AsRef<Path>>(project: &Project, pf_path: Option<P>) -> Result<(), Error> {
let pretty_json = json::as_pretty_json(&project);
let protonfile_path = build_protonfile_path(pf_path);
File::create(&protonfile_path)
.and_then(|mut protonfile| write!(protonfile, "{}\n", pretty_json))
.map_err(Error::Io)
}

/// Reads a file as a string.
/// Wraps Read::read_to_string errors in proton_cli::Error
pub fn file_as_string<P: AsRef<Path>>(path: P) -> Result<String, Error> {
File::open(path)
.and_then(|mut file| {
let mut string = String::new();
file.read_to_string(&mut string)
.and_then(|_| Ok(string.trim().to_string()))
})
.map_err(Error::Io)
}

fn build_protonfile_path<P: AsRef<Path>>(path_opt: Option<P>) -> PathBuf {
let mut protonfile_path = PathBuf::new();
let _ = match path_opt {
Some(path) => protonfile_path.push(path),
None => (),
};
protonfile_path.push("Protonfile.json");
protonfile_path
}
Loading