M Cargo.lock => Cargo.lock +1 -1
@@ 306,7 306,7 @@ dependencies = [
[[package]]
name = "der_die_das"
-version = "0.2.4"
+version = "0.2.5"
dependencies = [
"clap",
"clap_complete_command",
M Cargo.toml => Cargo.toml +1 -1
@@ 1,6 1,6 @@
[package]
name = "der_die_das"
-version = "0.2.4"
+version = "0.2.5"
edition = "2021"
authors = ["Perma Alesheikh <me@prma.dev>"]
license = "EUPL-1.2"
M src/arugments.rs => src/arugments.rs +21 -0
@@ 37,6 37,10 @@ pub enum Commands {
/// ask stuff.
#[command(subcommand)]
Ask(AskCommands),
+
+ /// edit Items.
+ #[command(subcommand)]
+ Edit(EditCommands),
}
#[derive(Clone, Debug, Subcommand)]
@@ 52,6 56,23 @@ pub enum NewCommands {
group: Option<String>,
},
}
+
+#[derive(Clone, Debug, Subcommand)]
+pub enum EditCommands {
+ Noun {
+ #[arg(short = 'i', long, value_name = "ID")]
+ id: i128,
+ #[arg(short = 'a', long, value_name = "ARTICLES")]
+ articles: Option<Vec<Article>>,
+ #[arg(short = 'w', long, value_name = "WORD")]
+ word: Option<String>,
+ #[arg(short = 'm', long, value_name = "MEANING")]
+ meaning: Option<String>,
+ #[arg(short = 'g', long, value_name = "GROUP")]
+ group: Option<String>,
+ },
+}
+
#[derive(Clone, Debug, Subcommand)]
pub enum ListCommands {
Nouns,
M src/attempt.rs => src/attempt.rs +1 -1
@@ 16,7 16,7 @@ pub enum Conclusion {
WrongArticle(Article),
}
-pub trait Repo {
+pub trait Repo: Clone {
/// Saves an attempt
///
/// # Errors
M src/commands.rs => src/commands.rs +49 -29
@@ 1,7 1,7 @@
use der_die_das::{
- attempt::{Attempt, Repo as _},
+ attempt::{self, Attempt},
history::History,
- nouns::Repo as _,
+ nouns::{self},
};
use inquire::{
validator::{ErrorMessage, StringValidator, Validation},
@@ 15,10 15,7 @@ use color_eyre::{
Result,
};
use comfy_table::{Cell, Row};
-use der_die_das::{
- nouns::{Article, Noun},
- storage::Storage,
-};
+use der_die_das::nouns::{Article, Noun};
use deunicode::deunicode;
use crate::{config::GroupConfiguration, importer};
@@ 29,7 26,7 @@ pub fn new_noun(
meaning: String,
article: Vec<Article>,
group: Option<String>,
- storage: &Storage,
+ storage: impl nouns::Repo,
) -> Result<()> {
if word.is_empty() {
return Err(eyre!("word must be at least 1 character"));
@@ 47,10 44,10 @@ pub fn new_noun(
Ok(())
}
-pub fn list_nouns(storage: &Storage) -> Result<()> {
+pub fn list_nouns(noun_repo: impl nouns::Repo, attempt_repo: impl attempt::Repo) -> Result<()> {
let h = History {
- nouns: storage.all_nouns()?,
- attempts: storage.all_attempts()?,
+ nouns: noun_repo.all_nouns()?,
+ attempts: attempt_repo.all_attempts()?,
};
h.grouped_confidence_map()
@@ 153,7 150,7 @@ impl StringValidator for ValidateLastWordShouldMatchTheSpelling {
}
}
-fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
+fn ask_noun(attempt_repo: impl attempt::Repo, noun: &Noun) -> Result<()> {
let prompt = format!("{} {}\n>", "___".reversed(), noun.word.reversed());
let resp = inquire::Text::new(&prompt)
.with_help_message(&noun.meaning)
@@ 182,7 179,7 @@ fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
let now = time::OffsetDateTime::now_utc();
if noun.articles.contains(&article) {
- storage.save_attempt(Attempt {
+ attempt_repo.save_attempt(Attempt {
id: now.unix_timestamp_nanos(),
at: now,
for_word: noun.id,
@@ 202,19 199,23 @@ fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
.bold()
);
- storage.save_attempt(Attempt {
+ attempt_repo.clone().save_attempt(Attempt {
id: now.unix_timestamp_nanos(),
at: now,
for_word: noun.id,
what_happened: der_die_das::attempt::Conclusion::WrongArticle(article),
})?;
- ask_noun(storage, noun)
+ ask_noun(attempt_repo, noun)
}
}
-fn get_confidence(storage: &Storage, id: i128) -> Result<(Noun, u8)> {
- let nouns = storage.all_nouns()?;
- let attempts = storage.all_attempts()?;
+fn get_confidence(
+ noun_repo: impl nouns::Repo,
+ attempt_repo: impl attempt::Repo,
+ id: i128,
+) -> Result<(Noun, u8)> {
+ let nouns = noun_repo.all_nouns()?;
+ let attempts = attempt_repo.all_attempts()?;
let h = History { nouns, attempts };
h.confidence_map()
.into_iter()
@@ 222,9 223,13 @@ fn get_confidence(storage: &Storage, id: i128) -> Result<(Noun, u8)> {
.ok_or_eyre("Some how the word is not there any more!")
}
-pub fn ask_next(storage: &Storage, group_config: &GroupConfiguration) -> Result<()> {
- let nouns = storage.all_nouns()?;
- let attempts = storage.all_attempts()?;
+pub fn ask_next(
+ noun_repo: impl nouns::Repo,
+ attempt_repo: impl attempt::Repo,
+ group_config: &GroupConfiguration,
+) -> Result<()> {
+ let nouns = noun_repo.clone().all_nouns()?;
+ let attempts = attempt_repo.clone().all_attempts()?;
let h = History { nouns, attempts };
let chosen = if group_config.enable {
@@ 234,9 239,9 @@ pub fn ask_next(storage: &Storage, group_config: &GroupConfiguration) -> Result<
}
.ok_or_eyre("there are no words available for asking. Have you added some?")?;
- ask_noun(storage, &chosen.0)?;
+ ask_noun(attempt_repo.clone(), &chosen.0)?;
- let (_, conf) = get_confidence(storage, chosen.0.id)?;
+ let (_, conf) = get_confidence(noun_repo, attempt_repo, chosen.0.id)?;
println!(
"The confidence for the word is now at {}.",
@@ 246,9 251,13 @@ pub fn ask_next(storage: &Storage, group_config: &GroupConfiguration) -> Result<
Ok(())
}
-pub fn group_ask(storage: &Storage, group_config: &GroupConfiguration) -> Result<()> {
- let nouns = storage.all_nouns()?;
- let attempts = storage.all_attempts()?;
+pub fn group_ask(
+ noun_repo: &impl nouns::Repo,
+ attempt_repo: &impl attempt::Repo,
+ group_config: &GroupConfiguration,
+) -> Result<()> {
+ let nouns = noun_repo.clone().all_nouns()?;
+ let attempts = attempt_repo.clone().all_attempts()?;
let h = History { nouns, attempts };
let gr = h
@@ 260,9 269,9 @@ pub fn group_ask(storage: &Storage, group_config: &GroupConfiguration) -> Result
println!();
println!("{}/{}", (i + 1).bold(), number_of_words_in_gr);
- ask_noun(storage, &noun)?;
+ ask_noun(attempt_repo.clone(), &noun)?;
- let (_, conf) = get_confidence(storage, noun.id)?;
+ let (_, conf) = get_confidence(noun_repo.clone(), attempt_repo.clone(), noun.id)?;
println!(
"The confidence for the word is now at {}.",
@@ 273,7 282,7 @@ pub fn group_ask(storage: &Storage, group_config: &GroupConfiguration) -> Result
})
}
-pub fn import_nouns(storage: &Storage, at: PathBuf) -> Result<()> {
+pub fn import_nouns(noun_repo: &impl nouns::Repo, at: PathBuf) -> Result<()> {
if !at.try_exists()? {
return Err(eyre!(format!(
"Such file or directory does not exist: {}",
@@ 296,7 305,18 @@ pub fn import_nouns(storage: &Storage, at: PathBuf) -> Result<()> {
imported_groups
.into_iter()
.flat_map(Vec::<Noun>::from)
- .try_for_each(|n| storage.save_noun(n))?;
+ .try_for_each(|n| noun_repo.clone().save_noun(n))?;
Ok(())
}
+
+pub fn edit_noun(
+ noun_repo: impl nouns::Repo,
+ id: i128,
+ article: Option<Vec<Article>>,
+ word: Option<String>,
+ meaning: Option<String>,
+ group: Option<String>,
+) -> Result<()> {
+ noun_repo.edit_noun_by_id(id, article, word, meaning, group)
+}
M src/main.rs => src/main.rs +17 -4
@@ 56,14 56,27 @@ fn main() -> Result<()> {
),
},
Commands::List(list_commands) => match list_commands {
- arugments::ListCommands::Nouns => commands::list_nouns(&storage),
+ arugments::ListCommands::Nouns => commands::list_nouns(&storage, &storage),
},
Commands::Import(import_commands) => match import_commands {
- arugments::ImportCommands::Nouns { at } => commands::import_nouns(&storage, at),
+ arugments::ImportCommands::Nouns { at } => commands::import_nouns(&&storage, at),
},
Commands::Ask(ask_commands) => match ask_commands {
- arugments::AskCommands::Nouns => commands::ask_next(&storage, &configs.group),
- arugments::AskCommands::Group => commands::group_ask(&storage, &configs.group),
+ arugments::AskCommands::Nouns => {
+ commands::ask_next(&storage, &storage, &configs.group)
+ }
+ arugments::AskCommands::Group => {
+ commands::group_ask(&&storage, &&storage, &configs.group)
+ }
+ },
+ Commands::Edit(edit_commands) => match edit_commands {
+ arugments::EditCommands::Noun {
+ articles,
+ word,
+ meaning,
+ group,
+ id,
+ } => commands::edit_noun(&storage, id, articles, word, meaning, group),
},
},
None => todo!(),
M src/nouns.rs => src/nouns.rs +16 -1
@@ 32,7 32,7 @@ impl Display for Article {
}
}
-pub trait Repo {
+pub trait Repo: Clone {
/// Saves one noun.
///
/// # Errors
@@ 63,4 63,19 @@ pub trait Repo {
/// This function will return an error if a noun by that ID does not exist
/// or there is an IO failure.
fn delete_noun_by_id(self, id: i128) -> Result<()>;
+
+ /// Edit noun
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if a noun by that ID does not exist
+ /// or there is an IO failure.
+ fn edit_noun_by_id(
+ self,
+ id: i128,
+ article: Option<Vec<Article>>,
+ word: Option<String>,
+ meaning: Option<String>,
+ group: Option<String>,
+ ) -> Result<()>;
}
M src/storage.rs => src/storage.rs +37 -0
@@ 121,6 121,43 @@ impl nouns::Repo for &Storage {
std::fs::remove_file(&file)?;
Ok(())
}
+
+ fn edit_noun_by_id(
+ self,
+ id: i128,
+ article: Option<Vec<Article>>,
+ word: Option<String>,
+ meaning: Option<String>,
+ group: Option<String>,
+ ) -> Result<()> {
+ let mut n = self.find_noun_by_id(id)?;
+
+ if let Some(x) = article {
+ n.articles = x;
+ };
+
+ if let Some(x) = word {
+ n.word = x;
+ };
+
+ if let Some(x) = meaning {
+ n.meaning = x;
+ };
+ if let Some(x) = group {
+ n.group = x;
+ };
+
+ let file = self.words_path().join(format!("{id}.json"));
+
+ if !file.try_exists()? {
+ return Err(eyre!("could not find a word with that id."));
+ }
+
+ let json_content = serde_json::to_string_pretty(&NounFile::from(n))?;
+
+ fs_extra::file::write_all(file, &json_content)?;
+ Ok(())
+ }
}
#[derive(Debug, Clone, Serialize, Deserialize)]