Skip to content

Commit

Permalink
Implemented Editing a Paper (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
Teddy-van-Jerry committed Aug 21, 2024
1 parent e4ec6be commit 3a3c05e
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 43 deletions.
134 changes: 99 additions & 35 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,48 @@ impl PaperCategory {
sub_categories: vec![],
}
}

fn copy_file(
dir: PathBuf,
outside_file: Option<String>,
id: &String,
entry_ref: &mut PaperEntry,
) -> Result<(), Box<dyn Error>> {
if let Some(outside_file) = outside_file {
// check if the file exists
let outside_file_path = PathBuf::from(&outside_file);
if !outside_file_path.exists() {
eprintln!("Error: the file '{}' does not exist.", outside_file);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"file not found",
)));
} else {
let file_name = match outside_file_path.extension() {
Some(ext) => {
let ext = ext.to_str().unwrap();
// if ext != "pdf" {
// eprintln!("Error: the file '{}' is not a PDF file.", outside_file);
// return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput, "not a PDF file")));
// }
format!("{}.{}", &id, ext)
}
_ => id.clone(),
};
let inside_file = dir.join(file_name);
std::fs::copy(outside_file_path, &inside_file)?;
entry_ref.file = Some(
inside_file
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string(),
);
}
}
Ok(())
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -69,6 +111,10 @@ impl Index {
}
}

/// Paper entry in the database
///
/// The fields are now for testing purpose.
/// More fields will be added in the future.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaperEntry {
pub title: Option<String>,
Expand All @@ -88,6 +134,18 @@ impl PaperEntry {
file: None,
}
}

pub fn update_metadata(&mut self, paper: &PaperEntry) {
if let Some(title) = paper.title.clone() {
self.title = Some(title);
}
if let Some(authors) = paper.authors.clone() {
self.authors = Some(authors);
}
if let Some(year) = paper.year {
self.year = Some(year);
}
}
}

type PaperID = String;
Expand Down Expand Up @@ -137,12 +195,15 @@ impl TpIndex for PaperCategory {
}

pub trait TpManage {
/// Add a paper entry to the category
/// Add a paper entry
///
/// If the paper entry is already in the category, it will be overwritten.
fn add(&mut self, id: PaperID, entry: PaperEntry, force: bool) -> Result<(), Box<dyn Error>>;

/// Remove a paper entry from the category
/// Edit a paper entry
fn edit(&mut self, id: PaperID, entry: PaperEntry) -> Result<(), Box<dyn Error>>;

/// Remove a paper entry
fn remove(&mut self, id: PaperID) -> Result<(), Box<dyn Error>>;
}

Expand Down Expand Up @@ -177,6 +238,7 @@ impl TpManage for PaperCategory {
force: bool,
) -> Result<(), Box<dyn Error>> {
// 1. check if the paper entry is already in the category
// (TODO: more efficient way as using the return value to avoid double check)
if self.papers.contains_key(&id) && !force {
eprintln!(
"Error: the paper entry '{}' already exists in the category.",
Expand All @@ -189,39 +251,7 @@ impl TpManage for PaperCategory {
}
// 2. copy the file to the category
let outside_file = entry.file.clone();
if let Some(outside_file) = outside_file {
// check if the file exists
let outside_file_path = PathBuf::from(&outside_file);
if !outside_file_path.exists() {
eprintln!("Error: the file '{}' does not exist.", outside_file);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"file not found",
)));
} else {
let file_name = match outside_file_path.extension() {
Some(ext) => {
let ext = ext.to_str().unwrap();
// if ext != "pdf" {
// eprintln!("Error: the file '{}' is not a PDF file.", outside_file);
// return Err(Box::new(std::io::Error::new(std::io::ErrorKind::InvalidInput, "not a PDF file")));
// }
format!("{}.{}", &id, ext)
}
_ => id.clone(),
};
let inside_file = self.dir.join(file_name);
std::fs::copy(outside_file_path, &inside_file)?;
entry.file = Some(
inside_file
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string(),
);
}
}
Self::copy_file(self.dir.clone(), outside_file, &id, &mut entry)?;
// 3. add the paper entry to the category
self.papers.insert(id, entry);
// 4. save the index
Expand All @@ -232,6 +262,33 @@ impl TpManage for PaperCategory {
self.index_to_file(&index)
}

fn edit(&mut self, id: PaperID, entry: PaperEntry) -> Result<(), Box<dyn Error>> {
match self.papers.get_mut(&id) {
Some(entry_ref) => {
// 1. copy the file to the category
let outside_file = entry.file.clone();
Self::copy_file(self.dir.clone(), outside_file, &id, entry_ref)?;
entry_ref.update_metadata(&entry);
// 2. save the index
let index = Index {
papers: self.papers.clone(),
sub_categories: self.sub_categories.clone(),
};
self.index_to_file(&index)
}
None => {
eprintln!(
"Error: the paper entry '{}' does not exist in the database.",
id
);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"paper entry not found",
)));
}
}
}

fn remove(&mut self, id: PaperID) -> Result<(), Box<dyn Error>> {
match self.papers.remove(&id) {
Some(entry) => {
Expand Down Expand Up @@ -269,6 +326,13 @@ impl TpManage for Database {
self.top_category.add(id, entry, force)
}

fn edit(&mut self, id: PaperID, entry: PaperEntry) -> Result<(), Box<dyn Error>> {
// 1. safety check
_ck_id(&id)?;
// 2. edit from the top category (TODO: check category)
self.top_category.edit(id, entry)
}

fn remove(&mut self, id: PaperID) -> Result<(), Box<dyn Error>> {
// 1. safety check
_ck_id(&id)?;
Expand Down
42 changes: 36 additions & 6 deletions src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl Manager {
Commands::Activate(_) => self.cmd_activate(),
Commands::Add(_) => self.cmd_add(),
Commands::Config(_) => self.cmd_config(),
// Commands::Edit(_) => self.cmd_edit(),
Commands::Edit(_) => self.cmd_edit(),
Commands::Info(_) => self.cmd_info(),
Commands::Init(_) => self.cmd_init(),
// Commands::List(_) => self.cmd_list(),
Expand Down Expand Up @@ -172,16 +172,46 @@ impl Manager {
year: args.year.clone(),
};
database
.add(
args.id.clone(),
paper,
args.force,
)
.add(args.id.clone(), paper, args.force)
.map_err(|_| ())?;
// 5. save the database to the file (TODO)
Ok(())
}

pub fn cmd_edit(&self) -> Result<(), ()> {
// 1. get the correct database directory
let database_dir = match &self.config.activated {
Some(activated) => activated.clone(),
None => {
eprintln!("Error: No database is activated.");
return Err(());
}
};
// 2. use the Database struct to handle the database
let mut database = Database::new_from_index(database_dir);
// 3. get the paper entry from the user input
let args = match &self.args.cmd {
Commands::Edit(args) => args,
_ => {
assert!(
false,
"Internal Error: This function should only be called in the 'edit' command."
);
return Err(());
}
};
// 4. edit the paper entry from the database
let paper = PaperEntry {
file: args.file.clone(),
title: args.title.clone(),
authors: args.authors.clone(),
year: args.year.clone(),
};
database.edit(args.id.clone(), paper).map_err(|_| ())?;
// 5. save the database to the file (TODO)
Ok(())
}

pub fn cmd_info(&self) -> Result<(), ()> {
// get the activated database
let activated = match &self.config.activated {
Expand Down
13 changes: 11 additions & 2 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,18 @@ pub struct CommandEditArgs {
/// The unique id of the paper to edit
#[arg(index = 1)]
pub id: String,
/// The file of the paper to edit
#[arg(short, long)]
/// The file of the paper to add
#[arg(short = 'f', long)]
pub file: Option<String>,
/// Title of the paper
#[arg(short = 't', long)]
pub title: Option<String>,
/// Author of the paper (one by one with multiple flags)
#[arg(short = 'a', long = "author")]
pub authors: Option<Vec<String>>,
/// Year of the paper
#[arg(short = 'y', long)]
pub year: Option<u32>,
}

#[derive(Args, Clone, Debug)]
Expand Down

0 comments on commit 3a3c05e

Please sign in to comment.