M Cargo.lock => Cargo.lock +19 -20
@@ 306,7 306,7 @@ dependencies = [
[[package]]
name = "der_die_das"
-version = "0.2.3"
+version = "0.2.4"
dependencies = [
"clap",
"clap_complete_command",
@@ 319,7 319,6 @@ dependencies = [
"inquire",
"serde",
"serde_json",
- "thiserror",
"time",
"tracing",
"tracing-error",
@@ 460,9 459,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]]
name = "inquire"
-version = "0.7.4"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe95f33091b9b7b517a5849bce4dce1b550b430fc20d58059fcaa319ed895d8b"
+checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
dependencies = [
"bitflags 2.5.0",
"crossterm 0.25.0",
@@ 505,9 504,9 @@ dependencies = [
[[package]]
name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
@@ 624,9 623,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]]
name = "parking_lot"
-version = "0.12.1"
+version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
dependencies = [
"lock_api",
"parking_lot_core",
@@ 634,15 633,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.5",
]
[[package]]
@@ 713,11 712,11 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.5.0",
]
[[package]]
@@ 801,18 800,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.198"
+version = "1.0.199"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
+checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.198"
+version = "1.0.199"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
+checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
dependencies = [
"proc-macro2",
"quote",
@@ 1068,9 1067,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
-version = "0.1.11"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "utf8parse"
M Cargo.toml => Cargo.toml +4 -2
@@ 1,6 1,6 @@
[package]
name = "der_die_das"
-version = "0.2.3"
+version = "0.2.4"
edition = "2021"
authors = ["Perma Alesheikh <me@prma.dev>"]
license = "EUPL-1.2"
@@ 32,8 32,10 @@ fs_extra = "1.3.0"
inquire = "0.7.4"
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.116"
-thiserror = "1.0.59"
time = { version = "0.3.36", features = ["serde", "local-offset", "serde-well-known"] }
tracing = "0.1.40"
tracing-error = "0.2.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
+
+[lints.clippy]
+pedantic = "warn"
M src/attempt.rs => src/attempt.rs +35 -2
@@ 7,19 7,52 @@ pub struct Attempt {
pub id: i128,
pub at: time::OffsetDateTime,
pub for_word: i128,
- pub what_happened: AttemptResult,
+ pub what_happened: Conclusion,
}
#[derive(Debug, Clone)]
-pub enum AttemptResult {
+pub enum Conclusion {
Success,
WrongArticle(Article),
}
pub trait Repo {
+ /// Saves an attempt
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the IO fails or the item already
+ /// Exists.
fn save_attempt(self, attempt: Attempt) -> Result<()>;
+
+ /// Returns all the attempts
+ ///
+ /// # Errors
+ ///
+ /// When IO failed.
fn all_attempts(self) -> Result<Vec<Attempt>>;
+
+ /// Find an attempt by its ID.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the item was not found or there
+ /// was an IO failure .
fn find_attempt_by_id(self, id: i128) -> Result<Attempt>;
+
+ /// Finds the attempts for an specific noun.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the item was not found or there
+ /// was an IO failure .
fn find_attempt_by_noun_id(self, id: i128) -> Result<Vec<Attempt>>;
+
+ /// Deletes an attempt by its ID.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the item was not found or there
+ /// was an IO failure .
fn delete_attempt_by_id(self, id: i128) -> Result<()>;
}
M src/commands.rs => src/commands.rs +20 -20
@@ 21,7 21,7 @@ use der_die_das::{
};
use deunicode::deunicode;
-use crate::{config::GroupConfig, importer};
+use crate::{config::GroupConfiguration, importer};
pub fn new_noun(
id: i128,
@@ 29,7 29,7 @@ pub fn new_noun(
meaning: String,
article: Vec<Article>,
group: Option<String>,
- storage: Storage,
+ storage: &Storage,
) -> Result<()> {
if word.is_empty() {
return Err(eyre!("word must be at least 1 character"));
@@ 47,7 47,7 @@ pub fn new_noun(
Ok(())
}
-pub fn list_nouns(storage: Storage) -> Result<()> {
+pub fn list_nouns(storage: &Storage) -> Result<()> {
let h = History {
nouns: storage.all_nouns()?,
attempts: storage.all_attempts()?,
@@ 160,10 160,10 @@ fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
.with_validators(&[
Box::new(ValidateShouldBeTwoWords {}),
Box::new(ValidateFirstWordShouldBeArticle {}),
- Box::new(ValidateLastWordShouldMatchTheSpelling(noun.word.to_owned())),
+ Box::new(ValidateLastWordShouldMatchTheSpelling(noun.word.clone())),
])
.prompt()?
- .to_owned();
+ .clone();
let resp: Vec<_> = resp.split_whitespace().collect();
if resp.len() != 2 {
return Err(eyre!("response is more than two words!"));
@@ 181,13 181,22 @@ fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
}?;
let now = time::OffsetDateTime::now_utc();
- if !noun.articles.contains(&article) {
+ if noun.articles.contains(&article) {
+ storage.save_attempt(Attempt {
+ id: now.unix_timestamp_nanos(),
+ at: now,
+ for_word: noun.id,
+ what_happened: der_die_das::attempt::Conclusion::Success,
+ })?;
+ println!("{}", "Correct!".green().bold());
+ Ok(())
+ } else {
println!("{}", "Article did not match!".red());
println!(
"correct article(s) is(are):\t{}",
noun.articles
.iter()
- .map(|s| s.to_string())
+ .map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
.bold()
@@ 197,18 206,9 @@ fn ask_noun(storage: &Storage, noun: &Noun) -> Result<()> {
id: now.unix_timestamp_nanos(),
at: now,
for_word: noun.id,
- what_happened: der_die_das::attempt::AttemptResult::WrongArticle(article),
+ what_happened: der_die_das::attempt::Conclusion::WrongArticle(article),
})?;
ask_noun(storage, noun)
- } else {
- storage.save_attempt(Attempt {
- id: now.unix_timestamp_nanos(),
- at: now,
- for_word: noun.id,
- what_happened: der_die_das::attempt::AttemptResult::Success,
- })?;
- println!("{}", "Correct!".green().bold());
- Ok(())
}
}
@@ 222,7 222,7 @@ 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: GroupConfig) -> Result<()> {
+pub fn ask_next(storage: &Storage, group_config: &GroupConfiguration) -> Result<()> {
let nouns = storage.all_nouns()?;
let attempts = storage.all_attempts()?;
let h = History { nouns, attempts };
@@ 246,7 246,7 @@ pub fn ask_next(storage: &Storage, group_config: GroupConfig) -> Result<()> {
Ok(())
}
-pub fn group_ask(storage: &Storage, group_config: GroupConfig) -> Result<()> {
+pub fn group_ask(storage: &Storage, group_config: &GroupConfiguration) -> Result<()> {
let nouns = storage.all_nouns()?;
let attempts = storage.all_attempts()?;
let h = History { nouns, attempts };
@@ 273,7 273,7 @@ pub fn group_ask(storage: &Storage, group_config: GroupConfig) -> Result<()> {
})
}
-pub fn import_nouns(storage: Storage, at: PathBuf) -> Result<()> {
+pub fn import_nouns(storage: &Storage, at: PathBuf) -> Result<()> {
if !at.try_exists()? {
return Err(eyre!(format!(
"Such file or directory does not exist: {}",
M src/config.rs => src/config.rs +24 -22
@@ 34,12 34,12 @@ impl Default for GroupConfigFile {
serde::Deserialize,
Default,
)]
-pub struct ConfigFile {
+pub struct ConfigurationFile {
pub data_path: Option<PathBuf>,
pub group: Option<GroupConfigFile>,
}
-impl TryFrom<PathBuf> for ConfigFile {
+impl TryFrom<PathBuf> for ConfigurationFile {
type Error = color_eyre::Report;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
@@ 50,7 50,7 @@ impl TryFrom<PathBuf> for ConfigFile {
}
}
-impl TryFrom<&PathBuf> for ConfigFile {
+impl TryFrom<&PathBuf> for ConfigurationFile {
type Error = color_eyre::Report;
fn try_from(value: &PathBuf) -> Result<Self, Self::Error> {
@@ 64,11 64,11 @@ impl TryFrom<&PathBuf> for ConfigFile {
#[derive(
Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
-pub struct GroupConfig {
+pub struct GroupConfiguration {
pub threshold: u8,
pub enable: bool,
}
-impl Default for GroupConfig {
+impl Default for GroupConfiguration {
fn default() -> Self {
Self {
threshold: 100,
@@ 77,7 77,7 @@ impl Default for GroupConfig {
}
}
-impl From<GroupConfigFile> for GroupConfig {
+impl From<GroupConfigFile> for GroupConfiguration {
fn from(value: GroupConfigFile) -> Self {
Self {
threshold: value.threshold.unwrap_or(100),
@@ 91,26 91,28 @@ impl From<GroupConfigFile> for GroupConfig {
)]
pub struct Config {
pub data_path: PathBuf,
- pub group: GroupConfig,
+ pub group: GroupConfiguration,
}
-impl TryFrom<ConfigFile> for Config {
+impl TryFrom<ConfigurationFile> for Config {
type Error = color_eyre::Report;
- fn try_from(value: ConfigFile) -> color_eyre::Result<Self> {
- let data_path = match value.data_path {
- Some(r) => r,
- None => {
- let p = dirs::data_dir()
- .ok_or_eyre("Could not find any folder to put the data in.")?
- .join("ddd");
- if !p.try_exists()? {
- fs_extra::dir::create_all(&p, false)?
- };
- p
- }
- };
+ fn try_from(value: ConfigurationFile) -> color_eyre::Result<Self> {
let group = value.group.unwrap_or_default().into();
- Ok(Config { data_path, group })
+
+ if let Some(data_path) = value.data_path {
+ return Ok(Config { data_path, group });
+ };
+ let p = dirs::data_dir()
+ .ok_or_eyre("Could not find any folder to put the data in.")?
+ .join("ddd");
+ if !p.try_exists()? {
+ fs_extra::dir::create_all(&p, false)?;
+ };
+
+ Ok(Config {
+ group,
+ data_path: p,
+ })
}
}
M src/history.rs => src/history.rs +10 -3
@@ 8,6 8,7 @@ pub struct History {
}
impl History {
+ #[must_use]
pub fn attempt_per_noun(&self) -> Vec<(Noun, Vec<Attempt>)> {
let n: Vec<_> = self
.nouns
@@ 30,16 31,17 @@ impl History {
n
}
+ #[must_use]
pub fn confidence_map(&self) -> Vec<(Noun, u8)> {
self.attempt_per_noun()
.iter()
.map(|s| {
let confidence = s.1.iter().fold(0u8, |confidence, a| match a.what_happened {
- crate::attempt::AttemptResult::Success => match confidence {
+ crate::attempt::Conclusion::Success => match confidence {
u8::MAX => confidence,
_ => confidence + 1,
},
- crate::attempt::AttemptResult::WrongArticle(_) => match confidence {
+ crate::attempt::Conclusion::WrongArticle(_) => match confidence {
u8::MIN => confidence,
_ => confidence - 1,
},
@@ 49,19 51,21 @@ impl History {
.collect()
}
+ #[must_use]
pub fn grouped_confidence_map(&self) -> HashMap<String, Vec<(Noun, u8)>> {
self.confidence_map().into_iter().fold(
HashMap::new(),
|mut acc: HashMap<String, Vec<(Noun, u8)>>, (n, c)| {
match acc.get_mut(&n.group) {
Some(cu) => cu.push((n, c)),
- None => _ = acc.insert(n.group.to_owned(), vec![(n, c)]),
+ None => _ = acc.insert(n.group.clone(), vec![(n, c)]),
};
acc
},
)
}
+ #[must_use]
pub fn next(&self) -> Option<(Noun, u8)> {
let mut sets = self.confidence_map();
@@ 69,6 73,8 @@ impl History {
sets.first().cloned()
}
+
+ #[must_use]
pub fn next_group(&self, threshold: u8) -> Option<Vec<(Noun, u8)>> {
let sets = self.grouped_confidence_map();
if sets.is_empty() {
@@ 89,6 95,7 @@ impl History {
Some(left)
}
+ #[must_use]
pub fn next_with_group(&self, threshold: u8) -> Option<(Noun, u8)> {
let sets = self.grouped_confidence_map();
if sets.is_empty() {
M src/importer.rs => src/importer.rs +2 -2
@@ 5,7 5,7 @@ use der_die_das::{
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
-pub struct Word {
+pub struct WordSet {
articles: Vec<ArticleFileV1>,
word: String,
meaning: String,
@@ 14,7 14,7 @@ pub struct Word {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Group {
name: String,
- words: Vec<Word>,
+ words: Vec<WordSet>,
}
impl From<Group> for Vec<Noun> {
M src/main.rs => src/main.rs +8 -8
@@ 25,7 25,7 @@ fn main() -> Result<()> {
debug!("the config path is at: {:#?}", &config_path);
- let configs_file = config::ConfigFile::try_from(config_path)?;
+ let configs_file = config::ConfigurationFile::try_from(config_path)?;
debug!("the configuration file is: {:#?}", &configs_file);
let configs = config::Config::try_from(configs_file)?;
@@ 52,18 52,18 @@ fn main() -> Result<()> {
meaning,
articles,
group,
- storage,
+ &storage,
),
},
Commands::List(list_commands) => match list_commands {
- arugments::ListCommands::Nouns => commands::list_nouns(storage),
+ arugments::ListCommands::Nouns => commands::list_nouns(&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, &configs.group),
+ arugments::AskCommands::Group => commands::group_ask(&storage, &configs.group),
},
},
None => todo!(),
@@ 88,9 88,9 @@ fn trace_install() -> Result<()> {
.with(ErrorLayer::default());
if current_filter == LevelFilter::OFF {
- registry.with(fmt_layer.without_time()).init()
+ registry.with(fmt_layer.without_time()).init();
} else {
- registry.with(fmt_layer).init()
+ registry.with(fmt_layer).init();
}
if env::var("RUST_BACKTRACE").is_err() && current_filter == LevelFilter::TRACE {
M src/nouns.rs => src/nouns.rs +26 -0
@@ 33,8 33,34 @@ impl Display for Article {
}
pub trait Repo {
+ /// Saves one noun.
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if the noun with the same ID already
+ /// exist or there is an IO failure.
fn save_noun(self, noun: Noun) -> Result<()>;
+
+ /// Returns all the nouns
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if there is an IO Failure.
fn all_nouns(self) -> Result<Vec<Noun>>;
+
+ /// Find noun by specific ID
+ ///
+ /// # Errors
+ ///
+ /// This function will return an error if a noun by that ID does not exist
+ /// or there is an IO failure.
fn find_noun_by_id(self, id: i128) -> Result<Noun>;
+
+ /// Delete the noun by its ID
+ ///
+ /// # Errors
+ ///
+ /// 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<()>;
}
M src/storage.rs => src/storage.rs +10 -10
@@ 7,7 7,7 @@ use color_eyre::{
use serde::{Deserialize, Serialize};
use crate::{
- attempt::{self, Attempt, AttemptResult},
+ attempt::{self, Attempt, Conclusion},
nouns::{self, Article, Noun},
};
@@ 81,7 81,7 @@ impl nouns::Repo for &Storage {
fn all_nouns(self) -> Result<Vec<Noun>> {
Ok(std::fs::read_dir(self.words_path())?
- .filter_map(|e| e.ok())
+ .filter_map(std::result::Result::ok)
.filter_map(|e| {
let at = e.path();
match at.extension().map(|ext| ext.to_str()) {
@@ 224,11 224,11 @@ impl From<AttemptFile> for Attempt {
}
}
-impl From<AttemptResultFileV1> for AttemptResult {
+impl From<AttemptResultFileV1> for Conclusion {
fn from(ar: AttemptResultFileV1) -> Self {
match ar {
- AttemptResultFileV1::Success => AttemptResult::Success,
- AttemptResultFileV1::WrongArticle(art) => AttemptResult::WrongArticle(art.into()),
+ AttemptResultFileV1::Success => Conclusion::Success,
+ AttemptResultFileV1::WrongArticle(art) => Conclusion::WrongArticle(art.into()),
}
}
}
@@ 244,11 244,11 @@ impl From<Attempt> for AttemptFile {
}
}
-impl From<AttemptResult> for AttemptResultFileV1 {
- fn from(ar: AttemptResult) -> Self {
+impl From<Conclusion> for AttemptResultFileV1 {
+ fn from(ar: Conclusion) -> Self {
match ar {
- AttemptResult::Success => AttemptResultFileV1::Success,
- AttemptResult::WrongArticle(art) => AttemptResultFileV1::WrongArticle(art.into()),
+ Conclusion::Success => AttemptResultFileV1::Success,
+ Conclusion::WrongArticle(art) => AttemptResultFileV1::WrongArticle(art.into()),
}
}
}
@@ 272,7 272,7 @@ impl attempt::Repo for &Storage {
fn all_attempts(self) -> Result<Vec<Attempt>> {
Ok(std::fs::read_dir(self.attempts_path())?
- .filter_map(|e| e.ok())
+ .filter_map(std::result::Result::ok)
.filter_map(|e| {
let at = e.path();
match at.extension().map(|ext| ext.to_str()) {