1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::{fs, io};
4
5use sofie_engine::{ActionPost, Binding, Components, Template, TemplateError};
6use sofie_model::Module;
7
8pub struct Screens {
10 by_file: HashMap<PathBuf, io::Result<Screen>>,
11 components: Components,
12 refused_components: Vec<(PathBuf, String)>,
14}
15
16pub struct Screen {
17 pub source: String,
18 pub template: Result<Template, TemplateError>,
19 pub bindings: Result<Vec<Binding>, TemplateError>,
20 pub actions: Result<Vec<ActionPost>, TemplateError>,
21}
22
23impl Screens {
24 #[must_use]
25 pub fn read(modules: &[Module]) -> Self {
26 let (components, refused_components) = read_components(modules);
27 let files = modules
28 .iter()
29 .flat_map(|module| &module.areas)
30 .flat_map(|area| {
31 let overrides = area.overrides.iter().map(|replacement| &replacement.file);
32 area.screens.iter().chain(overrides)
33 });
34 let by_file = files
35 .map(|file| (file.clone(), Screen::read(file, &components)))
36 .collect();
37 Self {
38 by_file,
39 components,
40 refused_components,
41 }
42 }
43
44 #[must_use]
48 pub fn screen(&self, file: &Path) -> &io::Result<Screen> {
49 self.by_file.get(file).unwrap_or_else(|| {
50 panic!(
51 "{} is not a screen of the app the screens were read from",
52 file.display()
53 )
54 })
55 }
56
57 #[must_use]
58 pub fn components(&self) -> &Components {
59 &self.components
60 }
61
62 #[must_use]
63 pub fn refused_components(&self) -> &[(PathBuf, String)] {
64 &self.refused_components
65 }
66}
67
68fn read_components(modules: &[Module]) -> (Components, Vec<(PathBuf, String)>) {
69 let mut components = Components::default();
70 let mut refused = Vec::new();
71 for file in modules.iter().flat_map(|module| &module.components) {
72 let name = file
73 .file_stem()
74 .map(|stem| stem.to_string_lossy().into_owned())
75 .unwrap_or_default();
76 let added = fs::read_to_string(file)
77 .map_err(|error| format!("the component cannot be read: {error}"))
78 .and_then(|source| components.add(&name, source).map_err(|error| error.message));
79 if let Err(message) = added {
80 refused.push((file.clone(), message));
81 }
82 }
83 (components, refused)
84}
85
86impl Screen {
87 fn read(file: &Path, components: &Components) -> io::Result<Self> {
88 let source = fs::read_to_string(file)?;
89 let name = file.display().to_string();
90 Ok(Self {
91 template: Template::parse_with(&name, &source, components),
92 bindings: sofie_engine::bindings_with(&name, &source, components),
93 actions: sofie_engine::action_posts_with(&name, &source, components),
94 source,
95 })
96 }
97}