@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,147 @@
|
|
|
1
|
+
use crate::OutputWriter;
|
|
2
|
+
use bsnext_core::dto::{
|
|
3
|
+
ExternalEvents, FileChanged, FilesChangedDTO, IdentityDTO, InputAccepted, ServerChange,
|
|
4
|
+
ServerChangeSetItem, ServersStarted, StoppedWatching, Watching,
|
|
5
|
+
};
|
|
6
|
+
use std::io::Write;
|
|
7
|
+
use std::path::PathBuf;
|
|
8
|
+
|
|
9
|
+
pub struct PrettyPrint;
|
|
10
|
+
|
|
11
|
+
impl OutputWriter for PrettyPrint {
|
|
12
|
+
fn handle_event<W: Write>(&self, sink: &mut W, evt: &ExternalEvents) -> anyhow::Result<()> {
|
|
13
|
+
match &evt {
|
|
14
|
+
ExternalEvents::ServersStarted(servers_started) => {
|
|
15
|
+
print_server_started(sink, servers_started)
|
|
16
|
+
}
|
|
17
|
+
ExternalEvents::StartupFailed(_input_err) => {
|
|
18
|
+
unreachable!("StartupFailed")
|
|
19
|
+
}
|
|
20
|
+
ExternalEvents::Watching(watching) => print_watching(sink, watching),
|
|
21
|
+
ExternalEvents::WatchingStopped(watching) => print_stopped_watching(sink, watching),
|
|
22
|
+
ExternalEvents::InputAccepted(input_accepted) => {
|
|
23
|
+
print_input_accepted(sink, input_accepted)
|
|
24
|
+
}
|
|
25
|
+
ExternalEvents::FileChanged(file_changed) => print_file_changed(sink, file_changed),
|
|
26
|
+
ExternalEvents::FilesChanged(files_changed) => print_files_changed(sink, files_changed),
|
|
27
|
+
ExternalEvents::InputFileChanged(file_changed) => {
|
|
28
|
+
print_input_file_changed(sink, file_changed)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn print_file_changed<W: Write>(w: &mut W, evt: &FileChanged) -> anyhow::Result<()> {
|
|
35
|
+
writeln!(w, "[change] {}", evt.path)?;
|
|
36
|
+
Ok(())
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pub fn print_files_changed<W: Write>(w: &mut W, evt: &FilesChangedDTO) -> anyhow::Result<()> {
|
|
40
|
+
match evt.paths.len() {
|
|
41
|
+
0 | 1 | 2 => {
|
|
42
|
+
writeln!(w, "[multi-change] {}", short_file_list(&evt.paths))?;
|
|
43
|
+
}
|
|
44
|
+
3.. => {
|
|
45
|
+
let other = evt.paths.len() - 2;
|
|
46
|
+
let subset = evt.paths.iter().take(2).collect::<Vec<_>>();
|
|
47
|
+
writeln!(
|
|
48
|
+
w,
|
|
49
|
+
"[multi-change] {} (and {} others)",
|
|
50
|
+
short_file_list(&subset),
|
|
51
|
+
other
|
|
52
|
+
)?;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
Ok(())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn short_file_list<A: AsRef<str>>(paths: &[A]) -> String {
|
|
59
|
+
let file_names = paths
|
|
60
|
+
.iter()
|
|
61
|
+
.filter_map(|p| {
|
|
62
|
+
PathBuf::from(p.as_ref())
|
|
63
|
+
.file_name()
|
|
64
|
+
.map(|filename| filename.to_string_lossy().to_string())
|
|
65
|
+
})
|
|
66
|
+
.collect::<Vec<_>>();
|
|
67
|
+
file_names.join(", ")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
pub fn print_input_file_changed<W: Write>(w: &mut W, evt: &FileChanged) -> anyhow::Result<()> {
|
|
71
|
+
writeln!(w, "[change:input] {}", evt.path)?;
|
|
72
|
+
Ok(())
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pub fn print_input_accepted<W: Write>(w: &mut W, evt: &InputAccepted) -> anyhow::Result<()> {
|
|
76
|
+
writeln!(w, "[input] {}", evt.path)?;
|
|
77
|
+
Ok(())
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn print_watching<W: Write>(w: &mut W, evt: &Watching) -> anyhow::Result<()> {
|
|
81
|
+
for x in &evt.paths {
|
|
82
|
+
writeln!(w, "[watching {}] {}", evt.debounce, x)?;
|
|
83
|
+
}
|
|
84
|
+
Ok(())
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pub fn print_stopped_watching<W: Write>(w: &mut W, evt: &StoppedWatching) -> anyhow::Result<()> {
|
|
88
|
+
for x in &evt.paths {
|
|
89
|
+
writeln!(w, "[watching:stopped] {}", x)?;
|
|
90
|
+
}
|
|
91
|
+
Ok(())
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn print_server_started<W>(w: &mut W, servers_started: &ServersStarted) -> anyhow::Result<()>
|
|
95
|
+
where
|
|
96
|
+
W: Write,
|
|
97
|
+
{
|
|
98
|
+
let ServersStarted {
|
|
99
|
+
servers_resp,
|
|
100
|
+
changeset,
|
|
101
|
+
} = servers_started;
|
|
102
|
+
|
|
103
|
+
for ServerChangeSetItem { change, identity } in &changeset.items {
|
|
104
|
+
let running = servers_resp
|
|
105
|
+
.servers
|
|
106
|
+
.iter()
|
|
107
|
+
.find(|x| x.identity == *identity);
|
|
108
|
+
match change {
|
|
109
|
+
ServerChange::Stopped { bind_address } => match &identity {
|
|
110
|
+
IdentityDTO::Both { name, bind_address } => {
|
|
111
|
+
writeln!(w, "[server removed] [{name}] http://{bind_address}")?;
|
|
112
|
+
}
|
|
113
|
+
IdentityDTO::Address { bind_address } => {
|
|
114
|
+
writeln!(w, "[server removed] http://{bind_address}")?;
|
|
115
|
+
}
|
|
116
|
+
IdentityDTO::Named { name } => {
|
|
117
|
+
writeln!(w, "[server removed] [{name}] http://{}", bind_address)?;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
ServerChange::Started => match &identity {
|
|
121
|
+
IdentityDTO::Both { name, bind_address } => {
|
|
122
|
+
if running.is_some() {
|
|
123
|
+
writeln!(w, "[server added] [{}] http://{}", name, bind_address)?;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
IdentityDTO::Address { bind_address } => {
|
|
127
|
+
if running.is_some() {
|
|
128
|
+
writeln!(w, "[server added] http://{}", bind_address)?;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
IdentityDTO::Named { name } => {
|
|
132
|
+
if let Some(running) = running {
|
|
133
|
+
writeln!(
|
|
134
|
+
w,
|
|
135
|
+
"[server added] [{}] http://{}",
|
|
136
|
+
name, &running.socket_addr
|
|
137
|
+
)?;
|
|
138
|
+
} else {
|
|
139
|
+
unreachable!("?");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
ServerChange::Patched => {}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
Ok(())
|
|
147
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "bsnext_resp"
|
|
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
|
+
tracing = { workspace = true }
|
|
10
|
+
axum = { workspace = true }
|
|
11
|
+
http = { workspace = true }
|
|
12
|
+
bytes = { workspace = true }
|
|
13
|
+
http-body-util = { workspace = true }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<script type="module" src="/__bs_js"></script>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// todo
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
use axum::body::Body;
|
|
2
|
+
use axum::extract::Request;
|
|
3
|
+
use axum::middleware::Next;
|
|
4
|
+
use axum::response::IntoResponse;
|
|
5
|
+
use bytes::Bytes;
|
|
6
|
+
use http::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE};
|
|
7
|
+
use http::{Response, StatusCode};
|
|
8
|
+
use tracing::{span, Level};
|
|
9
|
+
|
|
10
|
+
pub struct RespMod;
|
|
11
|
+
|
|
12
|
+
impl RespMod {
|
|
13
|
+
pub fn accepts_html(req: &Request) -> bool {
|
|
14
|
+
req.headers()
|
|
15
|
+
.get(ACCEPT)
|
|
16
|
+
.and_then(|v| v.to_str().ok().map(|s| s.contains("text/html")))
|
|
17
|
+
.unwrap_or(false)
|
|
18
|
+
}
|
|
19
|
+
pub fn is_html<T>(res: &Response<T>) -> bool {
|
|
20
|
+
res.headers()
|
|
21
|
+
.get(CONTENT_TYPE)
|
|
22
|
+
.and_then(|v| v.to_str().ok().map(|s| s.contains("text/html")))
|
|
23
|
+
.unwrap_or(false)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pub async fn response_modifications_layer(
|
|
28
|
+
req: Request,
|
|
29
|
+
next: Next,
|
|
30
|
+
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
|
31
|
+
let span = span!(parent: None, Level::TRACE, "resp-mod", uri=req.uri().path());
|
|
32
|
+
let _guard = span.enter();
|
|
33
|
+
let accepts_html = RespMod::accepts_html(&req);
|
|
34
|
+
|
|
35
|
+
let mut r = next.run(req).await;
|
|
36
|
+
let is_html = RespMod::is_html(&r);
|
|
37
|
+
if let (true, true) = (accepts_html, is_html) {
|
|
38
|
+
use http_body_util::BodyExt;
|
|
39
|
+
r.headers_mut()
|
|
40
|
+
.insert("x-bslive-inject", "true".parse().unwrap());
|
|
41
|
+
let (mut parts, body) = r.into_parts();
|
|
42
|
+
|
|
43
|
+
let bytes = match body.collect().await {
|
|
44
|
+
Ok(collected) => collected.to_bytes(),
|
|
45
|
+
Err(err) => {
|
|
46
|
+
return Err((
|
|
47
|
+
StatusCode::BAD_REQUEST,
|
|
48
|
+
format!("failed to read body: {err}"),
|
|
49
|
+
))
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if let Ok(body) = std::str::from_utf8(&bytes) {
|
|
54
|
+
tracing::trace!("replacing body content");
|
|
55
|
+
let next_body = body.replace(
|
|
56
|
+
"</body>",
|
|
57
|
+
format!(
|
|
58
|
+
"<!-- source: snippet.html-->\
|
|
59
|
+
{}\
|
|
60
|
+
\
|
|
61
|
+
<!-- end: snippet.html-->
|
|
62
|
+
</body>",
|
|
63
|
+
include_str!("js/snippet.html")
|
|
64
|
+
)
|
|
65
|
+
.as_str(),
|
|
66
|
+
);
|
|
67
|
+
let as_bytes = Bytes::from(next_body);
|
|
68
|
+
parts.headers.insert(CONTENT_LENGTH, as_bytes.len().into());
|
|
69
|
+
Ok(Response::from_parts(parts, Body::from(as_bytes)))
|
|
70
|
+
} else {
|
|
71
|
+
tracing::trace!("could not decode from bytes");
|
|
72
|
+
dbg!(&parts.headers);
|
|
73
|
+
Ok((parts, bytes).into_response())
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
tracing::trace!("not collecting body");
|
|
77
|
+
Ok(r.into_response())
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "bsnext_system"
|
|
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_input = { path = "../bsnext_input" }
|
|
10
|
+
bsnext_tracing = { path = "../bsnext_tracing" }
|
|
11
|
+
bsnext_core = { path = "../bsnext_core" }
|
|
12
|
+
bsnext_fs = { path = "../bsnext_fs" }
|
|
13
|
+
bsnext_example = { path = "../bsnext_example" }
|
|
14
|
+
rand = "0.8.5"
|
|
15
|
+
|
|
16
|
+
anyhow = { workspace = true }
|
|
17
|
+
thiserror = { workspace = true }
|
|
18
|
+
clap = { workspace = true }
|
|
19
|
+
tokio = { workspace = true }
|
|
20
|
+
serde = { workspace = true }
|
|
21
|
+
serde_yaml = { workspace = true }
|
|
22
|
+
actix = { workspace = true }
|
|
23
|
+
actix-rt = { workspace = true }
|
|
24
|
+
tracing = { workspace = true }
|
|
25
|
+
random_word = { workspace = true }
|
|
26
|
+
tempfile = { workspace = true }
|
|
27
|
+
|
|
28
|
+
[dev-dependencies]
|
|
29
|
+
insta = { version = "1.38.0", features = ["yaml"] }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
use crate::Example;
|
|
2
|
+
use bsnext_input::target::TargetKind;
|
|
3
|
+
use bsnext_tracing::{LogLevel, OutputFormat};
|
|
4
|
+
|
|
5
|
+
#[derive(clap::Parser, Debug)]
|
|
6
|
+
pub struct Args {
|
|
7
|
+
#[arg(short, long, value_enum)]
|
|
8
|
+
pub log_level: Option<LogLevel>,
|
|
9
|
+
|
|
10
|
+
#[arg(short, long, value_enum)]
|
|
11
|
+
pub format: Option<OutputFormat>,
|
|
12
|
+
|
|
13
|
+
/// Input files
|
|
14
|
+
#[arg(short, long)]
|
|
15
|
+
pub input: Vec<String>,
|
|
16
|
+
|
|
17
|
+
/// Write input to disk
|
|
18
|
+
#[arg(long)]
|
|
19
|
+
pub write: bool,
|
|
20
|
+
|
|
21
|
+
/// Write input to disk
|
|
22
|
+
#[arg(long, requires = "write")]
|
|
23
|
+
pub target: Option<TargetKind>,
|
|
24
|
+
|
|
25
|
+
#[arg(long, value_enum)]
|
|
26
|
+
pub example: Option<Example>,
|
|
27
|
+
|
|
28
|
+
/// create a temp folder for examples instead of using the current dir
|
|
29
|
+
#[arg(long, requires = "example")]
|
|
30
|
+
pub temp: bool,
|
|
31
|
+
|
|
32
|
+
/// create a temp folder for examples instead of using the current dir
|
|
33
|
+
#[arg(long, requires = "example")]
|
|
34
|
+
pub name: Option<String>,
|
|
35
|
+
|
|
36
|
+
/// Only works with `--example` - specify a port instead of a random one
|
|
37
|
+
#[arg(short, long)]
|
|
38
|
+
pub port: Option<u16>,
|
|
39
|
+
|
|
40
|
+
/// Paths to watch, incompatible with `-i` option
|
|
41
|
+
#[arg(trailing_var_arg = true, allow_hyphen_values = true, long)]
|
|
42
|
+
pub paths: Vec<String>,
|
|
43
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
use crate::monitor::{
|
|
2
|
+
to_route_watchables, to_server_watchables, AnyWatchable, Monitor, MonitorInput,
|
|
3
|
+
};
|
|
4
|
+
use actix::{Actor, Addr, AsyncContext, Handler, Running};
|
|
5
|
+
|
|
6
|
+
use bsnext_input::Input;
|
|
7
|
+
use std::collections::HashMap;
|
|
8
|
+
|
|
9
|
+
use actix_rt::Arbiter;
|
|
10
|
+
use bsnext_core::dto::{ExternalEvents, ServersStarted};
|
|
11
|
+
use std::path::PathBuf;
|
|
12
|
+
|
|
13
|
+
use bsnext_example::Example;
|
|
14
|
+
|
|
15
|
+
use crate::startup::{DidStart, StartupContext, StartupError, StartupResult, SystemStart};
|
|
16
|
+
use bsnext_core::servers_supervisor::actor::ServersSupervisor;
|
|
17
|
+
use bsnext_core::servers_supervisor::get_servers_handler::GetServersMessage;
|
|
18
|
+
use bsnext_core::servers_supervisor::input_changed_handler::InputChanged;
|
|
19
|
+
|
|
20
|
+
use bsnext_fs::actor::FsWatcher;
|
|
21
|
+
|
|
22
|
+
use crate::monitor_any_watchables::MonitorAnyWatchables;
|
|
23
|
+
use start_kind::StartKind;
|
|
24
|
+
use tokio::sync::mpsc::Sender;
|
|
25
|
+
use tokio::sync::oneshot;
|
|
26
|
+
|
|
27
|
+
pub mod args;
|
|
28
|
+
mod monitor;
|
|
29
|
+
mod monitor_any_watchables;
|
|
30
|
+
pub mod start_kind;
|
|
31
|
+
pub mod startup;
|
|
32
|
+
|
|
33
|
+
pub struct BsSystem {
|
|
34
|
+
self_addr: Option<Addr<BsSystem>>,
|
|
35
|
+
servers_addr: Option<Addr<ServersSupervisor>>,
|
|
36
|
+
events_sender: Option<Sender<ExternalEvents>>,
|
|
37
|
+
input_monitors: Vec<Addr<FsWatcher>>,
|
|
38
|
+
any_monitors: HashMap<AnyWatchable, Monitor>,
|
|
39
|
+
cwd: Option<PathBuf>,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl Default for BsSystem {
|
|
43
|
+
fn default() -> Self {
|
|
44
|
+
Self::new()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
impl Handler<StopSystem> for BsSystem {
|
|
49
|
+
type Result = ();
|
|
50
|
+
|
|
51
|
+
fn handle(&mut self, _msg: StopSystem, _ctx: &mut Self::Context) -> Self::Result {
|
|
52
|
+
todo!("can the system as a whole be stopped?")
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
impl BsSystem {
|
|
57
|
+
pub fn new() -> Self {
|
|
58
|
+
BsSystem {
|
|
59
|
+
self_addr: None,
|
|
60
|
+
servers_addr: None,
|
|
61
|
+
events_sender: None,
|
|
62
|
+
input_monitors: vec![],
|
|
63
|
+
any_monitors: Default::default(),
|
|
64
|
+
cwd: None,
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn accept_input(&mut self, input: &Input) {
|
|
69
|
+
let route_watchables = to_route_watchables(input);
|
|
70
|
+
let server_watchables = to_server_watchables(input);
|
|
71
|
+
|
|
72
|
+
tracing::debug!(
|
|
73
|
+
"accepting {} route watchables, and {} server watchables",
|
|
74
|
+
route_watchables.len(),
|
|
75
|
+
server_watchables.len()
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
let Some(self_address) = &self.self_addr else {
|
|
79
|
+
unreachable!("?")
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let Some(cwd) = &self.cwd else {
|
|
83
|
+
unreachable!("can this occur?")
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// todo: clean up this merging
|
|
87
|
+
let mut routes = route_watchables
|
|
88
|
+
.iter()
|
|
89
|
+
.map(|r| AnyWatchable::Route(r.to_owned()))
|
|
90
|
+
.collect::<Vec<_>>();
|
|
91
|
+
|
|
92
|
+
let servers = server_watchables
|
|
93
|
+
.iter()
|
|
94
|
+
.map(|w| AnyWatchable::Server(w.to_owned()))
|
|
95
|
+
.collect::<Vec<_>>();
|
|
96
|
+
|
|
97
|
+
routes.extend(servers);
|
|
98
|
+
|
|
99
|
+
self_address.do_send(MonitorAnyWatchables {
|
|
100
|
+
watchables: routes,
|
|
101
|
+
cwd: cwd.clone(),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn inform_servers(&mut self, input: Input) {
|
|
106
|
+
let Some(servers_addr) = &self.servers_addr else {
|
|
107
|
+
unreachable!("self.servers_addr cannot exist?");
|
|
108
|
+
};
|
|
109
|
+
Arbiter::current().spawn({
|
|
110
|
+
let addr = servers_addr.clone();
|
|
111
|
+
let events_sender = self.events_sender.as_ref().unwrap().clone();
|
|
112
|
+
async move {
|
|
113
|
+
let results = addr.send(InputChanged { input }).await;
|
|
114
|
+
let Ok(changeset) = results else {
|
|
115
|
+
unreachable!("?1")
|
|
116
|
+
};
|
|
117
|
+
let servers = addr.send(GetServersMessage).await;
|
|
118
|
+
let Ok(servers_resp) = servers else {
|
|
119
|
+
unreachable!("?2")
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
let output = ExternalEvents::ServersStarted(ServersStarted {
|
|
123
|
+
servers_resp,
|
|
124
|
+
changeset,
|
|
125
|
+
});
|
|
126
|
+
match events_sender.send(output).await {
|
|
127
|
+
Ok(_) => tracing::trace!("Ok"),
|
|
128
|
+
Err(_) => tracing::trace!("Err"),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fn ext_evt(&mut self, evt: ExternalEvents) {
|
|
135
|
+
if let Some(sender) = &self.events_sender {
|
|
136
|
+
Arbiter::current().spawn({
|
|
137
|
+
let events_sender = sender.clone();
|
|
138
|
+
async move {
|
|
139
|
+
match events_sender.send(evt).await {
|
|
140
|
+
Ok(_) => {}
|
|
141
|
+
Err(_) => tracing::error!("could not send"),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
impl Actor for BsSystem {
|
|
150
|
+
type Context = actix::Context<Self>;
|
|
151
|
+
|
|
152
|
+
fn started(&mut self, ctx: &mut Self::Context) {
|
|
153
|
+
tracing::trace!(actor.name = "BsSystem", actor.lifecyle = "started");
|
|
154
|
+
self.self_addr = Some(ctx.address());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn stopping(&mut self, _ctx: &mut Self::Context) -> Running {
|
|
158
|
+
tracing::trace!(actor.name = "BsSystem", actor.lifecyle = "stopping");
|
|
159
|
+
Running::Stop
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn stopped(&mut self, _ctx: &mut Self::Context) {
|
|
163
|
+
tracing::trace!(actor.name = "BsSystem", actor.lifecyle = "stopped");
|
|
164
|
+
self.self_addr = None;
|
|
165
|
+
self.servers_addr = None;
|
|
166
|
+
self.events_sender = None;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#[derive(actix::Message)]
|
|
171
|
+
#[rtype(result = "()")]
|
|
172
|
+
pub struct StopSystem;
|
|
173
|
+
|
|
174
|
+
#[derive(actix::Message)]
|
|
175
|
+
#[rtype(result = "()")]
|
|
176
|
+
pub struct Start {
|
|
177
|
+
pub kind: StartKind,
|
|
178
|
+
pub cwd: Option<PathBuf>,
|
|
179
|
+
pub ack: oneshot::Sender<()>,
|
|
180
|
+
pub events_sender: Sender<ExternalEvents>,
|
|
181
|
+
pub startup_oneshot_sender: oneshot::Sender<StartupResult>,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
impl Handler<Start> for BsSystem {
|
|
185
|
+
type Result = ();
|
|
186
|
+
|
|
187
|
+
fn handle(&mut self, msg: Start, ctx: &mut Self::Context) -> Self::Result {
|
|
188
|
+
self.events_sender = Some(msg.events_sender.clone());
|
|
189
|
+
self.cwd = msg.cwd;
|
|
190
|
+
|
|
191
|
+
let Some(cwd) = &self.cwd else {
|
|
192
|
+
unreachable!("?")
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
let servers = ServersSupervisor::new(msg.ack);
|
|
196
|
+
// store the servers addr for later
|
|
197
|
+
self.servers_addr = Some(servers.start());
|
|
198
|
+
|
|
199
|
+
let start_context = StartupContext::from_cwd(self.cwd.as_ref());
|
|
200
|
+
|
|
201
|
+
match msg.kind.input(&start_context) {
|
|
202
|
+
Ok((input, Some(path))) => {
|
|
203
|
+
ctx.address().do_send(MonitorInput {
|
|
204
|
+
path: path.clone(),
|
|
205
|
+
cwd: cwd.clone(),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
self.accept_input(&input);
|
|
209
|
+
self.inform_servers(input);
|
|
210
|
+
}
|
|
211
|
+
Ok((input, None)) => {
|
|
212
|
+
self.accept_input(&input);
|
|
213
|
+
self.inform_servers(input);
|
|
214
|
+
}
|
|
215
|
+
Err(e) => {
|
|
216
|
+
msg.startup_oneshot_sender
|
|
217
|
+
.send(Err(StartupError::InputError(e)))
|
|
218
|
+
.expect("oneshot must succeed");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
msg.startup_oneshot_sender
|
|
224
|
+
.send(Ok(DidStart::Started))
|
|
225
|
+
.expect("oneshot started must succeed")
|
|
226
|
+
}
|
|
227
|
+
}
|