@browsersync/bslive 0.0.5 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +32 -18
- package/bin.js +6 -0
- package/bslive/Cargo.toml +35 -0
- package/{build.rs → bslive/build.rs} +1 -1
- package/bslive/src/lib.rs +130 -0
- package/bsnext/Cargo.toml +21 -0
- package/bsnext/src/main.rs +110 -0
- package/crates/bsnext_client/Cargo.toml +8 -0
- package/crates/bsnext_client/build.rs +53 -0
- package/crates/bsnext_client/dist/index.js +6493 -0
- package/crates/bsnext_client/generated/dto.ts +116 -0
- package/crates/bsnext_client/generated/schema.ts +187 -0
- package/crates/bsnext_client/index.html +14 -0
- package/crates/bsnext_client/package-lock.json +2059 -0
- package/crates/bsnext_client/package.json +25 -0
- package/crates/bsnext_client/src/lib.rs +1 -0
- package/crates/bsnext_client/style.css +3 -0
- package/crates/bsnext_client/ts/console.ts +25 -0
- package/crates/bsnext_client/ts/index.ts +73 -0
- package/crates/bsnext_client/tsconfig.json +16 -0
- package/crates/bsnext_core/Cargo.toml +43 -0
- package/crates/bsnext_core/src/dir_loader.rs +230 -0
- package/crates/bsnext_core/src/dto.rs +281 -0
- package/crates/bsnext_core/src/handlers/mod.rs +1 -0
- package/crates/bsnext_core/src/handlers/proxy.rs +95 -0
- package/crates/bsnext_core/src/lib.rs +11 -0
- package/crates/bsnext_core/src/meta/mod.rs +5 -0
- package/crates/bsnext_core/src/not_found/mod.rs +2 -0
- package/crates/bsnext_core/src/not_found/not_found.html +20 -0
- package/crates/bsnext_core/src/not_found/not_found_service.rs +41 -0
- package/crates/bsnext_core/src/not_found/route_list.rs +49 -0
- package/crates/bsnext_core/src/panic_handler.rs +32 -0
- package/crates/bsnext_core/src/raw_loader.rs +226 -0
- package/crates/bsnext_core/src/server/actor.rs +92 -0
- package/crates/bsnext_core/src/server/error.rs +41 -0
- package/crates/bsnext_core/src/server/handler_change.rs +85 -0
- package/crates/bsnext_core/src/server/handler_listen.rs +157 -0
- package/crates/bsnext_core/src/server/handler_patch.rs +42 -0
- package/crates/bsnext_core/src/server/handler_routes_updated.rs +27 -0
- package/crates/bsnext_core/src/server/handler_stop.rs +38 -0
- package/crates/bsnext_core/src/server/mod.rs +10 -0
- package/crates/bsnext_core/src/server/router/mod.rs +112 -0
- package/crates/bsnext_core/src/server/router/tests.rs +204 -0
- package/crates/bsnext_core/src/server/signals.rs +11 -0
- package/crates/bsnext_core/src/server/state.rs +19 -0
- package/crates/bsnext_core/src/servers_supervisor/actor.rs +199 -0
- package/crates/bsnext_core/src/servers_supervisor/file_changed_handler.rs +41 -0
- package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +23 -0
- package/crates/bsnext_core/src/servers_supervisor/input_changed_handler.rs +21 -0
- package/crates/bsnext_core/src/servers_supervisor/mod.rs +6 -0
- package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +82 -0
- package/crates/bsnext_core/src/servers_supervisor/stop_handler.rs +26 -0
- package/crates/bsnext_core/src/ws/mod.rs +164 -0
- package/crates/bsnext_example/Cargo.toml +10 -0
- package/crates/bsnext_example/src/basic.rs +51 -0
- package/crates/bsnext_example/src/lib.rs +26 -0
- package/crates/bsnext_example/src/lit.rs +37 -0
- package/crates/bsnext_example/src/md.rs +18 -0
- package/crates/bsnext_fs/Cargo.toml +22 -0
- package/crates/bsnext_fs/src/actor.rs +122 -0
- package/crates/bsnext_fs/src/buffered_debounce.rs +166 -0
- package/crates/bsnext_fs/src/filter.rs +30 -0
- package/crates/bsnext_fs/src/inner_fs_event_handler.rs +94 -0
- package/crates/bsnext_fs/src/lib.rs +141 -0
- package/crates/bsnext_fs/src/remove_path_handler.rs +46 -0
- package/crates/bsnext_fs/src/stop_handler.rs +15 -0
- package/crates/bsnext_fs/src/stream.rs +167 -0
- package/crates/bsnext_fs/src/test/mod.rs +213 -0
- package/crates/bsnext_fs/src/watch_path_handler.rs +67 -0
- package/crates/bsnext_fs/src/watcher.rs +348 -0
- package/crates/bsnext_input/Cargo.toml +22 -0
- package/crates/bsnext_input/src/input_test/mod.rs +151 -0
- package/crates/bsnext_input/src/lib.rs +153 -0
- package/crates/bsnext_input/src/md.rs +541 -0
- package/crates/bsnext_input/src/paths.rs +64 -0
- package/crates/bsnext_input/src/route.rs +185 -0
- package/crates/bsnext_input/src/route_manifest.rs +186 -0
- package/crates/bsnext_input/src/server_config.rs +88 -0
- package/crates/bsnext_input/src/target.rs +7 -0
- package/crates/bsnext_input/src/watch_opt_test/mod.rs +68 -0
- package/crates/bsnext_input/src/yml.rs +1 -0
- package/crates/bsnext_output/Cargo.toml +16 -0
- package/crates/bsnext_output/src/json.rs +11 -0
- package/crates/bsnext_output/src/lib.rs +25 -0
- package/crates/bsnext_output/src/pretty.rs +147 -0
- package/crates/bsnext_resp/Cargo.toml +13 -0
- package/crates/bsnext_resp/src/js/snippet.html +1 -0
- package/crates/bsnext_resp/src/js/ws.js +1 -0
- package/crates/bsnext_resp/src/lib.rs +79 -0
- package/crates/bsnext_system/Cargo.toml +29 -0
- package/crates/bsnext_system/src/args.rs +43 -0
- package/crates/bsnext_system/src/lib.rs +227 -0
- package/crates/bsnext_system/src/monitor.rs +241 -0
- package/crates/bsnext_system/src/monitor_any_watchables.rs +127 -0
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +11 -0
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +29 -0
- package/crates/bsnext_system/src/start_kind/start_from_example.rs +49 -0
- package/crates/bsnext_system/src/start_kind/start_from_inputs.rs +67 -0
- package/crates/bsnext_system/src/start_kind/start_from_paths.rs +51 -0
- package/crates/bsnext_system/src/start_kind.rs +56 -0
- package/crates/bsnext_system/src/startup.rs +51 -0
- package/crates/bsnext_tracing/Cargo.toml +10 -0
- package/crates/bsnext_tracing/src/lib.rs +89 -0
- package/examples/basic/input.yml +5 -0
- package/examples/basic/public/index.html +15 -0
- package/examples/basic/public/reset.css +52 -0
- package/examples/basic/public/script.js +1 -0
- package/examples/basic/public/styles.css +3 -0
- package/examples/kitchen-sink/a-file.md +1 -0
- package/examples/kitchen-sink/api/1.json +1 -0
- package/examples/kitchen-sink/api/2.json +3 -0
- package/examples/kitchen-sink/app.js +1 -0
- package/examples/kitchen-sink/input.html +185 -0
- package/examples/kitchen-sink/input.yml +133 -0
- package/examples/kitchen-sink/styles-2.css +3 -0
- package/examples/lit/index.html +21 -0
- package/examples/lit/input.yml +5 -0
- package/examples/lit/lit.js +82 -0
- package/examples/lit/package-lock.json +62 -0
- package/examples/lit/package.json +15 -0
- package/examples/md-single/frontmatter.md +35 -0
- package/examples/md-single/md-single.md +35 -0
- package/examples/openai/.nvm +1 -0
- package/examples/openai/build.mjs +21 -0
- package/examples/openai/index.html +13 -0
- package/examples/openai/input.yml +59 -0
- package/examples/openai/package-lock.json +720 -0
- package/examples/openai/package.json +21 -0
- package/examples/openai/src/index.js +21 -0
- package/examples/openai/sse/01.txt +8 -0
- package/examples/proxy-simple/input.yml +9 -0
- package/examples/single/input.yaml +26 -0
- package/index.d.ts +3 -1
- package/index.js +3 -2
- package/package.json +22 -19
- package/run.sh +6 -0
- package/src/lib.rs +0 -9
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
use std::fmt::{Display, Formatter};
|
|
2
|
+
use std::hash::{Hash, Hasher};
|
|
3
|
+
use std::ops::Deref;
|
|
4
|
+
|
|
5
|
+
use typeshare::typeshare;
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
8
|
+
pub struct Route {
|
|
9
|
+
pub path: String,
|
|
10
|
+
#[serde(flatten)]
|
|
11
|
+
pub cors_opts: Option<CorsOpts>,
|
|
12
|
+
#[serde(flatten)]
|
|
13
|
+
pub delay_opts: Option<DelayOpts>,
|
|
14
|
+
#[serde(rename = "watch")]
|
|
15
|
+
#[serde(default)]
|
|
16
|
+
pub watch_opts: WatchOpts,
|
|
17
|
+
#[serde(flatten)]
|
|
18
|
+
pub kind: RouteKind,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
impl Default for Route {
|
|
22
|
+
fn default() -> Self {
|
|
23
|
+
Self {
|
|
24
|
+
path: "/".to_string(),
|
|
25
|
+
kind: RouteKind::Html {
|
|
26
|
+
html: "default".into(),
|
|
27
|
+
},
|
|
28
|
+
delay_opts: None,
|
|
29
|
+
cors_opts: None,
|
|
30
|
+
watch_opts: Default::default(),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl AsRef<Route> for Route {
|
|
36
|
+
fn as_ref(&self) -> &Route {
|
|
37
|
+
self
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
impl Route {
|
|
42
|
+
pub fn path(&self) -> &str {
|
|
43
|
+
self.path.as_str()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[derive(Debug, Hash, PartialEq, Clone, serde::Deserialize, serde::Serialize)]
|
|
48
|
+
#[serde(untagged)]
|
|
49
|
+
pub enum RouteKind {
|
|
50
|
+
Html { html: String },
|
|
51
|
+
Json { json: JsonWrapper },
|
|
52
|
+
Raw { raw: String },
|
|
53
|
+
Sse { sse: String },
|
|
54
|
+
Proxy(ProxyRoute),
|
|
55
|
+
Dir(DirRoute),
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#[derive(Debug, PartialEq, Clone, serde::Deserialize, serde::Serialize)]
|
|
59
|
+
pub struct JsonWrapper(serde_json::Value);
|
|
60
|
+
|
|
61
|
+
impl Display for JsonWrapper {
|
|
62
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
63
|
+
write!(f, "{}", serde_json::to_string(&self.0).expect("serde_json"))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl Deref for JsonWrapper {
|
|
68
|
+
type Target = serde_json::Value;
|
|
69
|
+
|
|
70
|
+
fn deref(&self) -> &Self::Target {
|
|
71
|
+
&self.0
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
impl Hash for JsonWrapper {
|
|
76
|
+
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
77
|
+
//todo: implement hashing for serde_value
|
|
78
|
+
if let Ok(as_str) = serde_json::to_string(&self.0) {
|
|
79
|
+
state.write(as_str.as_bytes());
|
|
80
|
+
} else {
|
|
81
|
+
todo!("handle error here?")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[typeshare]
|
|
87
|
+
impl RouteKind {
|
|
88
|
+
#[allow(dead_code)]
|
|
89
|
+
pub fn html(s: &str) -> Self {
|
|
90
|
+
Self::Html { html: s.into() }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
95
|
+
pub struct DirRoute {
|
|
96
|
+
pub dir: String,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
100
|
+
pub struct ProxyRoute {
|
|
101
|
+
pub proxy: String,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
105
|
+
pub enum CorsOpts {
|
|
106
|
+
#[serde(rename = "cors")]
|
|
107
|
+
Cors(bool),
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
111
|
+
pub enum DelayOpts {
|
|
112
|
+
#[serde(rename = "delay")]
|
|
113
|
+
Delay(DelayKind),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#[derive(
|
|
117
|
+
Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
118
|
+
)]
|
|
119
|
+
pub enum DelayKind {
|
|
120
|
+
#[serde(rename = "ms")]
|
|
121
|
+
Ms(u64),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[derive(
|
|
125
|
+
Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
126
|
+
)]
|
|
127
|
+
pub enum DebounceDuration {
|
|
128
|
+
#[serde(rename = "ms")]
|
|
129
|
+
Ms(u64),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#[derive(
|
|
133
|
+
Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
134
|
+
)]
|
|
135
|
+
#[serde(untagged)]
|
|
136
|
+
pub enum FilterKind {
|
|
137
|
+
StringGlob(String),
|
|
138
|
+
Extension { ext: String },
|
|
139
|
+
Glob { glob: String },
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[derive(
|
|
143
|
+
Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
144
|
+
)]
|
|
145
|
+
pub struct Spec {
|
|
146
|
+
#[serde(flatten)]
|
|
147
|
+
pub opts: Option<SpecOpts>,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#[derive(
|
|
151
|
+
Debug, Ord, PartialOrd, PartialEq, Eq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
152
|
+
)]
|
|
153
|
+
pub struct SpecOpts {
|
|
154
|
+
pub debounce: Option<DebounceDuration>,
|
|
155
|
+
pub filter: Option<FilterKind>,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)]
|
|
159
|
+
#[serde(untagged)]
|
|
160
|
+
pub enum WatchOpts {
|
|
161
|
+
Bool(bool),
|
|
162
|
+
InlineGlob(String),
|
|
163
|
+
Spec(Spec),
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
impl Default for WatchOpts {
|
|
167
|
+
fn default() -> Self {
|
|
168
|
+
Self::Bool(true)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
impl WatchOpts {
|
|
173
|
+
pub fn is_enabled(&self) -> bool {
|
|
174
|
+
!matches!(self, WatchOpts::Bool(false))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
#[derive(
|
|
179
|
+
Debug, PartialOrd, Ord, Eq, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize,
|
|
180
|
+
)]
|
|
181
|
+
pub struct Watcher {
|
|
182
|
+
pub dir: String,
|
|
183
|
+
pub debounce_ms: Option<u64>,
|
|
184
|
+
pub filter: Option<FilterKind>,
|
|
185
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
use crate::route::{Route, RouteKind};
|
|
2
|
+
use std::collections::{HashMap, HashSet};
|
|
3
|
+
use std::hash::{DefaultHasher, Hash, Hasher};
|
|
4
|
+
|
|
5
|
+
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
6
|
+
pub struct RoutesManifest {
|
|
7
|
+
inner: HashMap<RouteIdentity, u64>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl RoutesManifest {
|
|
11
|
+
pub fn changeset_for(&self, other: &Self) -> RouteChangeSet {
|
|
12
|
+
let prev = self.inner.keys().collect::<HashSet<_>>();
|
|
13
|
+
let next = other.inner.keys().collect::<HashSet<_>>();
|
|
14
|
+
let changed_only = prev
|
|
15
|
+
.intersection(&next)
|
|
16
|
+
.filter_map(|id| {
|
|
17
|
+
let old = self.inner.get(*id);
|
|
18
|
+
let new = other.inner.get(*id);
|
|
19
|
+
match (old, new) {
|
|
20
|
+
(Some(old), Some(new)) if old != new => Some((*id).to_owned()),
|
|
21
|
+
_ => None,
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
.collect::<Vec<_>>();
|
|
25
|
+
RouteChangeSet {
|
|
26
|
+
added: next.difference(&prev).map(|x| (*x).to_owned()).collect(),
|
|
27
|
+
removed: prev.difference(&next).map(|x| (*x).to_owned()).collect(),
|
|
28
|
+
changed: changed_only,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
pub fn new<A: AsRef<Route>>(routes: &[A]) -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
inner: routes
|
|
34
|
+
.iter()
|
|
35
|
+
.map(|a| {
|
|
36
|
+
let route = a.as_ref();
|
|
37
|
+
let mut hasher = DefaultHasher::new();
|
|
38
|
+
route.hash(&mut hasher);
|
|
39
|
+
let r2_hash = hasher.finish();
|
|
40
|
+
let id: RouteIdentity = a.as_ref().into();
|
|
41
|
+
(id, r2_hash)
|
|
42
|
+
})
|
|
43
|
+
.collect::<HashMap<RouteIdentity, u64>>(),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#[derive(Debug, PartialEq, Hash, Eq, Clone)]
|
|
49
|
+
pub struct RouteIdentity {
|
|
50
|
+
pub path: String,
|
|
51
|
+
pub kind_str: String,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
impl From<&Route> for RouteIdentity {
|
|
55
|
+
fn from(value: &Route) -> Self {
|
|
56
|
+
Self {
|
|
57
|
+
path: value.path.clone(),
|
|
58
|
+
kind_str: match value.kind {
|
|
59
|
+
RouteKind::Html { .. } => "RouteKind::Html",
|
|
60
|
+
RouteKind::Json { .. } => "RouteKind::Json",
|
|
61
|
+
RouteKind::Raw { .. } => "RouteKind::Raw",
|
|
62
|
+
RouteKind::Sse { .. } => "RouteKind::Sse",
|
|
63
|
+
RouteKind::Proxy(_) => "RouteKind::Proxy",
|
|
64
|
+
RouteKind::Dir(_) => "RouteKind::Dir",
|
|
65
|
+
}
|
|
66
|
+
.to_string(),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
71
|
+
pub struct RouteChangeSet {
|
|
72
|
+
pub added: Vec<RouteIdentity>,
|
|
73
|
+
pub removed: Vec<RouteIdentity>,
|
|
74
|
+
pub changed: Vec<RouteIdentity>,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[cfg(test)]
|
|
78
|
+
mod test {
|
|
79
|
+
use super::*;
|
|
80
|
+
|
|
81
|
+
use std::hash::DefaultHasher;
|
|
82
|
+
#[test]
|
|
83
|
+
fn test_route_hash() -> anyhow::Result<()> {
|
|
84
|
+
let r1 = Route {
|
|
85
|
+
path: "/".to_string(),
|
|
86
|
+
kind: RouteKind::Html {
|
|
87
|
+
html: String::from("hello world!"),
|
|
88
|
+
},
|
|
89
|
+
..Default::default()
|
|
90
|
+
};
|
|
91
|
+
let r2 = r#"
|
|
92
|
+
path: /
|
|
93
|
+
html: hello world!
|
|
94
|
+
"#;
|
|
95
|
+
let r2: Route = serde_yaml::from_str(&r2).expect("test");
|
|
96
|
+
|
|
97
|
+
let mut hasher = DefaultHasher::new();
|
|
98
|
+
r1.hash(&mut hasher);
|
|
99
|
+
let r1_hash = hasher.finish();
|
|
100
|
+
|
|
101
|
+
let mut hasher = DefaultHasher::new();
|
|
102
|
+
r2.hash(&mut hasher);
|
|
103
|
+
let r2_hash = hasher.finish();
|
|
104
|
+
|
|
105
|
+
assert_eq!(r1_hash, r2_hash);
|
|
106
|
+
|
|
107
|
+
Ok(())
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#[test]
|
|
111
|
+
fn test_route_hash_2() -> anyhow::Result<()> {
|
|
112
|
+
let r1 = r#"
|
|
113
|
+
path: /
|
|
114
|
+
html: hello world!
|
|
115
|
+
"#;
|
|
116
|
+
let r2 = r#"
|
|
117
|
+
path: /api
|
|
118
|
+
html: hello world!
|
|
119
|
+
"#;
|
|
120
|
+
let r2_edited = r#"
|
|
121
|
+
path: /api
|
|
122
|
+
html: hello world
|
|
123
|
+
"#;
|
|
124
|
+
|
|
125
|
+
let r1: Route = serde_yaml::from_str(&r1).expect("test");
|
|
126
|
+
let r2: Route = serde_yaml::from_str(&r2).expect("test");
|
|
127
|
+
let r2_edited: Route = serde_yaml::from_str(&r2_edited).expect("test");
|
|
128
|
+
|
|
129
|
+
let routes_orig = RoutesManifest::new(&[&r1, &r2]);
|
|
130
|
+
let routes_next = RoutesManifest::new(&[&r1]);
|
|
131
|
+
let routes_next_dup = RoutesManifest::new(&[&r1, &r2]);
|
|
132
|
+
let routes_next_3 = RoutesManifest::new(&[&r1, &r2_edited]);
|
|
133
|
+
|
|
134
|
+
let changeset_1 = routes_orig.changeset_for(&routes_next);
|
|
135
|
+
let changeset_2 = routes_orig.changeset_for(&routes_next_dup);
|
|
136
|
+
let changeset_3 = routes_orig.changeset_for(&routes_next_3);
|
|
137
|
+
|
|
138
|
+
assert_eq!(
|
|
139
|
+
changeset_1,
|
|
140
|
+
RouteChangeSet {
|
|
141
|
+
added: vec![],
|
|
142
|
+
removed: vec![RouteIdentity {
|
|
143
|
+
path: "/api".to_string(),
|
|
144
|
+
kind_str: "RouteKind::Html".to_string()
|
|
145
|
+
}],
|
|
146
|
+
changed: vec![],
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
assert_eq!(
|
|
151
|
+
changeset_1,
|
|
152
|
+
RouteChangeSet {
|
|
153
|
+
added: vec![],
|
|
154
|
+
removed: vec![RouteIdentity {
|
|
155
|
+
path: "/api".to_string(),
|
|
156
|
+
kind_str: "RouteKind::Html".to_string()
|
|
157
|
+
}],
|
|
158
|
+
changed: vec![],
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// dbg!(changeset_1);
|
|
163
|
+
assert_eq!(
|
|
164
|
+
changeset_2,
|
|
165
|
+
RouteChangeSet {
|
|
166
|
+
added: vec![],
|
|
167
|
+
removed: vec![],
|
|
168
|
+
changed: vec![],
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
assert_eq!(
|
|
173
|
+
changeset_3,
|
|
174
|
+
RouteChangeSet {
|
|
175
|
+
added: vec![],
|
|
176
|
+
removed: vec![],
|
|
177
|
+
changed: vec![RouteIdentity {
|
|
178
|
+
path: "/api".to_string(),
|
|
179
|
+
kind_str: "RouteKind::Html".to_string()
|
|
180
|
+
}],
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
Ok(())
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
use crate::route::{Route, Watcher};
|
|
2
|
+
use crate::{rand_word, PortError};
|
|
3
|
+
use std::hash::{DefaultHasher, Hash, Hasher};
|
|
4
|
+
use std::net::SocketAddr;
|
|
5
|
+
use std::str::FromStr;
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
|
|
8
|
+
pub struct ServerConfig {
|
|
9
|
+
#[serde(flatten)]
|
|
10
|
+
pub identity: Identity,
|
|
11
|
+
#[serde(default)]
|
|
12
|
+
pub routes: Vec<Route>,
|
|
13
|
+
#[serde(default)]
|
|
14
|
+
pub watchers: Vec<Watcher>,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#[derive(
|
|
18
|
+
Debug, Ord, PartialOrd, PartialEq, Hash, Eq, Clone, serde::Deserialize, serde::Serialize,
|
|
19
|
+
)]
|
|
20
|
+
#[serde(untagged)]
|
|
21
|
+
pub enum Identity {
|
|
22
|
+
Both { name: String, bind_address: String },
|
|
23
|
+
Address { bind_address: String },
|
|
24
|
+
Named { name: String },
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl Default for Identity {
|
|
28
|
+
fn default() -> Self {
|
|
29
|
+
Self::named()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl Identity {
|
|
34
|
+
pub fn named() -> Self {
|
|
35
|
+
Self::Named { name: rand_word() }
|
|
36
|
+
}
|
|
37
|
+
pub fn address<A: AsRef<str>>(a: A) -> Self {
|
|
38
|
+
Self::Address {
|
|
39
|
+
bind_address: a.as_ref().to_string(),
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/// if a port was provided, try to use it by validating it first
|
|
43
|
+
/// otherwise default to a named identity
|
|
44
|
+
pub fn from_port_or_named(port: Option<u16>) -> Result<Self, PortError> {
|
|
45
|
+
let result = port.map(|port| {
|
|
46
|
+
SocketAddr::from_str(&format!("0.0.0.0:{port}"))
|
|
47
|
+
.map_err(|err| PortError::InvalidPort { port, err })
|
|
48
|
+
});
|
|
49
|
+
match result {
|
|
50
|
+
None => Ok(Identity::named()),
|
|
51
|
+
Some(Ok(addr)) => Ok(Identity::address(addr.to_string())),
|
|
52
|
+
Some(Err(err)) => Err(err),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub fn as_id(&self) -> u64 {
|
|
57
|
+
let mut hasher = DefaultHasher::new();
|
|
58
|
+
self.hash(&mut hasher);
|
|
59
|
+
hasher.finish()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#[test]
|
|
64
|
+
fn server_config_as_enum() {
|
|
65
|
+
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
|
66
|
+
struct C {
|
|
67
|
+
servers: Vec<ServerConfig>,
|
|
68
|
+
}
|
|
69
|
+
let input = r#"
|
|
70
|
+
servers:
|
|
71
|
+
- bind_address: 127.0.0.1:3000
|
|
72
|
+
- name: server_1
|
|
73
|
+
- name: server_2
|
|
74
|
+
bind_address: 127.0.0.1:3001
|
|
75
|
+
routes:
|
|
76
|
+
- path: /
|
|
77
|
+
dir: .
|
|
78
|
+
"#;
|
|
79
|
+
let c: C = serde_yaml::from_str(input).unwrap();
|
|
80
|
+
|
|
81
|
+
// let baseline = ServerConfigInput::Named { name: "server_1".into() };
|
|
82
|
+
let baseline = Identity::Address {
|
|
83
|
+
bind_address: "127.0.0.1x:3000".into(),
|
|
84
|
+
};
|
|
85
|
+
for x in &c.servers {
|
|
86
|
+
assert_eq!(x.identity == baseline, false);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
use crate::route::{DebounceDuration, FilterKind, Spec, SpecOpts, WatchOpts};
|
|
2
|
+
|
|
3
|
+
#[test]
|
|
4
|
+
fn test_watch_opts_debounce() {
|
|
5
|
+
let input = r#"
|
|
6
|
+
debounce:
|
|
7
|
+
ms: 200
|
|
8
|
+
filter: "**/*.css"
|
|
9
|
+
"#;
|
|
10
|
+
let expected = WatchOpts::Spec(Spec {
|
|
11
|
+
opts: Some(SpecOpts {
|
|
12
|
+
debounce: Some(DebounceDuration::Ms(200)),
|
|
13
|
+
filter: Some(FilterKind::StringGlob("**/*.css".into())),
|
|
14
|
+
}),
|
|
15
|
+
});
|
|
16
|
+
let actual: WatchOpts = serde_yaml::from_str(input).unwrap();
|
|
17
|
+
assert_eq!(actual, expected);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[test]
|
|
21
|
+
fn test_watch_opts_inline_filter() {
|
|
22
|
+
let input = r#"
|
|
23
|
+
filter: "**/*.css"
|
|
24
|
+
"#;
|
|
25
|
+
let expected = WatchOpts::Spec(Spec {
|
|
26
|
+
opts: Some(SpecOpts {
|
|
27
|
+
debounce: None,
|
|
28
|
+
filter: Some(FilterKind::StringGlob("**/*.css".into())),
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
let actual: WatchOpts = serde_yaml::from_str(input).unwrap();
|
|
32
|
+
assert_eq!(actual, expected);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[test]
|
|
36
|
+
fn test_watch_opts_explicit_filter_ext() {
|
|
37
|
+
let input = r#"
|
|
38
|
+
filter:
|
|
39
|
+
ext: "css"
|
|
40
|
+
"#;
|
|
41
|
+
let expected = WatchOpts::Spec(Spec {
|
|
42
|
+
opts: Some(SpecOpts {
|
|
43
|
+
debounce: None,
|
|
44
|
+
filter: Some(FilterKind::Extension {
|
|
45
|
+
ext: "css".to_string(),
|
|
46
|
+
}),
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
let actual: WatchOpts = serde_yaml::from_str(input).unwrap();
|
|
50
|
+
assert_eq!(actual, expected);
|
|
51
|
+
}
|
|
52
|
+
#[test]
|
|
53
|
+
fn test_watch_opts_explicit_filter_glob() {
|
|
54
|
+
let input = r#"
|
|
55
|
+
filter:
|
|
56
|
+
glob: "**/*.css"
|
|
57
|
+
"#;
|
|
58
|
+
let expected = WatchOpts::Spec(Spec {
|
|
59
|
+
opts: Some(SpecOpts {
|
|
60
|
+
debounce: None,
|
|
61
|
+
filter: Some(FilterKind::Glob {
|
|
62
|
+
glob: "**/*.css".into(),
|
|
63
|
+
}),
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
let actual: WatchOpts = serde_yaml::from_str(input).unwrap();
|
|
67
|
+
assert_eq!(actual, expected);
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "bsnext_output"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
|
+
|
|
8
|
+
[dependencies]
|
|
9
|
+
bsnext_core = { path = "../bsnext_core" }
|
|
10
|
+
|
|
11
|
+
ansi_term = { version = "0.12.1" }
|
|
12
|
+
|
|
13
|
+
anyhow = { workspace = true }
|
|
14
|
+
clap = { workspace = true }
|
|
15
|
+
serde = { workspace = true }
|
|
16
|
+
serde_json = { workspace = true }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
use crate::OutputWriter;
|
|
2
|
+
use bsnext_core::dto::ExternalEvents;
|
|
3
|
+
use std::io::Write;
|
|
4
|
+
|
|
5
|
+
pub struct JsonPrint;
|
|
6
|
+
|
|
7
|
+
impl OutputWriter for JsonPrint {
|
|
8
|
+
fn handle_event<W: Write>(&self, sink: &mut W, evt: &ExternalEvents) -> anyhow::Result<()> {
|
|
9
|
+
write!(sink, "{}", serde_json::to_string(&evt)?).map_err(|e| anyhow::anyhow!(e.to_string()))
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
use crate::json::JsonPrint;
|
|
2
|
+
use crate::pretty::PrettyPrint;
|
|
3
|
+
use bsnext_core::dto::ExternalEvents;
|
|
4
|
+
use std::io::Write;
|
|
5
|
+
|
|
6
|
+
pub mod json;
|
|
7
|
+
pub mod pretty;
|
|
8
|
+
|
|
9
|
+
pub trait OutputWriter {
|
|
10
|
+
fn handle_event<W: Write>(&self, sink: &mut W, evt: &ExternalEvents) -> anyhow::Result<()>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub enum Writers {
|
|
14
|
+
Pretty,
|
|
15
|
+
Json,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
impl OutputWriter for Writers {
|
|
19
|
+
fn handle_event<W: Write>(&self, sink: &mut W, evt: &ExternalEvents) -> anyhow::Result<()> {
|
|
20
|
+
match self {
|
|
21
|
+
Writers::Pretty => PrettyPrint.handle_event(sink, evt),
|
|
22
|
+
Writers::Json => JsonPrint.handle_event(sink, evt),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|