1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2016 Mark Sta Ana.
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0>, at your option.
// This file may not be copied, modified, or distributed except
// according to those terms.

//! A collection of useful natural language functions.
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/wordsworth) and can be
//! used by adding `wordsworth` to the dependencies in your project's `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//! wordsworth = "0.1.*"
//! ```
//!
//! and this to your crate root:
//!
//! ```rust
//! extern crate wordsworth;
//! ```
//!
//! # Example
//!
//! ```rust
//! use wordsworth;
//! assert_eq!(3, wordsworth::syllable_counter("lucozade"));
//! assert_eq!(false, wordsworth::is_haiku("this is not\nnot\n a haiku"));
//! ```
//!

/// Returns the syllable count for a word
pub fn syllable_counter(word: &str) -> u32 {
    const DEBUG: bool = false; // must be a better way to do this

    let vowels = "aeiouy";
    let specials = vec!["ia", "ea"]; // single syllables in words like bread and lead, but split in names like Breanne and Adreann TODO: handle names, where we only use "ia"
    let specials_except_end = vec!["ie", "ya", "es", "ed"]; // seperate syllables unless ending the word

    let mut count = 0;
    let mut last_letter: char = ' ';
    let mut last_was_vowel = false;
    let normalised = word.to_lowercase();

    for letter in normalised.chars() {
        if vowels.chars().any(|c| c == letter) {

            // don't count diphthongs unless special cases
            let mut combo = String::new();
            combo.push(last_letter);
            combo.push(letter);

            let is_not_in_special = !&specials.iter().any(|x| x == &combo);
            let is_not_in_specials_except_end = !&specials_except_end.iter().any(|x| x == &combo);
            if last_was_vowel && is_not_in_special && is_not_in_specials_except_end {
                last_was_vowel = true;
            } else {
                count += 1;
                if DEBUG {
                    println!("count: {} => {}", count, letter);
                }
                last_was_vowel = true;
            }
        } else {
            last_was_vowel = false;
        }
        last_letter = letter;
    }

    if word.len() > 2 {
        let testcase_1 = &word[word.len() - 2..];
        let testcase_2 = &word[word.len() - 1..];


        if DEBUG {
            println!("test1 {} test2 {}", testcase_1, testcase_2);
        }
        if specials_except_end.iter().any(|x| x == &testcase_1) {
            if DEBUG {
                println!("!!{} match test1 in specials_except_end", word);
            }
            count -= 1;
        } else if testcase_1 != "ee" && testcase_2 == "e" && normalised != "the" {
            if DEBUG {
                println!("!!{} test1 and test2", word);
            }
            count -= 1;
        }
    }
    count
}

/// Returns a bool value for a haiku candidate
pub fn is_haiku(poem: &str) -> bool {
    // checks 3 lines
    // lines 1 and 3 = 5, otherwise 7
    let mut syllable_total = 0;
    let mut line_count = 0;

    for line in poem.lines() {
        println!("line: {:?}", line);
        for word in line.split(" ") {
            let syllable_count = syllable_counter(&word);
            syllable_total += syllable_count;
            // println!("\tword: {} - syllables: {}", word, syllable_count);
        }
        line_count += 1;
        // println!("line: {} subtotal: {}", line_count, syllable_total);
    }

    line_count == 3 && syllable_total == 17
}


#[cfg(test)]
#[test]
fn correct_syllable_count() {
    // TODO: Create a hashmap
    assert_eq!(1, syllable_counter("the"));
    assert_eq!(3, syllable_counter("lucozade"));
    assert_eq!(1, syllable_counter("love"));
    assert_eq!(2, syllable_counter("dodo"));
    assert_eq!(1, syllable_counter("world"));
    assert_eq!(2, syllable_counter("atom"));
    assert_eq!(3, syllable_counter("energy"));
    assert_eq!(4, syllable_counter("combination"));
}

// TODO: fn known_trixsy_words
#[test]
fn this_is_a_haiku() {
    // haiku source: https://www.youngwriters.co.uk/types-haiku-poem
    let haiku = "The sky is so blue.\nThe sun is so warm up high.\nI love the summer.";
    assert_eq!(true, is_haiku(&haiku));
}

// TODO: this_is_not_a_haiku - not 5-7-5
// TODO: this_is_not_a_haiku - not 3 lines