oca_parser_xls/xls_parser/
entries.rs

1use calamine::{open_workbook_auto, DataType, Reader};
2use isolang::Language;
3use serde::Serialize;
4use std::collections::{BTreeMap, HashMap};
5
6#[derive(Serialize)]
7pub struct ParsedResult {
8    pub codes: Vec<String>,
9    pub translations: BTreeMap<Language, BTreeMap<String, String>>,
10}
11
12const ENTRY_CODE_INDEX: u32 = 0;
13const ENTRY_LABEL_INDEX: u32 = 1;
14const SAMPLE_TEMPLATE_MSG: &str = "Sample file template can be found here: https://github.com/THCLab/oca-rs/blob/main/tests/assets/entries_template.xlsx";
15
16pub fn parse(path: String) -> Result<ParsedResult, String> {
17    let mut workbook = open_workbook_auto(path).or(Err(
18        "Provided file cannot be parsed. Check if file exists and format is XLS(X)",
19    ))?;
20    let mut sheet_names = workbook.sheet_names().to_vec();
21    sheet_names.retain(|n| n != "READ ME");
22
23    let mut translation_sheets: Vec<(Language, _)> = vec![];
24    for translation_sheet_name in sheet_names {
25        translation_sheets.push((
26            Language::from_639_1(&translation_sheet_name).ok_or(format!(
27                "Invalid language code: {}",
28                translation_sheet_name
29            ))?,
30            workbook
31                .worksheet_range(&translation_sheet_name.clone())
32                .unwrap()
33                .unwrap(),
34        ));
35    }
36
37    let first_translation_sheet = &translation_sheets
38        .first()
39        .ok_or(format!("Missing translation sheets. {SAMPLE_TEMPLATE_MSG}"))?
40        .1;
41
42    let start: u32 = 3;
43    let entries_range = (start - 1, first_translation_sheet.height() as u32);
44
45    let mut label_trans: HashMap<u32, HashMap<Language, String>> = HashMap::new();
46    for (lang, sheet) in translation_sheets.iter() {
47        for entry_index in (entries_range.0)..(entries_range.1) {
48            if let Some(DataType::String(label_value)) =
49                sheet.get_value((entry_index, ENTRY_LABEL_INDEX))
50            {
51                match label_trans.get_mut(&entry_index) {
52                    Some(entry_label_tr) => {
53                        entry_label_tr.insert(*lang, label_value.clone());
54                    }
55                    None => {
56                        let mut entry_label_tr: HashMap<Language, String> = HashMap::new();
57                        entry_label_tr.insert(*lang, label_value.clone());
58                        label_trans.insert(entry_index, entry_label_tr);
59                    }
60                }
61            }
62        }
63    }
64
65    let mut codes: Vec<String> = vec![];
66    let mut translations: BTreeMap<Language, BTreeMap<String, String>> = BTreeMap::new();
67
68    for entry_index in entries_range.0..entries_range.1 {
69        let entry_code = first_translation_sheet
70            .get_value((entry_index, ENTRY_CODE_INDEX))
71            .unwrap()
72            .to_string();
73        codes.push(entry_code.clone());
74
75        if let Some(label_tr) = label_trans.get(&entry_index).cloned() {
76            for (lang, label) in label_tr.iter() {
77                match translations.get_mut(lang) {
78                    Some(tr) => {
79                        tr.insert(entry_code.clone(), label.clone());
80                    }
81                    None => {
82                        let mut tr: BTreeMap<String, String> = BTreeMap::new();
83                        tr.insert(entry_code.clone(), label.clone());
84                        translations.insert(*lang, tr);
85                    }
86                }
87            }
88        }
89    }
90
91    Ok(ParsedResult {
92        codes,
93        translations,
94    })
95}