1use std::collections::HashMap;
2use std::path::PathBuf;
3use tauri::path::BaseDirectory;
4use tauri::{AppHandle, Manager, Runtime};
5use wvb::remote;
6
7pub use wvb::remote::HttpConfig as Http;
8
9type DynamicDirFn<R> = fn(app: &AppHandle<R>) -> Result<PathBuf, Box<dyn std::error::Error>>;
10
11#[derive(Clone)]
12pub(crate) enum Dir<R: Runtime> {
13 Static(String),
14 Dynamic(DynamicDirFn<R>),
15}
16
17impl<R: Runtime> Dir<R> {
18 pub fn resolve(&self, app: &AppHandle<R>) -> Result<PathBuf, Box<dyn std::error::Error>> {
19 match self {
20 Self::Static(dir) => Ok(PathBuf::from(dir)),
21 Self::Dynamic(f) => {
22 let dir = f(app)?;
23 Ok(dir)
24 }
25 }
26 }
27}
28
29#[derive(Clone, Default)]
30pub struct Source<R: Runtime> {
31 pub(crate) builtin_dir: Option<Dir<R>>,
32 pub(crate) remote_dir: Option<Dir<R>>,
33}
34
35impl<R: Runtime> Source<R> {
36 pub fn new() -> Self {
37 Self {
38 builtin_dir: None,
39 remote_dir: None,
40 }
41 }
42
43 pub fn builtin_dir<T: Into<String>>(mut self, dir: T) -> Self {
44 self.builtin_dir = Some(Dir::Static(dir.into()));
45 self
46 }
47
48 pub fn builtin_dir_fn(mut self, dir: DynamicDirFn<R>) -> Self {
49 self.builtin_dir = Some(Dir::Dynamic(dir));
50 self
51 }
52
53 pub fn remote_dir<T: Into<String>>(mut self, dir: T) -> Self {
54 self.remote_dir = Some(Dir::Static(dir.into()));
55 self
56 }
57
58 pub fn remote_dir_fn(mut self, dir: DynamicDirFn<R>) -> Self {
59 self.remote_dir = Some(Dir::Dynamic(dir));
60 self
61 }
62
63 pub(crate) fn resolve_builtin_dir(&self, app: &AppHandle<R>) -> crate::Result<PathBuf> {
64 let dir = match self.builtin_dir {
65 Some(ref builtin_dir) => {
66 let dir = builtin_dir
67 .resolve(app)
68 .map_err(|e| crate::Error::FailToResolveDirectory(e.to_string()))?;
69 dir
70 }
71 None => {
72 let dir = app.path().resolve("bundles", BaseDirectory::Resource)?;
73 dir
74 }
75 };
76 Ok(dir)
77 }
78
79 pub(crate) fn resolve_remote_dir(&self, app: &AppHandle<R>) -> crate::Result<PathBuf> {
80 let dir = match self.remote_dir {
81 Some(ref remote_dir) => {
82 let dir = remote_dir
83 .resolve(app)
84 .map_err(|e| crate::Error::FailToResolveDirectory(e.to_string()))?;
85 dir
86 }
87 None => {
88 let dir = app.path().resolve("bundles", BaseDirectory::Resource)?;
89 dir
90 }
91 };
92 Ok(dir)
93 }
94}
95
96#[derive(Clone, Default)]
97pub struct Remote {
98 builder: remote::RemoteBuilder,
99}
100
101impl Remote {
102 pub fn new(endpoint: impl Into<String>) -> Self {
103 let builder = remote::Remote::builder().endpoint(endpoint);
104 Self { builder }
105 }
106
107 pub fn http(mut self, http: Http) -> Self {
108 self.builder = self.builder.http(http);
109 self
110 }
111
112 pub fn on_download<F>(mut self, on_download: F) -> Self
113 where
114 F: Fn(u64, u64, String) + Send + Sync + 'static,
115 {
116 self.builder = self.builder.on_download(on_download);
117 self
118 }
119
120 pub(crate) fn build(self) -> crate::Result<remote::Remote> {
121 let remote = self.builder.build()?;
122 Ok(remote)
123 }
124}
125
126#[derive(Clone)]
127pub struct BundleProtocolConfig {
128 scheme: String,
129}
130
131impl BundleProtocolConfig {
132 pub fn new<S: Into<String>>(scheme: S) -> Self {
133 Self {
134 scheme: scheme.into(),
135 }
136 }
137}
138
139#[derive(Clone)]
140pub struct LocalProtocolConfig {
141 scheme: String,
142 pub(crate) hosts: HashMap<String, String>,
143}
144
145impl LocalProtocolConfig {
146 pub fn new<S: Into<String>>(scheme: S) -> Self {
147 Self {
148 scheme: scheme.into(),
149 hosts: HashMap::new(),
150 }
151 }
152
153 pub fn new_with_hosts<T: Into<HashMap<String, String>>>(scheme: String, hosts: T) -> Self {
154 Self {
155 scheme,
156 hosts: hosts.into(),
157 }
158 }
159
160 pub fn host<T: Into<String>, U: Into<String>>(mut self, host: T, url: U) -> Self {
161 self.hosts.insert(host.into(), url.into());
162 self
163 }
164
165 pub fn hosts<T: Into<HashMap<String, String>>>(mut self, hosts: T) -> Self {
166 self.hosts = hosts.into();
167 self
168 }
169}
170
171#[derive(Clone)]
172pub enum Protocol {
173 Bundle(BundleProtocolConfig),
174 Local(LocalProtocolConfig),
175}
176
177impl Protocol {
178 pub fn bundle<S: Into<String>>(scheme: S) -> BundleProtocolConfig {
179 BundleProtocolConfig::new(scheme)
180 }
181
182 pub fn local<S: Into<String>>(scheme: S) -> LocalProtocolConfig {
183 LocalProtocolConfig::new(scheme)
184 }
185
186 pub fn scheme(&self) -> &str {
187 match self {
188 Protocol::Bundle(x) => &x.scheme,
189 Protocol::Local(x) => &x.scheme,
190 }
191 }
192}
193
194impl From<BundleProtocolConfig> for Protocol {
195 fn from(value: BundleProtocolConfig) -> Self {
196 Protocol::Bundle(value)
197 }
198}
199
200impl From<LocalProtocolConfig> for Protocol {
201 fn from(value: LocalProtocolConfig) -> Self {
202 Protocol::Local(value)
203 }
204}
205
206#[derive(Clone, Default)]
207pub struct Config<R: Runtime> {
208 pub(crate) source: Source<R>,
209 pub(crate) protocols: Vec<Protocol>,
210 pub(crate) remote: Option<Remote>,
211}
212
213impl<R: Runtime> Config<R> {
214 pub fn new() -> Self {
215 Self {
216 source: Source::new(),
217 protocols: vec![],
218 remote: Default::default(),
219 }
220 }
221
222 pub fn source(mut self, source: Source<R>) -> Self {
223 self.source = source;
224 self
225 }
226
227 pub fn protocol<P: Into<Protocol>>(mut self, protocol: P) -> Self {
228 self.protocols.push(protocol.into());
229 self
230 }
231
232 pub fn remote(mut self, remote: Remote) -> Self {
233 self.remote = Some(remote);
234 self
235 }
236
237 pub(crate) fn build_remote(&self) -> crate::Result<Option<remote::Remote>> {
238 if let Some(ref remote_config) = self.remote {
239 let remote = remote_config.clone().build()?;
240 Ok(Some(remote))
241 } else {
242 Ok(None)
243 }
244 }
245}