@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.
Files changed (137) hide show
  1. package/Cargo.toml +32 -18
  2. package/bin.js +6 -0
  3. package/bslive/Cargo.toml +35 -0
  4. package/{build.rs → bslive/build.rs} +1 -1
  5. package/bslive/src/lib.rs +130 -0
  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 +3 -1
  134. package/index.js +3 -2
  135. package/package.json +22 -19
  136. package/run.sh +6 -0
  137. 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,7 @@
1
+ #[derive(Debug, Clone, Default, clap::ValueEnum)]
2
+ pub enum TargetKind {
3
+ #[default]
4
+ Yaml,
5
+ Toml,
6
+ Md,
7
+ }
@@ -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
+ }