1use serde::{Deserialize, Serialize};
3use http_body_util::BodyExt;
8use std::{
9 fs::File,
10 io::BufReader,
11 path::Path,
12};
13
14use anyhow::Context;
15use anyhow::Result;
16use ron::de::from_reader;
17
18use std::collections::HashMap;
21use std::sync::Arc;
22use holger_traits::RepositoryBackendTrait;
23use crate::exposed::ExposedEndpoint;
24use crate::exposed::fast_routes::FastRoutes;
25pub use crate::repository::Repository;
26pub use crate::storage::StorageEndpoint;
27
28pub mod exposed;
29mod repository;
30mod storage;
31
32pub fn wire_holger(holger: &mut Holger) -> Result<()> {
33 let mut repo_map = HashMap::new();
35 let mut exposed_map = HashMap::new();
36 let mut storage_map = HashMap::new();
37
38 for repo in &*holger.repositories {
39 repo_map.insert(repo.ron_name.clone(), repo as *const Repository);
40 }
41 for exp in &*holger.exposed_endpoints {
42 exposed_map.insert(exp.ron_name.clone(), exp as *const ExposedEndpoint);
43 }
44 for st in &*holger.storage_endpoints {
45 storage_map.insert(st.ron_name.clone(), st as *const StorageEndpoint);
46 }
47
48 for repo in &mut holger.repositories {
50 for name in &repo.ron_upstreams {
52 if let Some(ptr) = repo_map.get(name) {
53 repo.wired_upstreams.push(*ptr);
54 } else {
55 return Err(anyhow::anyhow!("Missing upstream repo: {}", name));
56 }
57 }
58
59 if let Some(io) = &mut repo.ron_in {
61 io.wired_storage = *storage_map
62 .get(&io.ron_storage_endpoint)
63 .ok_or_else(|| anyhow::anyhow!("Missing storage endpoint: {}", io.ron_storage_endpoint))?;
64 io.wired_exposed = *exposed_map
65 .get(&io.ron_exposed_endpoint)
66 .ok_or_else(|| anyhow::anyhow!("Missing exposed endpoint: {}", io.ron_exposed_endpoint))?;
67 }
68
69 if let Some(io) = &mut repo.ron_out {
71 io.wired_storage = *storage_map
72 .get(&io.ron_storage_endpoint)
73 .ok_or_else(|| anyhow::anyhow!("Missing storage endpoint: {}", io.ron_storage_endpoint))?;
74 io.wired_exposed = *exposed_map
75 .get(&io.ron_exposed_endpoint)
76 .ok_or_else(|| anyhow::anyhow!("Missing exposed endpoint: {}", io.ron_exposed_endpoint))?;
77 }
78 }
79
80 for exp in &mut holger.exposed_endpoints {
82 for repo in &holger.repositories {
83 if let Some(io) = &repo.ron_in {
84 if io.ron_exposed_endpoint == exp.ron_name {
85 exp.wired_in_repositories.push(repo as *const Repository);
86 }
87 }
88 if let Some(io) = &repo.ron_out {
89 if io.ron_exposed_endpoint == exp.ron_name {
90 exp.wired_out_repositories.push(repo as *const Repository);
91 }
92 }
93 }
94 }
95
96 for st in &mut holger.storage_endpoints {
97 for repo in &holger.repositories {
98 if let Some(io) = &repo.ron_in {
99 if io.ron_storage_endpoint == st.ron_name {
100 st.wired_in_repositories.push(repo as *const Repository);
101 }
102 }
103 if let Some(io) = &repo.ron_out {
104 if io.ron_storage_endpoint == st.ron_name {
105 st.wired_out_repositories.push(repo as *const Repository);
106 }
107 }
108 }
109 }
110
111 for repo in &holger.repositories {
113 let repo_ptr = repo as *const Repository;
114
115 if let Some(io) = &repo.ron_in {
117 let storage_ptr = io.wired_storage;
119 let storage = unsafe { &mut *(storage_ptr as *mut StorageEndpoint) };
120 storage.wired_in_repositories.push(repo_ptr);
121
122 let exposed_ptr = io.wired_exposed;
124 let exposed = unsafe { &mut *(exposed_ptr as *mut ExposedEndpoint) };
125 exposed.wired_in_repositories.push(repo_ptr);
126 }
127
128 if let Some(io) = &repo.ron_out {
130 let storage_ptr = io.wired_storage;
132 let storage = unsafe { &mut *(storage_ptr as *mut StorageEndpoint) };
133 storage.wired_out_repositories.push(repo_ptr);
134
135 let exposed_ptr = io.wired_exposed;
137 let exposed = unsafe { &mut *(exposed_ptr as *mut ExposedEndpoint) };
138 exposed.wired_out_repositories.push(repo_ptr);
139 }
140 }
141 for exp in &mut holger.exposed_endpoints {
143 let mut routes: Vec<(String, Arc<dyn RepositoryBackendTrait>)> = Vec::new();
145
146 for &repo_ptr in &exp.wired_out_repositories {
147 if repo_ptr.is_null() { continue; }
148 let repo: &Repository = unsafe { &*repo_ptr };
149
150 if let Some(backend_arc) = &repo.backend_repository {
151 routes.push((repo.ron_name.clone(), backend_arc.clone()));
152 }
153 }
154
155 exp.aggregated_routes = if routes.is_empty() {
156 None
157 } else {
158 Some(FastRoutes::new(routes))
159 };
160
161 exp.backend_from_config()?;
163
164 if let Some(routes) = exp.aggregated_routes.clone() {
166 let backend_mut = Arc::get_mut(&mut exp.backend_http2)
167 .expect("backend_http2 Arc must be unique here in wire_holger");
168 backend_mut.set_fast_routes(routes);
169 }
170 }
171
172 Ok(())
173}
174
175#[derive(Serialize, Deserialize)]
178pub struct Holger {
179 pub repositories: Vec<Repository>,
180 pub exposed_endpoints: Vec<ExposedEndpoint>,
181 pub storage_endpoints: Vec<StorageEndpoint>,
182}
183
184pub fn read_ron_config<P: AsRef<Path>>(path: P) -> Result<Holger> {
187 let file = File::open(path)?;
188 let reader = BufReader::new(file);
189 let holger: Holger = from_reader(reader)?;
190 Ok(holger)
191}
192
193
194impl Holger {
195 pub fn start(&self) -> anyhow::Result<()> {
196 for ep in &self.exposed_endpoints {
197 let backend = ep.backend_http2.clone();
198 tokio::spawn(async move {
199 if let Err(e) = backend.start().await {
200 eprintln!("Backend start failed: {e}");
201 }
202 });
203 }
204 Ok(())
205 }
206
207
208 pub fn stop(&self) -> anyhow::Result<()> {
209 for ep in &self.exposed_endpoints {
210 ep.backend_http2.stop()?;
212 }
213 Ok(())
214 }
215 pub fn instantiate_backends(&mut self) -> anyhow::Result<()> {
216 for ep in &mut self.exposed_endpoints {
217 ep.backend_from_config()?;
218 }
219 for se in &mut self.storage_endpoints {
220 se.backend_from_config()?;
221 }
222 for repo in &mut self.repositories {
223 repo.backend_from_config()?;
224 }
225 Ok(())
226 }
227}
228