#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate alloc;
mod snowball;
use alloc::borrow::Cow;
use alloc::string::String;
use core::fmt;
use core::str::FromStr;
use snowball::algorithms;
use snowball::SnowballEnv;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Algorithm {
Arabic,
Czech,
Danish,
Dutch,
DutchPorter,
English,
Finnish,
French,
German,
Greek,
Hungarian,
Italian,
Norwegian,
Polish,
Portuguese,
Romanian,
Russian,
Spanish,
Swedish,
Tamil,
Turkish,
}
impl Algorithm {
const ALL: [Algorithm; 21] = [
Algorithm::Arabic,
Algorithm::Czech,
Algorithm::Danish,
Algorithm::Dutch,
Algorithm::DutchPorter,
Algorithm::English,
Algorithm::Finnish,
Algorithm::French,
Algorithm::German,
Algorithm::Greek,
Algorithm::Hungarian,
Algorithm::Italian,
Algorithm::Norwegian,
Algorithm::Polish,
Algorithm::Portuguese,
Algorithm::Romanian,
Algorithm::Russian,
Algorithm::Spanish,
Algorithm::Swedish,
Algorithm::Tamil,
Algorithm::Turkish,
];
const fn name(self) -> &'static str {
match self {
Algorithm::Arabic => "arabic",
Algorithm::Czech => "czech",
Algorithm::Danish => "danish",
Algorithm::Dutch => "dutch",
Algorithm::DutchPorter => "dutch_porter",
Algorithm::English => "english",
Algorithm::Finnish => "finnish",
Algorithm::French => "french",
Algorithm::German => "german",
Algorithm::Greek => "greek",
Algorithm::Hungarian => "hungarian",
Algorithm::Italian => "italian",
Algorithm::Norwegian => "norwegian",
Algorithm::Polish => "polish",
Algorithm::Portuguese => "portuguese",
Algorithm::Romanian => "romanian",
Algorithm::Russian => "russian",
Algorithm::Spanish => "spanish",
Algorithm::Swedish => "swedish",
Algorithm::Tamil => "tamil",
Algorithm::Turkish => "turkish",
}
}
fn program(self) -> fn(&mut SnowballEnv) -> bool {
match self {
Algorithm::Arabic => algorithms::arabic::stem,
Algorithm::Czech => algorithms::czech::stem,
Algorithm::Danish => algorithms::danish::stem,
Algorithm::Dutch => algorithms::dutch::stem,
Algorithm::DutchPorter => algorithms::dutch_porter::stem,
Algorithm::English => algorithms::english::stem,
Algorithm::Finnish => algorithms::finnish::stem,
Algorithm::French => algorithms::french::stem,
Algorithm::German => algorithms::german::stem,
Algorithm::Greek => algorithms::greek::stem,
Algorithm::Hungarian => algorithms::hungarian::stem,
Algorithm::Italian => algorithms::italian::stem,
Algorithm::Norwegian => algorithms::norwegian::stem,
Algorithm::Polish => algorithms::polish::stem,
Algorithm::Portuguese => algorithms::portuguese::stem,
Algorithm::Romanian => algorithms::romanian::stem,
Algorithm::Russian => algorithms::russian::stem,
Algorithm::Spanish => algorithms::spanish::stem,
Algorithm::Swedish => algorithms::swedish::stem,
Algorithm::Tamil => algorithms::tamil::stem,
Algorithm::Turkish => algorithms::turkish::stem,
}
}
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownAlgorithm {
_private: (),
}
impl fmt::Display for UnknownAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("unknown stemming algorithm")
}
}
impl FromStr for Algorithm {
type Err = UnknownAlgorithm;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Algorithm::ALL
.iter()
.copied()
.find(|a| a.name() == s)
.ok_or(UnknownAlgorithm { _private: () })
}
}
#[derive(Clone, Copy)]
pub struct Stemmer {
algorithm: Algorithm,
program: fn(&mut SnowballEnv) -> bool,
}
impl Stemmer {
pub fn create(algorithm: Algorithm) -> Stemmer {
Stemmer {
algorithm,
program: algorithm.program(),
}
}
pub fn stem<'a>(&self, input: &'a str) -> Cow<'a, str> {
SnowballEnv::run(self.program, input)
}
pub fn stem_into(&self, input: &str, out: &mut String) {
SnowballEnv::run_into(self.program, input, out)
}
}
impl fmt::Debug for Stemmer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Stemmer")
.field("algorithm", &self.algorithm)
.finish()
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::Algorithm;
use core::fmt;
use serde::de::{Deserialize, Deserializer, Error, Unexpected, Visitor};
use serde::ser::{Serialize, Serializer};
impl Serialize for Algorithm {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.name())
}
}
struct NameVisitor;
impl<'de> Visitor<'de> for NameVisitor {
type Value = Algorithm;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a lowercase stemming algorithm name")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Algorithm, E> {
v.parse()
.map_err(|_| E::invalid_value(Unexpected::Str(v), &self))
}
}
impl<'de> Deserialize<'de> for Algorithm {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(NameVisitor)
}
}
}