lib.rs
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
pub mod configuration; pub mod errors; pub mod math; use crate::configuration::{ ApplicationConfiguration, KdlConfigurable }; use crate::errors::{ Error, Result }; use kdl::{ KdlDocument }; use zip::{ ZipArchive }; use std::fs::{ self, File }; use std::io::{ Read }; use std::path::{ Path }; #[derive(Debug)] pub struct Application { configuration: ApplicationConfiguration, source: String, } impl Application { pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Application> { let path = path.as_ref(); if path.is_dir() { Application::from_directory(path) } else { Application::from_zip_archive(path) } } pub fn from_directory<P: AsRef<Path>>(path: P) -> Result<Application> { let Some(kdl) = Application::load_crucible_kdl_from_directory(&path)? else { return Ok(Application::default()); }; let configuration = kdl .get("application") .and_then(|node| node.children()) .map(|children| ApplicationConfiguration::from_kdl_document(children)) .unwrap_or(Ok(ApplicationConfiguration::default()))?; let source = path .as_ref() .display() .to_string(); Ok(Application { configuration, source, }) } pub fn from_zip_archive<P: AsRef<Path>>(path: P) -> Result<Application> { let Some(kdl) = Application::load_crucible_kdl_from_zip_archive(&path)? else { return Ok(Application::default()); }; let configuration = kdl .get("application") .and_then(|node| node.children()) .map(|children| ApplicationConfiguration::from_kdl_document(children)) .unwrap_or(Ok(ApplicationConfiguration::default()))?; let source = path .as_ref() .display() .to_string(); Ok(Application { configuration, source, }) } pub fn configuration(&self) -> &ApplicationConfiguration { &self.configuration } pub fn source(&self) -> &str { &self.source } fn load_crucible_kdl_from_directory<P: AsRef<Path>>(path: P) -> Result<Option<KdlDocument>> { let path = path.as_ref(); for entry in path.read_dir()? { let entry = entry?; let entry_path = entry.path(); if !entry_path.is_file() { continue; } if !ApplicationConfiguration::is_configuration_file(&entry_path) { continue; } let content = fs::read_to_string(entry_path)?; let kdl = content.parse::<KdlDocument>()?; return Ok(Some(kdl)) } Ok(None) } fn load_crucible_kdl_from_zip_archive<P: AsRef<Path>>(path: P) -> Result<Option<KdlDocument>> { let path = path.as_ref(); let file = File::open(path)?; let mut archive = ZipArchive::new(file)?; for i in 0..archive.len() { let mut entry = archive.by_index(i)?; if !entry.is_file() { continue; } let entry_path = entry .enclosed_name() .ok_or(Error::BadZipFileEntry(i))?; if !ApplicationConfiguration::is_configuration_file(entry_path) { continue; } let content = { let mut buffer = String::new(); entry.read_to_string(&mut buffer)?; buffer }; let kdl = content.parse::<KdlDocument>()?; return Ok(Some(kdl)) } Ok(None) } } impl Default for Application { fn default() -> Application { Application { configuration: ApplicationConfiguration::default(), source: "<default application instance>".to_string(), } } }