use rand::distr::{Distribution, Uniform};
use std::str::FromStr;
use crate::rng_joiner::Joiner;
use crate::rng_syllable::Syllable;
#[derive(Clone, Debug, PartialEq)]
pub struct Syllables(Vec<Syllable>);
impl Syllables {
pub fn new() -> Syllables {
Syllables::new_from_vector(Vec::new())
}
pub fn new_from_vector(v: Vec<Syllable>) -> Syllables {
Syllables(v)
}
pub fn new_from_array(strs: &[&str]) -> Syllables {
strs.iter()
.map(|s| Syllable::from_str(s).expect("array contains valid syllable strings"))
.collect()
}
pub fn add(&mut self, elem: Syllable) {
self.0.push(elem);
}
pub fn all(&self) -> &Vec<Syllable> {
&self.0
}
pub fn collapse(&self) -> String {
let mut s = String::new();
for syllable in &self.0 {
s.push_str(syllable.value.as_str());
}
s
}
pub fn contains(&self, syllable: &Syllable) -> bool {
self.0.contains(syllable)
}
pub fn filter_from(&self, from: Joiner) -> Syllables {
let v = self
.0
.iter()
.filter(|s| from.joins(s.jprevious))
.cloned()
.collect();
Syllables::new_from_vector(v)
}
pub fn first(&self) -> Option<&Syllable> {
self.0.first()
}
pub fn get(&self, index: usize) -> Option<&Syllable> {
self.0.get(index)
}
pub fn get_random(&self) -> Option<&Syllable> {
self.0.get(self.rnd())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn last(&self) -> Option<&Syllable> {
self.0.last()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn next_from(&self, from_syllable: &Syllable) -> Syllable {
self.filter_from(from_syllable.jnext)
.get_random()
.expect("a compatible syllable follows")
.clone()
}
fn rnd(&self) -> usize {
let mut rng = rand::rng();
let length = self.len();
if length < 2 {
0
} else {
let die = Uniform::new(0, self.len() - 1).expect("range is non-empty");
die.sample(&mut rng)
}
}
}
impl Default for Syllables {
fn default() -> Self {
Syllables::new()
}
}
impl FromIterator<Syllable> for Syllables {
fn from_iter<I: IntoIterator<Item = Syllable>>(iter: I) -> Self {
let mut c = Syllables::new();
for i in iter {
c.add(i);
}
c
}
}
impl IntoIterator for Syllables {
type Item = Syllable;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod syllables_tests {
use super::*;
use proptest::prelude::*;
#[test]
fn add() {
let mut syllables = Syllables::new_from_array(&["ch", "abc"]);
syllables.add(Syllable::from_str("efg").unwrap());
syllables.add(Syllable::from_str("hij").unwrap());
assert_eq!(syllables.len(), 4);
}
#[test]
fn all() {
let c = Syllables::new_from_array(&["ch", "abc"]);
let all: &Vec<Syllable> = c.all();
assert_eq!(c.len(), 2);
assert_eq!(c.get(0).unwrap(), &Syllable::from_str("ch").unwrap());
assert_eq!(c.get(1).unwrap(), &Syllable::from_str("abc").unwrap());
assert_eq!(all.len(), 2);
assert_eq!(all.get(0).unwrap(), &Syllable::from_str("ch").unwrap());
assert_eq!(all.get(1).unwrap(), &Syllable::from_str("abc").unwrap());
}
#[test]
fn collapse() {
let syllables = Syllables::new_from_array(&["ch", "abc"]);
let s = syllables.collapse();
assert_eq!("chabc".to_string(), s);
}
#[test]
fn contains() {
let mut syllables = Syllables::new_from_array(&["ch", "abc"]);
let efg = Syllable::from_str("efg").unwrap();
assert!(!syllables.contains(&efg));
syllables.add(efg.clone());
assert!(syllables.contains(&efg));
}
#[test]
fn filter_from() {
let syllables = Syllables::new_from_array(&["ch", "abc"]);
let joiner = Joiner::SOME | Joiner::ONLY_VOWEL;
let filtered = syllables.filter_from(joiner);
assert!(!filtered.contains(&Syllable::from_str("ch").unwrap()));
assert!(filtered.contains(&Syllable::from_str("abc").unwrap()));
}
#[test]
fn first() {
let zero = Syllables::new();
let three = Syllables::new_from_array(&["ch", "abc", "efg"]);
assert!(zero.first().is_none());
assert_eq!(three.first().unwrap(), &Syllable::from_str("ch").unwrap());
}
#[test]
fn last() {
let zero = Syllables::new();
let three = Syllables::new_from_array(&["ch", "abc", "efg"]);
assert!(zero.last().is_none());
assert_eq!(three.last().unwrap(), &Syllable::from_str("efg").unwrap());
}
#[test]
fn len() {
let zero = Syllables::new();
let three = Syllables::new_from_array(&["ch", "abc", "efg"]);
assert_eq!(zero.len(), 0);
assert_eq!(three.len(), 3);
}
#[test]
fn get() {
let syllables = Syllables::new_from_array(&["ch", "abc", "er", "go", "to"]);
assert_eq!(syllables.len(), 5);
assert_eq!(
syllables.get(0).unwrap(),
&Syllable::from_str("ch").unwrap()
);
assert_eq!(
syllables.get(1).unwrap(),
&Syllable::from_str("abc").unwrap()
);
assert_eq!(
syllables.get(2).unwrap(),
&Syllable::from_str("er").unwrap()
);
assert_eq!(
syllables.get(3).unwrap(),
&Syllable::from_str("go").unwrap()
);
assert_eq!(
syllables.get(4).unwrap(),
&Syllable::from_str("to").unwrap()
);
assert!(syllables.get(5).is_none());
}
#[test]
fn next_from() {
let b = Syllable::from_str("b").unwrap();
let mut v = Syllables::new();
v.add(b.clone());
let a = Syllable::from_str("a").unwrap();
let actual = v.next_from(&a);
assert_eq!(actual, b);
}
proptest! {
#[test]
fn rnd_test(_ in 0..20i32) {
let c = Syllables::new_from_array(&["ch", "abc", "er", "go", "to"]);
let n = c.rnd();
assert!(n < c.len());
}
#[test]
fn get_random(_ in 0..20i32) {
let syllables = Syllables::new_from_array(&["ch", "abc", "er", "go", "to"]);
let rnd = syllables.get_random().unwrap();
let non = Syllable::from_str("efg").unwrap();
assert!(syllables.contains(rnd));
assert!(!syllables.contains(&non));
}
}
}