oca_parser_xls/xls_parser/
entries.rs1use 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}