use std::cmp::Ordering;
use std::fmt::Debug;
use std::{fmt, ops};
use thin_vec::ThinVec;
use crate::child_storage::ChildStorage;
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use unicode_segmentation::UnicodeSegmentation;
type WordEnd<D> = Option<ThinVec<D>>;
pub(crate) struct RemoveData<D> {
must_keep: bool,
pub(crate) data: WordEnd<D>,
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub struct TrieDataNode<D> {
#[cfg_attr(feature = "serde", serde(rename = "c"))]
pub(crate) children: ChildStorage<TrieDataNode<D>>,
#[cfg_attr(feature = "serde", serde(rename = "wed"))]
word_end_data: WordEnd<D>,
}
impl<D> Default for TrieDataNode<D> {
fn default() -> Self {
Self {
children: ChildStorage::default(),
word_end_data: None,
}
}
}
impl<D> TrieDataNode<D> {
pub(crate) fn new() -> Self {
TrieDataNode {
children: Default::default(),
word_end_data: None,
}
}
pub(crate) fn remove_all_words_collect(&mut self, found_data: &mut Vec<D>) -> usize {
let num_removed = self
.children
.values_mut()
.map(|child| child.remove_all_words_collect(found_data))
.sum::<usize>()
+ self.is_associated() as usize;
if let Some(data_vec) = self.disassociate() {
found_data.extend(data_vec);
}
self.clear_children();
num_removed
}
pub(crate) fn count_words(&self) -> usize {
self.children
.values()
.map(|child| child.count_words())
.sum::<usize>()
+ self.is_associated() as usize
}
pub(crate) fn generate_all_data<'a>(&'a self, found_data: &mut Vec<&'a D>) {
if let Some(data_vec) = &self.word_end_data {
found_data.extend(data_vec.iter());
}
self.children
.values()
.for_each(|x| x.generate_all_data(found_data));
}
pub(crate) fn generate_all_data_mut<'a>(&'a mut self, found_data: &mut Vec<&'a mut D>) {
if let Some(data_vec) = &mut self.word_end_data {
found_data.extend(data_vec.iter_mut());
}
self.children
.values_mut()
.for_each(|x| x.generate_all_data_mut(found_data));
}
pub(crate) fn push_data(&mut self, data: D) {
self.get_association_mut().as_mut().unwrap().push(data);
}
pub(crate) fn find_words(&self, substring: &mut String, found_words: &mut Vec<String>) {
if self.is_associated() {
found_words.push(substring.to_string());
}
for (&character, node) in self.children.iter() {
substring.push(character);
node.find_words(substring, found_words);
substring.pop();
}
}
pub(crate) fn words_min_max(
&self,
substring: &mut String,
current_visual_len: usize,
found_words: &mut Vec<String>,
current_best_len: &mut Option<usize>,
#[cfg(feature = "unicode")] is_path_pure_ascii: bool,
ord: Ordering,
) {
if self.is_associated() {
match current_best_len {
Some(best_len) => match current_visual_len.cmp(best_len) {
o if o == ord => {
*best_len = current_visual_len;
found_words.clear();
found_words.push(substring.clone());
}
Ordering::Equal => {
found_words.push(substring.clone());
}
_ => {}
},
None => {
*current_best_len = Some(current_visual_len);
found_words.push(substring.clone());
}
}
}
for (&character, node) in self.children.iter() {
substring.push(character);
#[cfg(feature = "unicode")]
let (next_visual_len, next_is_ascii) = if is_path_pure_ascii && character.is_ascii() {
(current_visual_len + 1, true)
} else {
(substring.graphemes(true).count(), false)
};
#[cfg(not(feature = "unicode"))]
let next_visual_len = current_visual_len + 1;
node.words_min_max(
substring,
next_visual_len,
found_words,
current_best_len,
#[cfg(feature = "unicode")]
next_is_ascii,
ord,
);
substring.pop();
}
}
pub(crate) fn clear_word_end_association(&mut self, keep_word: bool) -> WordEnd<D> {
let return_data = self.disassociate();
if keep_word && return_data.is_some() {
self.associate();
}
return_data
}
pub(crate) fn remove_one_word<'b>(
&mut self,
mut characters: impl Iterator<Item = char>,
) -> RemoveData<D> {
let next_character = match characters.next() {
None => {
return RemoveData {
must_keep: false,
data: self.disassociate(),
}
}
Some(char) => char,
};
let next_node = self.children.get_mut(next_character).unwrap();
let must_keep = next_node.remove_one_word(characters);
if self.children.len() > 1 || must_keep.must_keep {
return RemoveData {
must_keep: true,
data: must_keep.data,
};
}
self.clear_children();
RemoveData {
must_keep: self.is_associated(),
data: must_keep.data,
}
}
pub(crate) fn associate(&mut self) {
self.word_end_data = Some(ThinVec::new());
}
pub(crate) fn disassociate(&mut self) -> WordEnd<D> {
self.word_end_data.take()
}
pub(crate) fn is_associated(&self) -> bool {
self.word_end_data.is_some()
}
pub(crate) fn get_association(&self) -> &WordEnd<D> {
&self.word_end_data
}
pub(crate) fn get_association_mut(&mut self) -> &mut WordEnd<D> {
&mut self.word_end_data
}
pub(crate) fn clear_children(&mut self) {
self.children = Default::default();
}
}
impl<D> ops::AddAssign for TrieDataNode<D> {
fn add_assign(&mut self, rhs: Self) {
for (char, mut rhs_next_node) in rhs.children.into_iter() {
match self.children.remove(char) {
Some(mut self_next_node) => {
if let Some(data_vec_rhs) = rhs_next_node.word_end_data.take() {
if let Some(data_vec_self) = &mut self_next_node.word_end_data {
data_vec_self.extend(data_vec_rhs);
} else {
self_next_node.word_end_data = Some(data_vec_rhs);
}
}
self_next_node += rhs_next_node;
self.children.insert_direct(char, self_next_node);
}
None => {
self.children.insert_direct(char, rhs_next_node);
}
}
}
}
}
impl<D: PartialEq> PartialEq for TrieDataNode<D> {
fn eq(&self, other: &Self) -> bool {
if !self.children.has_same_keys(&other.children) {
return false;
}
if !match (&self.word_end_data, &other.word_end_data) {
(Some(self_vec), Some(other_vec)) => {
self_vec.len() == other_vec.len() && self_vec.iter().all(|k| other_vec.contains(k))
}
(None, None) => true,
_ => false,
} {
return false;
}
self.children
.iter()
.map(|(char, self_child)| (self_child, other.children.get(*char).unwrap()))
.all(|(self_child, other_child)| other_child == self_child)
}
}
impl<D: Debug> Debug for TrieDataNode<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("Node");
if let Some(data) = &self.word_end_data {
s.field("data", &data);
}
s.field("children", &self.children);
s.finish()
}
}