vidyut_chandas/
chandas.rs

1use crate::akshara::{scan_lines, Akshara};
2use crate::vrtta::{MatchType, Vrtta};
3use std::error::Error;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7/// Describes a result of classifying an input string with `Chandas`.
8pub struct MatchResult {
9    vrtta: Option<Vrtta>,
10    match_type: MatchType,
11    aksharas: Vec<Vec<Akshara>>,
12}
13
14impl MatchResult {
15    /// The vrtta match for this query.
16    pub fn vrtta(&self) -> &Option<Vrtta> {
17        &self.vrtta
18    }
19
20    /// The match type for this query.
21    pub fn match_type(&self) -> MatchType {
22        self.match_type
23    }
24
25    /// The aksharas in this query.
26    pub fn aksharas(&self) -> &Vec<Vec<Akshara>> {
27        &self.aksharas
28    }
29}
30
31/// A metrical classifier.
32///
33///
34/// ### Usage
35///
36/// ```
37/// use vidyut_chandas::{Chandas, MatchType, Vrtta};
38///
39/// let vrttas: Vec<Vrtta> = vec![
40///     "vasantatilakA\tvrtta\tGGLGLLLGLLGLGG".try_into().unwrap(),
41///     "mandAkrAntA\tvrtta\tGGGGLLLLLGGLGGLGG".try_into().unwrap(),
42///     "puzpitAgrA\tvrtta\tLLLLLLGLGLGG/LLLLGLLGLGLGG".try_into().unwrap(),
43///     "udgatA\tvrtta\tLLGLGLLLGL/LLLLLGLGLG/GLLLLLLGLLG/LLGLGLLLGLGLG".try_into().unwrap()
44/// ];
45/// let chandas = Chandas::new(vrttas);
46///
47/// let result = chandas.classify("mAtaH samastajagatAM maDukEwaBAreH");
48/// assert_eq!(result.vrtta().as_ref().unwrap().name(), "vasantatilakA");
49/// assert_eq!(result.match_type(), MatchType::Pada);
50/// ```
51#[derive(Clone, Debug, Eq, Hash, PartialEq)]
52pub struct Chandas {
53    vrttas: Vec<Vrtta>,
54}
55
56impl Chandas {
57    /// Creates a new `Chandas` instance.
58    pub fn new(vrttas: Vec<Vrtta>) -> Chandas {
59        Self { vrttas }
60    }
61
62    /// Creates a new `Chandas` instance by defining meters from the given text data.
63    ///
64    /// We recommend using this constructor when the program does not have access to the
65    /// filesystem, e.g. when using this code in WebAssembly.
66    pub fn from_text(data: &str) -> Result<Self, Box<dyn Error>> {
67        let vrttas: Result<Vec<_>, _> = data.lines().map(Vrtta::try_from).collect();
68        Ok(Self::new(vrttas?))
69    }
70
71    /// Creates a new classifier from the given data path.
72    pub fn from_file(path: &Path) -> Result<Self, Box<dyn Error>> {
73        let path = PathBuf::from(path).join(path);
74        let data = fs::read_to_string(path)?;
75        let vrttas: Result<Vec<_>, _> = data.lines().map(Vrtta::try_from).collect();
76
77        Ok(Self::new(vrttas?))
78    }
79
80    /// The vrttas available to this classifier.
81    pub fn vrttas(&self) -> &Vec<Vrtta> {
82        &self.vrttas
83    }
84
85    /// Classifies the input string against an internal list of meters.
86    ///
87    /// Currently, this function supports only simple samavrtta.
88    pub fn classify(&self, text: impl AsRef<str>) -> MatchResult {
89        let aksharas = scan_lines(text.as_ref().lines());
90
91        let mut best_match = MatchType::None;
92        let mut i_best = None;
93        for (i, vrtta) in self.vrttas.iter().enumerate() {
94            let match_type = vrtta.try_match(&aksharas);
95            if match_type > best_match {
96                i_best = Some(i);
97                best_match = match_type;
98            }
99        }
100
101        if let Some(i) = i_best {
102            MatchResult {
103                vrtta: Some(self.vrttas[i].clone()),
104                match_type: best_match,
105                aksharas,
106            }
107        } else {
108            MatchResult {
109                vrtta: None,
110                match_type: best_match,
111                aksharas,
112            }
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    fn assert_has_vrtta(c: &Chandas, text: &str, expected: &str) {
122        let res = c.classify(text);
123        assert_eq!(res.vrtta().as_ref().unwrap().name(), expected);
124    }
125
126    fn new_chandas() -> Chandas {
127        Chandas::new(vec![
128            "vasantatilakA\tvrtta\tGGLGLLLGLLGLGG".try_into().unwrap(),
129            "mandAkrAntA\tvrtta\tGGGGLLLLLGGLGGLGG".try_into().unwrap(),
130            "puzpitAgrA\tvrtta\tLLLLLLGLGLGG/LLLLGLLGLGLGG"
131                .try_into()
132                .unwrap(),
133            "udgatA\tvrtta\tLLGLGLLLGL/LLLLLGLGLG/GLLLLLLGLLG/LLGLGLLLGLGLG"
134                .try_into()
135                .unwrap(),
136        ])
137    }
138
139    #[test]
140    fn classify_samavrtta_single_pada() {
141        let c = new_chandas();
142        assert_has_vrtta(&c, "mAtaH samastajagatAM maDukEwaBAreH", "vasantatilakA");
143        assert_has_vrtta(&c, "mAtaH\nsamastajagatAM\nmaDukEwaBAreH", "vasantatilakA");
144    }
145
146    #[test]
147    fn classify_samavrtta_full_verse() {
148        let c = new_chandas();
149        assert_has_vrtta(
150            &c,
151            "kaScitkAntAvirahaguruRA svADikArapramattaH
152            zApenAstaMgamitamahimA varzaBogyeRa BartuH .
153            yakzaScakre janakatanayAsnAnapuRyodakezu
154            snigDacCAyAtaruzu vasatiM rAmagiryASramezu .. 1 ..",
155            "mandAkrAntA",
156        );
157        assert!(c.classify("mo mo go go vidyunmAlA").vrtta().is_none());
158    }
159
160    #[test]
161    fn classify_ardhasamavrtta_pada_1() {
162        let c = new_chandas();
163        assert_has_vrtta(&c, "aTa madanavaDUrupaplavAntaM", "puzpitAgrA");
164    }
165
166    #[test]
167    fn classify_ardhasamavrtta_half() {
168        let c = new_chandas();
169        assert_has_vrtta(
170            &c,
171            "aTa madanavaDUrupaplavAntaM vyasanakfSA paripAlayAmbaBUva",
172            "puzpitAgrA",
173        );
174        assert_has_vrtta(
175            &c,
176            "aTa\nmadanavaDUrupaplavAntaM\nvyasanakfSA\nparipAlayAmbaBUva",
177            "puzpitAgrA",
178        );
179    }
180
181    #[test]
182    fn classify_ardhasamavrtta_full_verse() {
183        let c = new_chandas();
184        assert_has_vrtta(
185            &c,
186            "aTa madanavaDUrupaplavAntaM vyasanakfSA paripAlayAmbaBUva |
187                SaSina iva divAtanasya leKA kiraRaparikzayaDUsarA pradozam ||",
188            "puzpitAgrA",
189        );
190    }
191
192    #[test]
193    fn classify_vishamavrtta_pada_1() {
194        let c = new_chandas();
195        assert_has_vrtta(&c, "aTa vAsavasya vacanena", "udgatA");
196    }
197
198    #[test]
199    fn classify_vishamavrtta_full_verse() {
200        let c = new_chandas();
201        assert_has_vrtta(
202            &c,
203            "aTa vAsavasya vacanena ruciravadanastrilocanam |
204                klAntirahitamaBirADayituM viDivattapAMsi vidaDe DanaMjayaH ||",
205            "udgatA",
206        );
207    }
208}