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
use jpreprocess_njd::NJDNode;

use crate::limit::Limit;

use super::*;

#[derive(Clone, Debug)]
pub struct Utterance {
    pub breath_groups: Vec<BreathGroup>,
}

impl Utterance {
    pub fn to_k(&self) -> jlabel::Utterance {
        format!(
            "/K:{}+{}-{}",
            Limit::S.ulimit(self.breath_groups.len()),
            Limit::M.ulimit(self.count_accent_phrase()),
            Limit::LL.ulimit(self.count_mora())
        );
        jlabel::Utterance {
            breath_group_count: Limit::S.ulimit(self.breath_groups.len()),
            accent_phrase_count: Limit::M.ulimit(self.count_accent_phrase()),
            mora_count: Limit::LL.ulimit(self.count_mora()),
        }
    }

    pub fn count_accent_phrase(&self) -> usize {
        self.breath_groups
            .iter()
            .map(|bg| bg.count_accent_phrase())
            .sum()
    }
    pub fn count_mora(&self) -> usize {
        self.breath_groups.iter().map(|bg| bg.count_mora()).sum()
    }
}

impl From<&[NJDNode]> for Utterance {
    fn from(nodes: &[NJDNode]) -> Self {
        let mut breath_groups: Vec<BreathGroup> = Vec::new();
        let mut accent_phrases: Vec<AccentPhrase> = Vec::with_capacity(nodes.len());

        for node in nodes {
            if node.get_pron().is_question() {
                if let Some(accent_phrase) = accent_phrases.last_mut() {
                    accent_phrase.set_interrogative();
                } else {
                    eprintln!("WARN: First mora should not be question flag.");
                }
            }
            if node.get_pron().is_touten() || node.get_pron().is_question() {
                if !accent_phrases.is_empty() {
                    breath_groups.push(BreathGroup::new(accent_phrases));
                }
                accent_phrases = Vec::new();
                continue;
            }

            if matches!(node.get_chain_flag(), Some(true)) {
                if let Some(accent_phrase) = accent_phrases.last_mut() {
                    accent_phrase.push_node(node);
                } else {
                    accent_phrases.push(AccentPhrase::new(node));
                    eprintln!("WARN: First mora cannot be chained.");
                }
            } else {
                accent_phrases.push(AccentPhrase::new(node));
            }
        }
        if !accent_phrases.is_empty() {
            breath_groups.push(BreathGroup::new(accent_phrases));
        }

        Self { breath_groups }
    }
}

#[cfg(test)]
mod tests {
    use crate::Utterance;

    #[test]
    fn test_send() {
        fn assert_send<T: Send>() {}
        assert_send::<Utterance>();
    }

    #[test]
    fn test_sync() {
        fn assert_sync<T: Sync>() {}
        assert_sync::<Utterance>();
    }
}