@browsersync/bslive 0.0.7 → 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.
Files changed (136) hide show
  1. package/Cargo.toml +30 -2
  2. package/bin.js +3 -2
  3. package/bslive/Cargo.toml +16 -1
  4. package/bslive/build.rs +1 -1
  5. package/bslive/src/lib.rs +120 -4
  6. package/bsnext/Cargo.toml +21 -0
  7. package/bsnext/src/main.rs +110 -0
  8. package/crates/bsnext_client/Cargo.toml +8 -0
  9. package/crates/bsnext_client/build.rs +53 -0
  10. package/crates/bsnext_client/dist/index.js +6493 -0
  11. package/crates/bsnext_client/generated/dto.ts +116 -0
  12. package/crates/bsnext_client/generated/schema.ts +187 -0
  13. package/crates/bsnext_client/index.html +14 -0
  14. package/crates/bsnext_client/package-lock.json +2059 -0
  15. package/crates/bsnext_client/package.json +25 -0
  16. package/crates/bsnext_client/src/lib.rs +1 -0
  17. package/crates/bsnext_client/style.css +3 -0
  18. package/crates/bsnext_client/ts/console.ts +25 -0
  19. package/crates/bsnext_client/ts/index.ts +73 -0
  20. package/crates/bsnext_client/tsconfig.json +16 -0
  21. package/crates/bsnext_core/Cargo.toml +43 -0
  22. package/crates/bsnext_core/src/dir_loader.rs +230 -0
  23. package/crates/bsnext_core/src/dto.rs +281 -0
  24. package/crates/bsnext_core/src/handlers/mod.rs +1 -0
  25. package/crates/bsnext_core/src/handlers/proxy.rs +95 -0
  26. package/crates/bsnext_core/src/lib.rs +11 -0
  27. package/crates/bsnext_core/src/meta/mod.rs +5 -0
  28. package/crates/bsnext_core/src/not_found/mod.rs +2 -0
  29. package/crates/bsnext_core/src/not_found/not_found.html +20 -0
  30. package/crates/bsnext_core/src/not_found/not_found_service.rs +41 -0
  31. package/crates/bsnext_core/src/not_found/route_list.rs +49 -0
  32. package/crates/bsnext_core/src/panic_handler.rs +32 -0
  33. package/crates/bsnext_core/src/raw_loader.rs +226 -0
  34. package/crates/bsnext_core/src/server/actor.rs +92 -0
  35. package/crates/bsnext_core/src/server/error.rs +41 -0
  36. package/crates/bsnext_core/src/server/handler_change.rs +85 -0
  37. package/crates/bsnext_core/src/server/handler_listen.rs +157 -0
  38. package/crates/bsnext_core/src/server/handler_patch.rs +42 -0
  39. package/crates/bsnext_core/src/server/handler_routes_updated.rs +27 -0
  40. package/crates/bsnext_core/src/server/handler_stop.rs +38 -0
  41. package/crates/bsnext_core/src/server/mod.rs +10 -0
  42. package/crates/bsnext_core/src/server/router/mod.rs +112 -0
  43. package/crates/bsnext_core/src/server/router/tests.rs +204 -0
  44. package/crates/bsnext_core/src/server/signals.rs +11 -0
  45. package/crates/bsnext_core/src/server/state.rs +19 -0
  46. package/crates/bsnext_core/src/servers_supervisor/actor.rs +199 -0
  47. package/crates/bsnext_core/src/servers_supervisor/file_changed_handler.rs +41 -0
  48. package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +23 -0
  49. package/crates/bsnext_core/src/servers_supervisor/input_changed_handler.rs +21 -0
  50. package/crates/bsnext_core/src/servers_supervisor/mod.rs +6 -0
  51. package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +82 -0
  52. package/crates/bsnext_core/src/servers_supervisor/stop_handler.rs +26 -0
  53. package/crates/bsnext_core/src/ws/mod.rs +164 -0
  54. package/crates/bsnext_example/Cargo.toml +10 -0
  55. package/crates/bsnext_example/src/basic.rs +51 -0
  56. package/crates/bsnext_example/src/lib.rs +26 -0
  57. package/crates/bsnext_example/src/lit.rs +37 -0
  58. package/crates/bsnext_example/src/md.rs +18 -0
  59. package/crates/bsnext_fs/Cargo.toml +22 -0
  60. package/crates/bsnext_fs/src/actor.rs +122 -0
  61. package/crates/bsnext_fs/src/buffered_debounce.rs +166 -0
  62. package/crates/bsnext_fs/src/filter.rs +30 -0
  63. package/crates/bsnext_fs/src/inner_fs_event_handler.rs +94 -0
  64. package/crates/bsnext_fs/src/lib.rs +141 -0
  65. package/crates/bsnext_fs/src/remove_path_handler.rs +46 -0
  66. package/crates/bsnext_fs/src/stop_handler.rs +15 -0
  67. package/crates/bsnext_fs/src/stream.rs +167 -0
  68. package/crates/bsnext_fs/src/test/mod.rs +213 -0
  69. package/crates/bsnext_fs/src/watch_path_handler.rs +67 -0
  70. package/crates/bsnext_fs/src/watcher.rs +348 -0
  71. package/crates/bsnext_input/Cargo.toml +22 -0
  72. package/crates/bsnext_input/src/input_test/mod.rs +151 -0
  73. package/crates/bsnext_input/src/lib.rs +153 -0
  74. package/crates/bsnext_input/src/md.rs +541 -0
  75. package/crates/bsnext_input/src/paths.rs +64 -0
  76. package/crates/bsnext_input/src/route.rs +185 -0
  77. package/crates/bsnext_input/src/route_manifest.rs +186 -0
  78. package/crates/bsnext_input/src/server_config.rs +88 -0
  79. package/crates/bsnext_input/src/target.rs +7 -0
  80. package/crates/bsnext_input/src/watch_opt_test/mod.rs +68 -0
  81. package/crates/bsnext_input/src/yml.rs +1 -0
  82. package/crates/bsnext_output/Cargo.toml +16 -0
  83. package/crates/bsnext_output/src/json.rs +11 -0
  84. package/crates/bsnext_output/src/lib.rs +25 -0
  85. package/crates/bsnext_output/src/pretty.rs +147 -0
  86. package/crates/bsnext_resp/Cargo.toml +13 -0
  87. package/crates/bsnext_resp/src/js/snippet.html +1 -0
  88. package/crates/bsnext_resp/src/js/ws.js +1 -0
  89. package/crates/bsnext_resp/src/lib.rs +79 -0
  90. package/crates/bsnext_system/Cargo.toml +29 -0
  91. package/crates/bsnext_system/src/args.rs +43 -0
  92. package/crates/bsnext_system/src/lib.rs +227 -0
  93. package/crates/bsnext_system/src/monitor.rs +241 -0
  94. package/crates/bsnext_system/src/monitor_any_watchables.rs +127 -0
  95. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +11 -0
  96. package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +29 -0
  97. package/crates/bsnext_system/src/start_kind/start_from_example.rs +49 -0
  98. package/crates/bsnext_system/src/start_kind/start_from_inputs.rs +67 -0
  99. package/crates/bsnext_system/src/start_kind/start_from_paths.rs +51 -0
  100. package/crates/bsnext_system/src/start_kind.rs +56 -0
  101. package/crates/bsnext_system/src/startup.rs +51 -0
  102. package/crates/bsnext_tracing/Cargo.toml +10 -0
  103. package/crates/bsnext_tracing/src/lib.rs +89 -0
  104. package/examples/basic/input.yml +5 -0
  105. package/examples/basic/public/index.html +15 -0
  106. package/examples/basic/public/reset.css +52 -0
  107. package/examples/basic/public/script.js +1 -0
  108. package/examples/basic/public/styles.css +3 -0
  109. package/examples/kitchen-sink/a-file.md +1 -0
  110. package/examples/kitchen-sink/api/1.json +1 -0
  111. package/examples/kitchen-sink/api/2.json +3 -0
  112. package/examples/kitchen-sink/app.js +1 -0
  113. package/examples/kitchen-sink/input.html +185 -0
  114. package/examples/kitchen-sink/input.yml +133 -0
  115. package/examples/kitchen-sink/styles-2.css +3 -0
  116. package/examples/lit/index.html +21 -0
  117. package/examples/lit/input.yml +5 -0
  118. package/examples/lit/lit.js +82 -0
  119. package/examples/lit/package-lock.json +62 -0
  120. package/examples/lit/package.json +15 -0
  121. package/examples/md-single/frontmatter.md +35 -0
  122. package/examples/md-single/md-single.md +35 -0
  123. package/examples/openai/.nvm +1 -0
  124. package/examples/openai/build.mjs +21 -0
  125. package/examples/openai/index.html +13 -0
  126. package/examples/openai/input.yml +59 -0
  127. package/examples/openai/package-lock.json +720 -0
  128. package/examples/openai/package.json +21 -0
  129. package/examples/openai/src/index.js +21 -0
  130. package/examples/openai/sse/01.txt +8 -0
  131. package/examples/proxy-simple/input.yml +9 -0
  132. package/examples/single/input.yaml +26 -0
  133. package/index.d.ts +2 -0
  134. package/index.js +2 -1
  135. package/package.json +17 -17
  136. package/run.sh +6 -0
@@ -0,0 +1,241 @@
1
+ use crate::BsSystem;
2
+ use actix::{Actor, Addr, AsyncContext};
3
+ use std::hash::Hash;
4
+
5
+ use bsnext_core::dto;
6
+ use bsnext_core::dto::{ExternalEvents, StoppedWatching, Watching};
7
+ use bsnext_core::servers_supervisor::file_changed_handler::{FileChanged, FilesChanged};
8
+ use bsnext_fs::watch_path_handler::RequestWatchPath;
9
+ use bsnext_fs::{Debounce, FsEvent, FsEventContext, FsEventKind};
10
+ use bsnext_input::route::{
11
+ DebounceDuration, DirRoute, FilterKind, RouteKind, Spec, SpecOpts, WatchOpts,
12
+ };
13
+ use bsnext_input::server_config::Identity;
14
+ use bsnext_input::Input;
15
+ use std::path::{Path, PathBuf};
16
+ use std::time::Duration;
17
+
18
+ use bsnext_fs::actor::FsWatcher;
19
+
20
+ use tracing::{span, Level};
21
+
22
+ #[derive(Debug, Clone)]
23
+ pub struct Monitor {
24
+ pub(crate) addr: Addr<FsWatcher>,
25
+ pub(crate) path: PathBuf,
26
+ }
27
+
28
+ #[derive(actix::Message)]
29
+ #[rtype(result = "()")]
30
+ pub struct MonitorInput {
31
+ pub path: PathBuf,
32
+ pub cwd: PathBuf,
33
+ }
34
+
35
+ impl actix::Handler<MonitorInput> for BsSystem {
36
+ type Result = ();
37
+
38
+ fn handle(&mut self, msg: MonitorInput, ctx: &mut Self::Context) -> Self::Result {
39
+ let mut input_watcher = bsnext_fs::actor::FsWatcher::for_input(&msg.cwd, 0);
40
+
41
+ // todo: does this need to be configurable (eg: by main config)?
42
+ input_watcher.with_debounce(Debounce::Trailing {
43
+ duration: Duration::from_millis(300),
44
+ });
45
+
46
+ tracing::trace!("[main.rs] starting input monitor");
47
+
48
+ let input_watcher_addr = input_watcher.start();
49
+ self.input_monitors.push(input_watcher_addr.clone());
50
+
51
+ input_watcher_addr.do_send(RequestWatchPath {
52
+ recipients: vec![ctx.address().recipient()],
53
+ path: msg.path.to_path_buf(),
54
+ });
55
+ }
56
+ }
57
+
58
+ impl actix::Handler<FsEvent> for BsSystem {
59
+ type Result = ();
60
+
61
+ fn handle(&mut self, msg: FsEvent, _ctx: &mut Self::Context) -> Self::Result {
62
+ match msg.kind {
63
+ FsEventKind::ChangeBuffered(buffer_change) => {
64
+ // let id = msg.ctx_id();
65
+ let paths = buffer_change
66
+ .events
67
+ .iter()
68
+ .map(|evt| evt.absolute.to_owned())
69
+ .collect::<Vec<_>>();
70
+ let as_strings = paths
71
+ .iter()
72
+ .map(|p| p.to_string_lossy().to_string())
73
+ .collect::<Vec<String>>();
74
+ if let Some(servers) = &self.servers_addr {
75
+ servers.do_send(FilesChanged {
76
+ paths: paths.clone(),
77
+ id: msg.ctx.id(),
78
+ })
79
+ }
80
+ let evt = ExternalEvents::FilesChanged(dto::FilesChangedDTO { paths: as_strings });
81
+ self.ext_evt(evt);
82
+ }
83
+ FsEventKind::Change(inner) => {
84
+ let span = span!(Level::TRACE, "FsWatchEvent", ?inner.absolute_path);
85
+ let _guard = span.enter();
86
+ match msg.ctx {
87
+ FsEventContext::InputFile { id: _ } => {
88
+ tracing::info!("InputFile file changed");
89
+ let input = Input::from_input_path(&inner.absolute_path);
90
+
91
+ let Ok(input) = input else {
92
+ tracing::debug!("ignoring FsWatchEvent because the input was invalid");
93
+ let err = input.unwrap_err();
94
+ tracing::error!("{}", err);
95
+ return;
96
+ };
97
+
98
+ self.accept_input(&input);
99
+ self.inform_servers(input);
100
+
101
+ let evt = ExternalEvents::InputFileChanged(
102
+ dto::FileChanged::from_path_buf(&inner.path),
103
+ );
104
+ self.ext_evt(evt);
105
+ }
106
+ FsEventContext::Other { id } => {
107
+ tracing::trace!("Other");
108
+ // todo: tie these changed to an input identity?
109
+ if let Some(servers) = &self.servers_addr {
110
+ servers.do_send(FileChanged {
111
+ path: inner.absolute_path.clone(),
112
+ id,
113
+ })
114
+ }
115
+ let evt = ExternalEvents::FileChanged(dto::FileChanged::from_path_buf(
116
+ &inner.path,
117
+ ));
118
+
119
+ self.ext_evt(evt);
120
+ }
121
+ }
122
+ }
123
+ FsEventKind::PathAdded(path) => {
124
+ tracing::trace!(?path.path, "path added");
125
+ let evt =
126
+ ExternalEvents::Watching(Watching::from_path_buf(&path.path, path.debounce));
127
+ self.ext_evt(evt);
128
+ }
129
+ FsEventKind::PathRemoved(path) => {
130
+ tracing::trace!(?path.path, "path removed");
131
+ let evt =
132
+ ExternalEvents::WatchingStopped(StoppedWatching::from_path_buf(&path.path));
133
+ self.ext_evt(evt);
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Clone)]
140
+ pub enum AnyWatchable {
141
+ Server(ServerWatchable),
142
+ Route(RouteWatchable),
143
+ }
144
+
145
+ impl AnyWatchable {
146
+ pub fn spec_opts(&self) -> Option<&SpecOpts> {
147
+ match self {
148
+ AnyWatchable::Server(server) => server.spec.opts.as_ref(),
149
+ AnyWatchable::Route(route) => route.spec.opts.as_ref(),
150
+ }
151
+ }
152
+ pub fn watch_path(&self) -> &Path {
153
+ match self {
154
+ AnyWatchable::Server(server) => &server.dir,
155
+ AnyWatchable::Route(route) => &route.dir,
156
+ }
157
+ }
158
+ }
159
+
160
+ #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Clone)]
161
+ pub struct ServerWatchable {
162
+ pub server_identity: Identity,
163
+ pub dir: PathBuf,
164
+ pub spec: Spec,
165
+ }
166
+
167
+ #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Clone)]
168
+ pub struct RouteWatchable {
169
+ pub server_identity: Identity,
170
+ pub route_path: String,
171
+ pub dir: PathBuf,
172
+ pub spec: Spec,
173
+ }
174
+
175
+ pub fn to_server_watchables(input: &Input) -> Vec<ServerWatchable> {
176
+ input
177
+ .servers
178
+ .iter()
179
+ .flat_map(|server_config| {
180
+ server_config.watchers.iter().map(|watcher| {
181
+ // let spec = to_spec(&r.watch_opts);
182
+ ServerWatchable {
183
+ server_identity: server_config.identity.clone(),
184
+ dir: PathBuf::from(&watcher.dir),
185
+ spec: Spec {
186
+ opts: Some(SpecOpts {
187
+ debounce: watcher.debounce_ms.map(DebounceDuration::Ms),
188
+ filter: watcher.filter.clone(),
189
+ }),
190
+ },
191
+ }
192
+ })
193
+ })
194
+ .collect()
195
+ }
196
+
197
+ pub fn to_route_watchables(input: &Input) -> Vec<RouteWatchable> {
198
+ input
199
+ .servers
200
+ .iter()
201
+ .flat_map(|server_config| {
202
+ server_config
203
+ .routes
204
+ .iter()
205
+ .filter(|r| r.watch_opts.is_enabled())
206
+ .filter_map(|r| match &r.kind {
207
+ RouteKind::Html { .. } => None,
208
+ RouteKind::Json { .. } => None,
209
+ RouteKind::Raw { .. } => None,
210
+ RouteKind::Sse { .. } => None,
211
+ RouteKind::Proxy(_) => None,
212
+ RouteKind::Dir(DirRoute { dir }) => {
213
+ let spec = to_spec(&r.watch_opts);
214
+ Some(RouteWatchable {
215
+ server_identity: server_config.identity.clone(),
216
+ route_path: r.path.to_string(),
217
+ dir: PathBuf::from(dir),
218
+ spec,
219
+ })
220
+ }
221
+ })
222
+ })
223
+ .collect()
224
+ }
225
+
226
+ fn to_spec(wo: &WatchOpts) -> Spec {
227
+ match wo {
228
+ WatchOpts::Bool(enabled) if !*enabled => unreachable!("should be handled..."),
229
+ WatchOpts::Bool(enabled) if *enabled => Spec { opts: None },
230
+ WatchOpts::InlineGlob(glob) => Spec {
231
+ opts: Some(SpecOpts {
232
+ debounce: None,
233
+ filter: Some(FilterKind::Glob {
234
+ glob: glob.to_string(),
235
+ }),
236
+ }),
237
+ },
238
+ WatchOpts::Spec(spec) => spec.to_owned(),
239
+ WatchOpts::Bool(_) => todo!("unreachable"),
240
+ }
241
+ }
@@ -0,0 +1,127 @@
1
+ use crate::monitor::{AnyWatchable, Monitor};
2
+ use crate::BsSystem;
3
+ use actix::{Actor, AsyncContext};
4
+ use bsnext_fs::actor::FsWatcher;
5
+ use bsnext_fs::filter::Filter;
6
+ use bsnext_fs::stop_handler::StopWatcher;
7
+ use bsnext_fs::watch_path_handler::RequestWatchPath;
8
+ use bsnext_fs::Debounce;
9
+ use bsnext_input::route::{DebounceDuration, FilterKind, SpecOpts};
10
+ use std::collections::BTreeSet;
11
+ use std::path::PathBuf;
12
+ use std::time::Duration;
13
+
14
+ #[derive(actix::Message)]
15
+ #[rtype(result = "()")]
16
+ pub struct MonitorAnyWatchables {
17
+ pub watchables: Vec<AnyWatchable>,
18
+ pub cwd: PathBuf,
19
+ }
20
+
21
+ impl actix::Handler<MonitorAnyWatchables> for BsSystem {
22
+ type Result = ();
23
+
24
+ fn handle(&mut self, msg: MonitorAnyWatchables, ctx: &mut Self::Context) -> Self::Result {
25
+ tracing::debug!("MonitorAnyWatchables {:?}", msg.watchables);
26
+ tracing::trace!("MonitorAnyWatchables {:#?}", msg.watchables);
27
+
28
+ let existing = self.any_monitors.keys().collect::<BTreeSet<_>>();
29
+ let incoming = msg.watchables.iter().collect::<BTreeSet<_>>();
30
+
31
+ let in_both = incoming.intersection(&existing).collect::<Vec<_>>();
32
+ let to_add = incoming.difference(&existing).collect::<Vec<_>>();
33
+ let to_remove = existing.difference(&incoming).collect::<Vec<_>>();
34
+
35
+ tracing::debug!("{} watchables exist in current + incoming", in_both.len());
36
+ tracing::debug!("removing {} watchables", to_remove.len());
37
+
38
+ for any_watchable in to_remove {
39
+ if let Some(mon) = self.any_monitors.get(any_watchable) {
40
+ mon.addr.do_send(StopWatcher);
41
+ ctx.notify(DropMonitor((*any_watchable).clone()))
42
+ }
43
+ }
44
+
45
+ tracing::debug!("adding {} new watchables", to_add.len());
46
+ for watchable in to_add {
47
+ let mut input_watcher = match watchable {
48
+ AnyWatchable::Server(server_watchable) => {
49
+ FsWatcher::new(&msg.cwd, server_watchable.server_identity.as_id())
50
+ }
51
+ AnyWatchable::Route(route_watchable) => {
52
+ FsWatcher::new(&msg.cwd, route_watchable.server_identity.as_id())
53
+ }
54
+ };
55
+
56
+ if let Some(opts) = &watchable.spec_opts() {
57
+ // todo: handle the specs here!
58
+ if let Some(filter) = &opts.filter {
59
+ let f = match filter {
60
+ FilterKind::Extension { ext } => Filter::Extension {
61
+ ext: ext.to_string(),
62
+ },
63
+ FilterKind::StringGlob(glob) => Filter::Glob {
64
+ glob: glob.to_owned(),
65
+ },
66
+ FilterKind::Glob { glob } => Filter::Glob {
67
+ glob: glob.to_owned(),
68
+ },
69
+ };
70
+ input_watcher.with_filter(f);
71
+ }
72
+ }
73
+
74
+ let duration = match &watchable.spec_opts() {
75
+ Some(SpecOpts {
76
+ debounce: Some(DebounceDuration::Ms(ms)),
77
+ ..
78
+ }) => Duration::from_millis(*ms),
79
+ _ => Duration::from_millis(300),
80
+ };
81
+
82
+ input_watcher.with_debounce(Debounce::Buffered { duration });
83
+
84
+ let input_watcher_addr = input_watcher.start();
85
+
86
+ let monitor = Monitor {
87
+ addr: input_watcher_addr.clone(),
88
+ path: watchable.watch_path().to_path_buf(),
89
+ };
90
+
91
+ monitor.addr.do_send(RequestWatchPath {
92
+ recipients: vec![ctx.address().recipient()],
93
+ path: monitor.path.clone(),
94
+ });
95
+
96
+ ctx.notify(InsertMonitor((*watchable).clone(), monitor))
97
+ }
98
+ }
99
+ }
100
+
101
+ #[derive(actix::Message)]
102
+ #[rtype(result = "()")]
103
+ struct DropMonitor(AnyWatchable);
104
+
105
+ impl actix::Handler<DropMonitor> for BsSystem {
106
+ type Result = ();
107
+
108
+ fn handle(&mut self, msg: DropMonitor, _ctx: &mut Self::Context) -> Self::Result {
109
+ tracing::trace!(watchable=?msg.0, "DropMonitor");
110
+ self.any_monitors.remove(&msg.0);
111
+ tracing::trace!("dropped, Monitor count {}", self.any_monitors.len());
112
+ }
113
+ }
114
+
115
+ #[derive(actix::Message)]
116
+ #[rtype(result = "()")]
117
+ struct InsertMonitor(AnyWatchable, Monitor);
118
+
119
+ impl actix::Handler<InsertMonitor> for BsSystem {
120
+ type Result = ();
121
+
122
+ fn handle(&mut self, msg: InsertMonitor, _ctx: &mut Self::Context) -> Self::Result {
123
+ tracing::trace!(watchable=?msg.0, "InsertMonitor");
124
+ self.any_monitors.insert(msg.0, msg.1);
125
+ tracing::trace!("inserted, Monitor count {}", self.any_monitors.len());
126
+ }
127
+ }
@@ -0,0 +1,11 @@
1
+ ---
2
+ source: bsnext_system/src/start_kind/start_from_paths.rs
3
+ expression: input
4
+ ---
5
+ servers:
6
+ - bind_address: "0.0.0.0:3000"
7
+ routes:
8
+ - path: /
9
+ watch: true
10
+ dir: "."
11
+ watchers: []
@@ -0,0 +1,29 @@
1
+ ---
2
+ source: bsnext_system/src/start_kind/start_from_paths.rs
3
+ expression: input
4
+ ---
5
+ Input {
6
+ servers: [
7
+ ServerConfig {
8
+ identity: Address {
9
+ bind_address: "0.0.0.0:3000",
10
+ },
11
+ routes: [
12
+ Route {
13
+ path: "/",
14
+ cors_opts: None,
15
+ delay_opts: None,
16
+ watch_opts: Bool(
17
+ true,
18
+ ),
19
+ kind: Dir(
20
+ DirRoute {
21
+ dir: ".",
22
+ },
23
+ ),
24
+ },
25
+ ],
26
+ watchers: [],
27
+ },
28
+ ],
29
+ }
@@ -0,0 +1,49 @@
1
+ use crate::startup::{StartupContext, SystemStart};
2
+ use bsnext_example::Example;
3
+ use bsnext_input::server_config::Identity;
4
+ use bsnext_input::target::TargetKind;
5
+ use bsnext_input::{fs_write_input, rand_word, DirError, Input, InputError};
6
+ use std::fs;
7
+ use std::path::PathBuf;
8
+
9
+ #[derive(Debug)]
10
+ pub struct StartFromExample {
11
+ pub example: Example,
12
+ pub write_input: bool,
13
+ pub target_kind: TargetKind,
14
+ pub port: Option<u16>,
15
+ pub temp: bool,
16
+ pub name: Option<String>,
17
+ }
18
+
19
+ impl SystemStart for StartFromExample {
20
+ fn input(&self, ctx: &StartupContext) -> Result<(Input, Option<PathBuf>), InputError> {
21
+ let identity = Identity::from_port_or_named(self.port)?;
22
+ let input = self.example.into_input(identity);
23
+ let name = self.name.clone();
24
+ let dir = if self.temp {
25
+ let temp_dir = std::env::temp_dir();
26
+ let word = name.unwrap_or_else(rand_word);
27
+ let num = rand::random::<f64>();
28
+ let next_dir = temp_dir.join(format!("bslive-{word}-{num}"));
29
+ fs::create_dir_all(&next_dir)
30
+ .map_err(|_e| DirError::CannotCreate {
31
+ path: next_dir.clone(),
32
+ })
33
+ .and_then(|_pb| {
34
+ std::env::set_current_dir(&next_dir).map_err(|_e| DirError::CannotMove {
35
+ path: next_dir.clone(),
36
+ })
37
+ })
38
+ .map(|_| next_dir.clone())?
39
+ } else {
40
+ ctx.cwd.to_path_buf()
41
+ };
42
+ if self.write_input {
43
+ let path = fs_write_input(&dir, &input, self.target_kind.clone())?;
44
+ Ok((input, Some(path)))
45
+ } else {
46
+ Ok((input, None))
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,67 @@
1
+ use crate::startup::{StartupContext, SystemStart};
2
+
3
+ use bsnext_input::{Input, InputError};
4
+ use std::path::{Path, PathBuf};
5
+
6
+ #[derive(Debug)]
7
+ pub struct StartFromInputPaths {
8
+ pub input_paths: Vec<String>,
9
+ }
10
+
11
+ impl SystemStart for StartFromInputPaths {
12
+ fn input(&self, ctx: &StartupContext) -> Result<(Input, Option<PathBuf>), InputError> {
13
+ let (input, path) = from_yml_paths(&ctx.cwd, &self.input_paths)?;
14
+ Ok((input, Some(path)))
15
+ }
16
+ }
17
+
18
+ fn from_yml_paths<T: AsRef<str>>(cwd: &Path, inputs: &[T]) -> Result<(Input, PathBuf), InputError> {
19
+ let input_candidates = inputs
20
+ .iter()
21
+ .map(|path| cwd.join(path.as_ref()))
22
+ .collect::<Vec<PathBuf>>();
23
+
24
+ let lookups = ["input.yml", "input.yaml"]
25
+ .iter()
26
+ .map(|path| cwd.join(path))
27
+ .collect::<Vec<PathBuf>>();
28
+
29
+ let auto_candidates = lookups
30
+ .iter()
31
+ .filter(|path| Path::exists(path))
32
+ .collect::<Vec<&PathBuf>>();
33
+
34
+ let exists = input_candidates
35
+ .iter()
36
+ .filter(|path| Path::exists(path))
37
+ .collect::<Vec<&PathBuf>>();
38
+
39
+ let missing = input_candidates
40
+ .iter()
41
+ .filter(|path| !Path::exists(path))
42
+ .collect::<Vec<&PathBuf>>();
43
+
44
+ if !missing.is_empty() {
45
+ for path in &missing {
46
+ tracing::error!(?path, "input file not found");
47
+ }
48
+ return Err(InputError::NotFound(
49
+ missing.first().expect("guarded").to_path_buf(),
50
+ ));
51
+ }
52
+
53
+ let first_user = exists.first();
54
+ let first_auto = auto_candidates.first();
55
+
56
+ let Some(input_path) = first_user.or(first_auto) else {
57
+ return Err(InputError::MissingInputs);
58
+ };
59
+
60
+ tracing::info!(?input_path);
61
+
62
+ let result = Input::from_input_path(input_path);
63
+ match result {
64
+ Ok(input) => Ok((input, input_path.to_path_buf())),
65
+ Err(e) => Err(InputError::InvalidInput(e.to_string())),
66
+ }
67
+ }
@@ -0,0 +1,51 @@
1
+ use crate::startup::{StartupContext, SystemStart};
2
+ use bsnext_input::paths::from_paths;
3
+ use bsnext_input::server_config::Identity;
4
+ use bsnext_input::target::TargetKind;
5
+ use bsnext_input::{fs_write_input, Input, InputError};
6
+ use std::path::PathBuf;
7
+
8
+ #[derive(Debug)]
9
+ pub struct StartFromPaths {
10
+ pub paths: Vec<String>,
11
+ pub write_input: bool,
12
+ pub port: Option<u16>,
13
+ }
14
+
15
+ impl SystemStart for StartFromPaths {
16
+ fn input(&self, ctx: &StartupContext) -> Result<(Input, Option<PathBuf>), InputError> {
17
+ let identity = Identity::from_port_or_named(self.port)?;
18
+ let input = from_paths(&ctx.cwd, &self.paths, identity)?;
19
+ if self.write_input {
20
+ let path = fs_write_input(&ctx.cwd, &input, TargetKind::Yaml)?;
21
+
22
+ Ok((input, Some(path)))
23
+ } else {
24
+ Ok((input, None))
25
+ }
26
+ }
27
+ }
28
+
29
+ #[cfg(test)]
30
+ mod test {
31
+ use super::*;
32
+ #[test]
33
+ fn test() -> anyhow::Result<()> {
34
+ use tempfile::tempdir;
35
+ let tmp_dir = tempdir()?;
36
+ let v = StartFromPaths {
37
+ paths: vec![".".into()],
38
+ write_input: false,
39
+ port: Some(3000),
40
+ };
41
+ let ctx = StartupContext {
42
+ cwd: tmp_dir.path().to_path_buf(),
43
+ };
44
+ let i = v.input(&ctx);
45
+ tmp_dir.close()?;
46
+ let (input, _) = i.unwrap();
47
+ insta::assert_debug_snapshot!(input);
48
+ insta::assert_yaml_snapshot!(input);
49
+ Ok(())
50
+ }
51
+ }
@@ -0,0 +1,56 @@
1
+ use crate::args::Args;
2
+ use crate::start_kind::start_from_example::StartFromExample;
3
+ use crate::start_kind::start_from_inputs::StartFromInputPaths;
4
+ use crate::start_kind::start_from_paths::StartFromPaths;
5
+ use crate::startup::{StartupContext, SystemStart};
6
+
7
+ use bsnext_input::{Input, InputError};
8
+ use std::path::PathBuf;
9
+
10
+ pub mod start_from_example;
11
+ pub mod start_from_inputs;
12
+ pub mod start_from_paths;
13
+
14
+ #[derive(Debug)]
15
+ pub enum StartKind {
16
+ FromInputs(StartFromInputPaths),
17
+ FromExample(StartFromExample),
18
+ FromPaths(StartFromPaths),
19
+ }
20
+
21
+ impl StartKind {
22
+ pub fn from_args(args: Args) -> Self {
23
+ if let Some(example) = args.example {
24
+ return StartKind::FromExample(StartFromExample {
25
+ example,
26
+ write_input: args.write,
27
+ port: args.port,
28
+ temp: args.temp,
29
+ name: args.name,
30
+ target_kind: args.target.unwrap_or_default(),
31
+ });
32
+ }
33
+
34
+ if !args.paths.is_empty() {
35
+ StartKind::FromPaths(StartFromPaths {
36
+ paths: args.paths,
37
+ write_input: args.write,
38
+ port: args.port,
39
+ })
40
+ } else {
41
+ StartKind::FromInputs(StartFromInputPaths {
42
+ input_paths: args.input.clone(),
43
+ })
44
+ }
45
+ }
46
+ }
47
+
48
+ impl SystemStart for StartKind {
49
+ fn input(&self, ctx: &StartupContext) -> Result<(Input, Option<PathBuf>), InputError> {
50
+ match self {
51
+ Self::FromInputs(from_inputs) => from_inputs.input(ctx),
52
+ Self::FromExample(from_example) => from_example.input(ctx),
53
+ Self::FromPaths(from_paths) => from_paths.input(ctx),
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,51 @@
1
+ use bsnext_input::{Input, InputError};
2
+ use std::env::current_dir;
3
+ use std::path::PathBuf;
4
+
5
+ pub type StartupResult = Result<DidStart, StartupError>;
6
+
7
+ pub struct Startup {
8
+ pub tasks: Vec<StartupTask>,
9
+ }
10
+ #[derive(Debug)]
11
+ pub struct StartupContext {
12
+ pub cwd: PathBuf,
13
+ }
14
+
15
+ impl StartupContext {
16
+ pub fn from_cwd(cwd: Option<&PathBuf>) -> Self {
17
+ StartupContext {
18
+ cwd: cwd.map(ToOwned::to_owned).unwrap_or_else(|| {
19
+ PathBuf::from(
20
+ current_dir()
21
+ .expect("if current_dir fails, nothing can work")
22
+ .to_string_lossy()
23
+ .to_string(),
24
+ )
25
+ }),
26
+ }
27
+ }
28
+ }
29
+
30
+ pub trait SystemStart {
31
+ fn input(&self, ctx: &StartupContext) -> Result<(Input, Option<PathBuf>), InputError>;
32
+ }
33
+
34
+ impl Default for StartupContext {
35
+ fn default() -> Self {
36
+ Self::from_cwd(None)
37
+ }
38
+ }
39
+
40
+ #[derive(Debug)]
41
+ pub enum DidStart {
42
+ Started,
43
+ }
44
+
45
+ #[derive(Debug, thiserror::Error)]
46
+ pub enum StartupError {
47
+ #[error("An input error prevented startup")]
48
+ InputError(#[from] InputError),
49
+ }
50
+
51
+ pub enum StartupTask {}
@@ -0,0 +1,10 @@
1
+ [package]
2
+ name = "bsnext_tracing"
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-subscriber = { workspace = true }
10
+ clap = { workspace = true }