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
use calamine::{open_workbook_auto, DataType, Reader};
use isolang::Language;
use serde::Serialize;
use std::collections::{BTreeMap, HashMap};

#[derive(Serialize)]
pub struct ParsedResult {
    pub codes: Vec<String>,
    pub translations: BTreeMap<Language, BTreeMap<String, String>>,
}

const ENTRY_CODE_INDEX: u32 = 0;
const ENTRY_LABEL_INDEX: u32 = 1;
const SAMPLE_TEMPLATE_MSG: &str = "Sample file template can be found here: https://github.com/THCLab/oca-rs/blob/main/tests/assets/entries_template.xlsx";

pub fn parse(path: String) -> Result<ParsedResult, String> {
    let mut workbook = open_workbook_auto(path).or(Err(
        "Provided file cannot be parsed. Check if file exists and format is XLS(X)",
    ))?;
    let mut sheet_names = workbook.sheet_names().to_vec();
    sheet_names.retain(|n| n != "READ ME");

    let mut translation_sheets: Vec<(Language, _)> = vec![];
    for translation_sheet_name in sheet_names {
        translation_sheets.push((
            Language::from_639_1(&translation_sheet_name).ok_or(format!(
                "Invalid language code: {}",
                translation_sheet_name
            ))?,
            workbook
                .worksheet_range(&translation_sheet_name.clone())
                .unwrap()
                .unwrap(),
        ));
    }

    let first_translation_sheet = &translation_sheets
        .first()
        .ok_or(format!("Missing translation sheets. {SAMPLE_TEMPLATE_MSG}"))?
        .1;

    let start: u32 = 3;
    let entries_range = (start - 1, first_translation_sheet.height() as u32);

    let mut label_trans: HashMap<u32, HashMap<Language, String>> = HashMap::new();
    for (lang, sheet) in translation_sheets.iter() {
        for entry_index in (entries_range.0)..(entries_range.1) {
            if let Some(DataType::String(label_value)) =
                sheet.get_value((entry_index, ENTRY_LABEL_INDEX))
            {
                match label_trans.get_mut(&entry_index) {
                    Some(entry_label_tr) => {
                        entry_label_tr.insert(*lang, label_value.clone());
                    }
                    None => {
                        let mut entry_label_tr: HashMap<Language, String> = HashMap::new();
                        entry_label_tr.insert(*lang, label_value.clone());
                        label_trans.insert(entry_index, entry_label_tr);
                    }
                }
            }
        }
    }

    let mut codes: Vec<String> = vec![];
    let mut translations: BTreeMap<Language, BTreeMap<String, String>> = BTreeMap::new();

    for entry_index in entries_range.0..entries_range.1 {
        let entry_code = first_translation_sheet
            .get_value((entry_index, ENTRY_CODE_INDEX))
            .unwrap()
            .to_string();
        codes.push(entry_code.clone());

        if let Some(label_tr) = label_trans.get(&entry_index).cloned() {
            for (lang, label) in label_tr.iter() {
                match translations.get_mut(lang) {
                    Some(tr) => {
                        tr.insert(entry_code.clone(), label.clone());
                    }
                    None => {
                        let mut tr: BTreeMap<String, String> = BTreeMap::new();
                        tr.insert(entry_code.clone(), label.clone());
                        translations.insert(*lang, tr);
                    }
                }
            }
        }
    }

    Ok(ParsedResult {
        codes,
        translations,
    })
}