use std::{
any::Any,
borrow::Borrow,
cmp::Ordering,
error::Error,
fmt::{Debug, Display},
path::Path,
};
pub use self::layered::Layered;
pub use self::loader::{
AssetLoader, DEFAULT_DICT_NAMES, LoadDictionaryError, SingleDictionaryLoader,
UserDictionaryManager,
};
#[cfg(feature = "sqlite")]
pub use self::sqlite::{SqliteDictionary, SqliteDictionaryBuilder, SqliteDictionaryError};
pub use self::trie::{Trie, TrieBuilder, TrieOpenOptions, TrieStatistics};
pub use self::trie_buf::TrieBuf;
pub use self::usage::DictionaryUsage;
use crate::exn::Exn;
use crate::zhuyin::Syllable;
mod layered;
mod loader;
#[cfg(feature = "sqlite")]
mod sqlite;
mod trie;
mod trie_buf;
mod uhash;
mod usage;
#[derive(Debug, Clone, Default)]
pub struct DictionaryInfo {
pub name: String,
pub copyright: String,
pub license: String,
pub version: String,
pub software: String,
pub usage: DictionaryUsage,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Phrase {
text: Box<str>,
freq: u32,
last_used: Option<u64>,
}
impl Phrase {
pub fn new<S>(phrase: S, freq: u32) -> Phrase
where
S: Into<Box<str>>,
{
Phrase {
text: phrase.into(),
freq,
last_used: None,
}
}
pub fn with_time(mut self, last_used: u64) -> Phrase {
self.last_used = Some(last_used);
self
}
pub fn freq(&self) -> u32 {
self.freq
}
pub fn last_used(&self) -> Option<u64> {
self.last_used
}
pub fn as_str(&self) -> &str {
self.text.borrow()
}
}
impl PartialOrd for Phrase {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Phrase {
fn cmp(&self, other: &Self) -> Ordering {
match self.freq.cmp(&other.freq) {
Ordering::Equal => {}
ord => return ord,
}
self.text.cmp(&other.text)
}
}
impl AsRef<str> for Phrase {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<Phrase> for String {
fn from(phrase: Phrase) -> Self {
phrase.text.into_string()
}
}
impl From<Phrase> for Box<str> {
fn from(phrase: Phrase) -> Self {
phrase.text
}
}
impl From<Phrase> for (String, u32) {
fn from(phrase: Phrase) -> Self {
(phrase.text.into_string(), phrase.freq)
}
}
impl<S> From<(S, u32)> for Phrase
where
S: Into<Box<str>>,
{
fn from(tuple: (S, u32)) -> Self {
Phrase::new(tuple.0, tuple.1)
}
}
impl<S> From<(S, u32, u64)> for Phrase
where
S: Into<Box<str>>,
{
fn from(tuple: (S, u32, u64)) -> Self {
Phrase::new(tuple.0, tuple.1).with_time(tuple.2)
}
}
impl Display for Phrase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub type Phrases<'a> = Box<dyn Iterator<Item = Phrase> + 'a>;
pub type Entries<'a> = Box<dyn Iterator<Item = (Vec<Syllable>, Phrase)> + 'a>;
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum LookupStrategy {
#[default]
Standard,
FuzzyPartialPrefix,
}
pub trait Dictionary: Debug {
fn lookup(&self, syllables: &[Syllable], strategy: LookupStrategy) -> Vec<Phrase>;
fn entries(&self) -> Entries<'_>;
fn about(&self) -> DictionaryInfo;
fn path(&self) -> Option<&Path>;
fn set_usage(&mut self, usage: DictionaryUsage);
fn reopen(&mut self) -> Result<(), UpdateDictionaryError> {
Err(UpdateDictionaryError::new("unimplemented"))
}
fn flush(&mut self) -> Result<(), UpdateDictionaryError> {
Err(UpdateDictionaryError::new("unimplemented"))
}
fn add_phrase(
&mut self,
_syllables: &[Syllable],
_phrase: Phrase,
) -> Result<(), UpdateDictionaryError> {
Err(UpdateDictionaryError::new("unimplemented"))
}
fn update_phrase(
&mut self,
_syllables: &[Syllable],
_phrase: Phrase,
_user_freq: u32,
_time: u64,
) -> Result<(), UpdateDictionaryError> {
Err(UpdateDictionaryError::new("unimplemented"))
}
fn remove_phrase(
&mut self,
_syllables: &[Syllable],
_phrase_str: &str,
) -> Result<(), UpdateDictionaryError> {
Err(UpdateDictionaryError::new("unimplemented"))
}
}
pub trait DictionaryBuilder: Any {
fn set_info(&mut self, info: DictionaryInfo) -> Result<(), BuildDictionaryError>;
fn insert(
&mut self,
syllables: &[Syllable],
phrase: Phrase,
) -> Result<(), BuildDictionaryError>;
fn build(&mut self, path: &Path) -> Result<(), BuildDictionaryError>;
}
#[derive(Debug)]
pub struct UpdateDictionaryError {
message: &'static str,
source: Option<Box<dyn Error + Send + Sync>>,
}
impl UpdateDictionaryError {
pub(crate) fn new(message: &'static str) -> UpdateDictionaryError {
UpdateDictionaryError {
message,
source: None,
}
}
}
impl Display for UpdateDictionaryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "update dictionary failed: {}", self.message)
}
}
impl_exn!(UpdateDictionaryError);
#[derive(Debug)]
pub struct BuildDictionaryError {
msg: String,
source: Option<Box<dyn Error + Send + Sync + 'static>>,
}
impl BuildDictionaryError {
fn new(msg: &str) -> BuildDictionaryError {
BuildDictionaryError {
msg: msg.to_string(),
source: None,
}
}
}
impl Display for BuildDictionaryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "build dictionary error: {}", self.msg)
}
}
impl_exn!(BuildDictionaryError);
#[cfg(test)]
mod tests {
use crate::dictionary::{Dictionary, DictionaryBuilder};
#[test]
fn ensure_object_safe() {
const _: Option<&dyn Dictionary> = None;
const _: Option<&dyn DictionaryBuilder> = None;
}
}