From 8de88b5c7c41bc16a8f6979b69bbd94b7fb9b5cd Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Wed, 14 Jan 2026 10:06:22 +0100 Subject: Migrate from usize to isize for integers in models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SQLite3, like many other DBs (e.g. PostgreSQL), assumes by default an i64::MAX value. Hence, while in the application we are talking mainly in unsigned terms (which is what makes sense here), this is not realistic to what ends up happening on the DB side. Up until now this just worked behind the scenes because rustqlite did the heavy lifting via default trait implementations of usize which handled this translation for us. This default implementation for usize was dropped in 0.38.0 (see [1]). Even if it can be enabled back via the "fallible_uint" feature, it's more fair to stick to isize, as it's not much of a problem on our side, and it's more transparent in regards to what happens in the end in the DB. Last but not least, this commit also upgrades rustqlite to the latest 0.38.0, with the rest of the dependency tree. [1] https://github.com/rusqlite/rusqlite/issues/1722) Signed-off-by: Miquel Sabaté Solà --- lib/mihi/Cargo.toml | 2 +- lib/mihi/src/lib.rs | 89 +++++++++++++++++++++++++++++------------------------ 2 files changed, 49 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/mihi/Cargo.toml b/lib/mihi/Cargo.toml index 4b973d6..15ed1fb 100644 --- a/lib/mihi/Cargo.toml +++ b/lib/mihi/Cargo.toml @@ -8,6 +8,6 @@ edition.workspace = true license.workspace = true [dependencies] -rusqlite = { version = "0.35.0", features = ["bundled"] } +rusqlite = { version = "0.38.0", features = ["bundled"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/lib/mihi/src/lib.rs b/lib/mihi/src/lib.rs index 5c15d8c..fc73189 100644 --- a/lib/mihi/src/lib.rs +++ b/lib/mihi/src/lib.rs @@ -40,10 +40,10 @@ impl std::fmt::Display for Category { } } -impl TryFrom for Category { +impl TryFrom for Category { type Error = &'static str; - fn try_from(value: usize) -> Result { + fn try_from(value: isize) -> Result { match value { 0 => Ok(Self::Unknown), 1 => Ok(Self::Noun), @@ -83,10 +83,10 @@ impl Gender { } } -impl TryFrom for Gender { +impl TryFrom for Gender { type Error = &'static str; - fn try_from(value: usize) -> Result { + fn try_from(value: isize) -> Result { match value { 0 => Ok(Self::Masculine), 1 => Ok(Self::Feminine), @@ -117,10 +117,10 @@ pub enum Language { Latin, } -impl TryFrom for Language { +impl TryFrom for Language { type Error = &'static str; - fn try_from(value: usize) -> Result { + fn try_from(value: isize) -> Result { match value { 0 => Ok(Self::Unknown), 1 => Ok(Self::Latin), @@ -206,8 +206,8 @@ pub struct Word { pub enunciated: String, pub particle: String, pub language: Language, - pub declension_id: Option, - pub conjugation_id: Option, + pub declension_id: Option, + pub conjugation_id: Option, pub kind: String, pub category: Category, pub regular: bool, @@ -216,17 +216,17 @@ pub struct Word { pub suffix: Option, pub translation: Value, pub flags: Value, - pub succeeded: usize, - pub steps: usize, - pub weight: usize, + pub succeeded: isize, + pub steps: isize, + pub weight: isize, } impl Word { pub fn from( particle: String, category: Category, - declension_id: Option, - conjugation_id: Option, + declension_id: Option, + conjugation_id: Option, gender: Gender, kind: String, ) -> Word { @@ -251,7 +251,7 @@ impl Word { } } - pub fn inflection_id(&self) -> Option { + pub fn inflection_id(&self) -> Option { if matches!(self.category, Category::Verb) { return Some(self.conjugation_id.unwrap()); } @@ -341,7 +341,7 @@ pub fn create_word(word: Word) -> Result<(), String> { match word.category { Category::Noun => match word.declension_id { Some(id @ 1..7) => { - if !DECLENSIONS_WITH_KINDS[id - 1].contains(&word.kind.as_str()) { + if !DECLENSIONS_WITH_KINDS[id as usize - 1].contains(&word.kind.as_str()) { return Err(format!("bad kind for declension '{id}'")); } } @@ -354,7 +354,7 @@ pub fn create_word(word: Word) -> Result<(), String> { }, Category::Adjective => match word.declension_id { Some(id @ (1 | 3)) => { - if !ADJECTIVE_KINDS[id - 1].contains(&word.kind.as_str()) { + if !ADJECTIVE_KINDS[id as usize - 1].contains(&word.kind.as_str()) { return Err(format!("bad kind for declension '{id}'")); } } @@ -410,14 +410,14 @@ pub fn create_word(word: Word) -> Result<(), String> { params![ word.enunciated.trim(), word.particle.trim(), - word.language as usize, + word.language as isize, word.declension_id, word.conjugation_id, word.kind.trim(), - word.category as usize, + word.category as isize, word.regular, word.locative, - word.gender as usize, + word.gender as isize, word.suffix, serde_json::to_string(&word.flags).unwrap(), serde_json::to_string(&word.translation).unwrap(), @@ -451,10 +451,10 @@ pub fn update_word(word: Word) -> Result<(), String> { word.declension_id, word.conjugation_id, word.kind, - word.category as usize, + word.category as isize, word.regular, word.locative, - word.gender as usize, + word.gender as isize, word.suffix, serde_json::to_string(&word.flags).unwrap(), serde_json::to_string(&word.translation).unwrap(), @@ -514,14 +514,14 @@ pub fn find_by(enunciated: &str) -> Result { id: row.get(0).unwrap(), enunciated: row.get(1).unwrap(), particle: row.get(2).unwrap(), - language: row.get::(3).unwrap().try_into()?, + language: row.get::(3).unwrap().try_into()?, declension_id: row.get(4).unwrap(), conjugation_id: row.get(5).unwrap(), kind: row.get(6).unwrap(), - category: row.get::(7).unwrap().try_into()?, + category: row.get::(7).unwrap().try_into()?, regular: row.get(8).unwrap(), locative: row.get(9).unwrap(), - gender: row.get::(10).unwrap().try_into()?, + gender: row.get::(10).unwrap().try_into()?, suffix: row.get(11).unwrap(), translation: serde_json::from_str(&row.get::(12).unwrap()).unwrap(), succeeded: row.get(13).unwrap(), @@ -556,7 +556,7 @@ fn flags_clause(flags: &Vec) -> String { pub fn select_relevant_words( category: Category, flags: &Vec, - number: usize, + number: isize, ) -> Result, String> { let conn = get_connection()?; let mut stmt = conn @@ -574,7 +574,7 @@ pub fn select_relevant_words( .as_str(), ) .unwrap(); - let mut it = stmt.query([category as usize, number]).unwrap(); + let mut it = stmt.query([category as isize, number]).unwrap(); let mut res = vec![]; while let Some(row) = it.next().unwrap() { @@ -582,14 +582,14 @@ pub fn select_relevant_words( id: row.get(0).unwrap(), enunciated: row.get(1).unwrap(), particle: row.get(2).unwrap(), - language: row.get::(3).unwrap().try_into()?, + language: row.get::(3).unwrap().try_into()?, declension_id: row.get(4).unwrap(), conjugation_id: row.get(5).unwrap(), kind: row.get(6).unwrap(), - category: row.get::(7).unwrap().try_into()?, + category: row.get::(7).unwrap().try_into()?, regular: row.get(8).unwrap(), locative: row.get(9).unwrap(), - gender: row.get::(10).unwrap().try_into()?, + gender: row.get::(10).unwrap().try_into()?, suffix: row.get(11).unwrap(), translation: serde_json::from_str(&row.get::(12).unwrap()).unwrap(), succeeded: row.get(13).unwrap(), @@ -601,7 +601,7 @@ pub fn select_relevant_words( Ok(res) } -pub fn update_success(word: &Word, success: usize, steps: usize) -> Result<(), String> { +pub fn update_success(word: &Word, success: isize, steps: isize) -> Result<(), String> { let conn = get_connection()?; match conn.execute( @@ -659,10 +659,10 @@ impl std::fmt::Display for ExerciseKind { } } -impl TryFrom for ExerciseKind { +impl TryFrom for ExerciseKind { type Error = &'static str; - fn try_from(value: usize) -> Result { + fn try_from(value: isize) -> Result { match value { 0 => Ok(Self::Pensum), 1 => Ok(Self::Translation), @@ -709,7 +709,7 @@ pub fn create_exercise(exercise: Exercise) -> Result<(), String> { exercise.enunciate, exercise.solution, exercise.lessons, - exercise.kind as usize, + exercise.kind as isize, ], ) { Ok(_) => Ok(()), @@ -765,7 +765,7 @@ pub fn find_exercise_by_title(title: &str) -> Result { enunciate: row.get(2).unwrap(), solution: row.get(3).unwrap(), lessons: row.get(4).unwrap(), - kind: row.get::(5).unwrap().try_into()?, + kind: row.get::(5).unwrap().try_into()?, }), None => Err("no exercises were found with this title".to_string()), }, @@ -791,7 +791,7 @@ pub fn update_exercise(exercise: Exercise) -> Result<(), String> { exercise.enunciate, exercise.solution, exercise.lessons, - exercise.kind as usize, + exercise.kind as isize, ], ) { Ok(_) => Ok(()), @@ -814,7 +814,7 @@ pub fn delete_exercise(title: &str) -> Result<(), String> { // by `kind`. pub fn select_relevant_exercises( kind: Option, - limit: usize, + limit: isize, ) -> Result, String> { let conn = get_connection()?; @@ -830,7 +830,7 @@ pub fn select_relevant_exercises( LIMIT ?2", ) .unwrap(); - stmt.query([kind as usize, limit]).unwrap() + stmt.query([kind as isize, limit]).unwrap() } None => { stmt = conn @@ -853,7 +853,7 @@ pub fn select_relevant_exercises( enunciate: row.get(2).unwrap(), solution: row.get(3).unwrap(), lessons: row.get(4).unwrap(), - kind: row.get::(5).unwrap().try_into()?, + kind: row.get::(5).unwrap().try_into()?, }); } Ok(res) @@ -1093,17 +1093,24 @@ pub fn group_declension_inflections( while let Some(row) = it.next().unwrap() { // Fetch the number and account for defectives on number. - let number: usize = row.get(1).unwrap(); + let number_i: isize = row.get(1).unwrap(); + let number: usize = usize::try_from(number_i).expect("not expecting a negative number"); if (number == 0 && word.is_flag_set("onlyplural")) || (number == 1 && word.is_flag_set("onlysingular")) { continue; } - let case = row.get(3).unwrap(); + let case_i: isize = row.get(3).unwrap(); let term: String = row.get(4).unwrap(); - table.add(word, case, number, gender, &term); + table.add( + word, + usize::try_from(case_i).expect("not expecting a negative number"), + number, + gender, + &term, + ); } if let Some(sets) = word.flags.get("sets") { -- cgit v1.2.3