From b86a375b1cfa7b2220cf57f4732607fa86fe2c71 Mon Sep 17 00:00:00 2001 From: chodak166 Date: Sat, 13 Dec 2025 11:49:33 +0100 Subject: [PATCH] Added PL major system rules --- lib/src/application/errors.rs | 38 --- lib/src/application/services.rs | 2 +- lib/src/application/traits.rs | 46 ---- lib/src/core/entities.rs | 9 + lib/src/core/errors.rs | 24 +- lib/src/core/sys_major/rules_pl.rs | 219 +++++++++++++++++- lib/src/core/traits.rs | 41 +++- .../infrastructure/json_file_dict_source.rs | 2 +- .../infrastructure/sqlite_dict_repository.rs | 8 +- .../presentation/cli/commands/import_dict.rs | 2 +- 10 files changed, 270 insertions(+), 121 deletions(-) diff --git a/lib/src/application/errors.rs b/lib/src/application/errors.rs index 3f20cdc..8b13789 100644 --- a/lib/src/application/errors.rs +++ b/lib/src/application/errors.rs @@ -1,39 +1 @@ -// #[derive(Debug)] -// pub enum RepositoryError { -// NotFound, -// ConnectionFailed, -// InvalidData(String), -// Unexpected(String), -// } -// impl std::fmt::Display for RepositoryError { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "{:?}", self) -// } -// } - -// impl std::error::Error for RepositoryError {} - -use thiserror::Error; - -// #[derive(Error, Debug)] -// pub enum RepositoryError { -// #[error("Database connection failed")] -// ConnectionFailed(#[source] sqlx::Error), -// #[error("Database query failed: {0}")] -// QueryFailed(#[source] sqlx::Error), //TODO: sqlx id infrastructure -// #[error("Dictionary '{0}' not found")] -// NotFound(String), -// #[error("Invalid data encountered")] -// InvalidData, -// } - -#[derive(Error, Debug)] -pub enum RepositoryError { - #[error("Database connection failed")] - ConnectionFailed, - #[error("Dictionary '{0}' not found")] - NotFound(String), - #[error("Storage error: {0}")] - StorageError(String), -} diff --git a/lib/src/application/services.rs b/lib/src/application/services.rs index f3e51d7..2085fe2 100644 --- a/lib/src/application/services.rs +++ b/lib/src/application/services.rs @@ -1,4 +1,4 @@ -use crate::application::traits::{DictRepository, DictSource}; +use crate::core::traits::{DictRepository, DictSource}; pub struct DictImporter<'a, R> { repo: &'a R, diff --git a/lib/src/application/traits.rs b/lib/src/application/traits.rs index db8d186..8b13789 100644 --- a/lib/src/application/traits.rs +++ b/lib/src/application/traits.rs @@ -1,47 +1 @@ -use crate::{ - application::errors::RepositoryError, - core::entities::{Dict, DictEntry}, -}; -// pub trait DictRepository { -// fn create(&self, name: &str) -> Result<(), RepositoryError>; -// // Batch saving is usually much faster than 1-by-1 for SQL -// fn save_entries(&self, dict_name: &str, entries: &[DictEntry]) -> Result<(), RepositoryError>; - -// fn fetch_many( -// &self, -// name: &str, -// limit: Option, -// offset: Option, -// ) -> Result; - -// // Get the next available ID for a dictionary -// fn get_next_id(&self, dict_name: &str) -> Result; -// } - -#[async_trait::async_trait] -pub trait DictRepository: Send + Sync { - async fn create(&self, name: &str) -> Result<(), RepositoryError>; - - /// "Upsert" logic: - /// - If entry exists (by text), update metadata. - /// - If not, insert new. - /// - IDs are handled by the Database. - async fn save_entries( - &self, - dict_name: &str, - entries: &[DictEntry], - ) -> Result<(), RepositoryError>; - - /// Fetch a page of entries. - async fn fetch_many( - &self, - name: &str, - limit: Option, - offset: Option, - ) -> Result; -} - -pub trait DictSource { - fn next_entry(&mut self) -> Option>; -} diff --git a/lib/src/core/entities.rs b/lib/src/core/entities.rs index 3a96f78..3e67db4 100644 --- a/lib/src/core/entities.rs +++ b/lib/src/core/entities.rs @@ -36,3 +36,12 @@ impl Dict { self.entries.insert(entry.id.unwrap(), entry); } } + +// pub struct DecodedItem { +// pub value: String, +// } + +// pub struct DecodedResult { +// pub input: String, +// pub output: Vec, +// } diff --git a/lib/src/core/errors.rs b/lib/src/core/errors.rs index 758a094..573a66f 100644 --- a/lib/src/core/errors.rs +++ b/lib/src/core/errors.rs @@ -1,15 +1,11 @@ -// use std::fmt; +use thiserror::Error; -// #[derive(Debug)] -// pub enum RepositoryError { -// NotFound, -// ConnectionFailed, -// InvalidData(String), -// Unexpected(String), -// } - -// impl fmt::Display for RepositoryError { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// write!(f, "{:?}", self) -// } -// } +#[derive(Error, Debug)] +pub enum RepositoryError { + #[error("Data source connection failed")] + ConnectionFailed, + #[error("Dictionary '{0}' not found")] + NotFound(String), + #[error("Storage error: {0}")] + StorageError(String), +} diff --git a/lib/src/core/sys_major/rules_pl.rs b/lib/src/core/sys_major/rules_pl.rs index ac6054a..51b6de0 100644 --- a/lib/src/core/sys_major/rules_pl.rs +++ b/lib/src/core/sys_major/rules_pl.rs @@ -3,13 +3,220 @@ use super::encoder::{Rule, Rules}; pub fn get_rules() -> Rules { vec![ Rule { - phoneme_in: "PL".to_string(), + not_after: vec![], + only_after: vec![], + phoneme_in: "S".to_string(), + phoneme_out: "0".to_string(), + not_before: vec!["I".to_string(), "Z".to_string()], + only_before: vec![], + }, + Rule { + not_after: vec![ + "C".to_string(), + "D".to_string(), + "R".to_string(), + "S".to_string(), + ], + only_after: vec![], + phoneme_in: "Z".to_string(), + phoneme_out: "0".to_string(), + not_before: vec!["I".to_string()], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "T".to_string(), + phoneme_out: "1".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + only_after: vec![], + not_after: vec![], + phoneme_in: "D".to_string(), + phoneme_out: "1".to_string(), + not_before: vec!["Z".to_string(), "Ź".to_string(), "Ż".to_string()], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "N".to_string(), phoneme_out: "2".to_string(), - not_after: vec!["Y".to_string()], - not_before: vec!["X".to_string()], - only_after: vec!["A".to_string()], - only_before: vec!["C".to_string()], + not_before: vec!["I".to_string()], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "M".to_string(), + phoneme_out: "3".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "R".to_string(), + phoneme_out: "4".to_string(), + not_before: vec!["Z".to_string()], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "L".to_string(), + phoneme_out: "5".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "J".to_string(), + phoneme_out: "6".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "K".to_string(), + phoneme_out: "7".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "G".to_string(), + phoneme_out: "7".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "F".to_string(), + phoneme_out: "8".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "W".to_string(), + phoneme_out: "8".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "P".to_string(), + phoneme_out: "9".to_string(), + not_before: vec![], + only_before: vec![], + }, + Rule { + not_after: vec![], + only_after: vec![], + phoneme_in: "B".to_string(), + phoneme_out: "9".to_string(), + not_before: vec![], + only_before: vec![], }, - // ...more entries... ] } + +#[cfg(test)] +mod tests { + use super::*; + use crate::SystemEncoder; + use crate::core::sys_major::Encoder; + + #[test] + fn test_major_dict_pl_encode_0_1() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("SZSCZ"); + assert_eq!(output, "0") + } + + #[test] + fn test_major_dict_pl_encode_0_2() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("SZSICZ"); + assert_eq!(output, "") + } + + #[test] + fn test_major_dict_pl_encode_0_3() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("SZCZRZZCZDZSZ"); + assert_eq!(output, "0") + } + + #[test] + fn test_major_dict_pl_encode_0_4() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("SZCZRZZICZDZSZ"); + assert_eq!(output, "") + } + + #[test] + fn test_major_dict_pl_encode_1_1() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("SZTCZ"); + assert_eq!(output, "1") + } + + #[test] + fn test_major_dict_pl_encode_1_2() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("DZDŻDŹDDZDŻDŹ"); + assert_eq!(output, "1") + } + + #[test] + fn test_major_dict_pl_encode_1_3() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("DZDŻDŹDZDZDŻDŹ"); + assert_eq!(output, "") + } + + #[test] + fn test_major_dict_pl_encode_2_1() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("NINNI"); + assert_eq!(output, "2") + } + + #[test] + fn test_major_dict_pl_encode_2_2() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("NININI"); + assert_eq!(output, "") + } + + #[test] + fn test_major_dict_pl_encode_4_1() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("RZRRZ"); + assert_eq!(output, "4") + } + + #[test] + fn test_major_dict_pl_encode_4_2() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("RZRZRZ"); + assert_eq!(output, "") + } + + #[test] + fn test_major_dict_pl_encode_full_1() { + let encoder = Encoder::new(get_rules()); + let output = encoder.encode("ATADANAMARALAJAKAGAFAWAPABA"); + assert_eq!(output, "1123456778899") + } +} diff --git a/lib/src/core/traits.rs b/lib/src/core/traits.rs index 8753b86..7943be4 100644 --- a/lib/src/core/traits.rs +++ b/lib/src/core/traits.rs @@ -1,15 +1,38 @@ +use super::entities::{Dict, DictEntry}; +use super::errors::RepositoryError; + pub trait SystemEncoder { fn encode(&self, word: &str) -> String; } -// pub trait WordRepository { -// fn save(word: &WordEntry) -> Result; -// fn save_many(words: &Vec) -> Result<(), RepositoryError>; -// fn fetch(id: WordEntryId) -> Result; -// fn fetch_many(ids: &Vec) -> Result, RepositoryError>; -// } +// pub trait SystenDecoder { +// fn initialize(&self) -> Result<(), anyhow::Error>; -// pub trait DictRepository { -// fn save(dict: &Dict) -> Result<(), RepositoryError>; -// fn fetch(name: &str) -> Result; // } + +#[async_trait::async_trait] +pub trait DictRepository: Send + Sync { + async fn create(&self, name: &str) -> Result<(), RepositoryError>; + + /// "Upsert" logic: + /// - If entry exists (by text), update metadata. + /// - If not, insert new. + /// - IDs are handled by the Database. + async fn save_entries( + &self, + dict_name: &str, + entries: &[DictEntry], + ) -> Result<(), RepositoryError>; + + /// Fetch a page of entries. + async fn fetch_many( + &self, + name: &str, + limit: Option, + offset: Option, + ) -> Result; +} + +pub trait DictSource { + fn next_entry(&mut self) -> Option>; +} diff --git a/lib/src/infrastructure/json_file_dict_source.rs b/lib/src/infrastructure/json_file_dict_source.rs index e392d4f..760fb94 100644 --- a/lib/src/infrastructure/json_file_dict_source.rs +++ b/lib/src/infrastructure/json_file_dict_source.rs @@ -1,5 +1,5 @@ -use crate::application::traits::DictSource; use crate::core::entities::DictEntry; +use crate::core::traits::DictSource; use serde::Deserialize; use std::collections::HashMap; use std::fs::File; diff --git a/lib/src/infrastructure/sqlite_dict_repository.rs b/lib/src/infrastructure/sqlite_dict_repository.rs index b8bdb17..aef5ea5 100644 --- a/lib/src/infrastructure/sqlite_dict_repository.rs +++ b/lib/src/infrastructure/sqlite_dict_repository.rs @@ -1,12 +1,10 @@ -use crate::application::errors::RepositoryError; -use crate::application::traits::DictRepository; -use crate::core::entities::{Dict, DictEntry, DictEntryId}; +use crate::core::entities::{Dict, DictEntry}; +use crate::core::errors::RepositoryError; +use crate::core::traits::DictRepository; use sqlx::{Row, SqlitePool, sqlite::SqliteConnectOptions}; use std::collections::HashMap; use std::str::FromStr; -// --- DTO: Data Transfer Object --- -// This struct exists ONLY to talk to the database. #[derive(sqlx::FromRow)] struct SqliteEntryDto { id: i64, diff --git a/lib/src/presentation/cli/commands/import_dict.rs b/lib/src/presentation/cli/commands/import_dict.rs index 0f7e693..f5fb519 100644 --- a/lib/src/presentation/cli/commands/import_dict.rs +++ b/lib/src/presentation/cli/commands/import_dict.rs @@ -1,5 +1,5 @@ -use crate::application::traits::DictRepository; use crate::application::{config::ImportDictConfig, services::DictImporter}; +use crate::core::traits::DictRepository; use crate::infrastructure::json_file_dict_source::JsonFileDictSource; use tracing::{debug, error, info};