1use std::collections::HashMap;
4
5use brink_format::{DefinitionId, LineEntry, LocaleData};
6
7use crate::error::RuntimeError;
8use crate::program::Program;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum LocaleMode {
13 Strict,
16 Overlay,
18}
19
20pub fn apply_locale(
26 program: &Program,
27 locale: &LocaleData,
28 base: &[Vec<LineEntry>],
29 mode: LocaleMode,
30) -> Result<Vec<Vec<LineEntry>>, RuntimeError> {
31 if locale.base_checksum != program.source_checksum {
32 return Err(RuntimeError::LocaleChecksumMismatch {
33 expected: program.source_checksum,
34 actual: locale.base_checksum,
35 });
36 }
37
38 let scope_idx_map: HashMap<DefinitionId, usize> = program
40 .scope_ids
41 .iter()
42 .enumerate()
43 .map(|(i, &id)| (id, i))
44 .collect();
45
46 let mut result = base.to_vec();
48 let mut covered = vec![false; program.scope_ids.len()];
49
50 for locale_scope in &locale.line_tables {
51 let Some(&idx) = scope_idx_map.get(&locale_scope.scope_id) else {
52 return Err(RuntimeError::LocaleScopeNotInBase(locale_scope.scope_id));
53 };
54
55 let entries: Vec<LineEntry> = locale_scope
57 .lines
58 .iter()
59 .map(|le| {
60 let flags = brink_format::LineFlags::from_content(&le.content);
61 LineEntry {
62 content: le.content.clone(),
63 flags,
64 source_hash: 0,
65 audio_ref: le.audio_ref.clone(),
66 slot_info: Vec::new(),
67 source_location: None,
68 }
69 })
70 .collect();
71
72 result[idx] = entries;
73 covered[idx] = true;
74 }
75
76 if matches!(mode, LocaleMode::Strict) {
77 for (i, was_covered) in covered.iter().enumerate() {
78 if !was_covered {
79 return Err(RuntimeError::LocaleScopeMissing(program.scope_ids[i]));
80 }
81 }
82 }
83
84 Ok(result)
85}