diff --git a/Cargo.lock b/Cargo.lock
index 0ee08bf..c0a8474 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -525,6 +525,7 @@ dependencies = [
name = "bsnext_example"
version = "0.2.2"
dependencies = [
+ "bsnext_html",
"bsnext_input",
"bsnext_md",
"clap",
@@ -548,6 +549,18 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "bsnext_html"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bsnext_input",
+ "indent",
+ "insta",
+ "scraper",
+ "unindent",
+]
+
[[package]]
name = "bsnext_input"
version = "0.2.2"
@@ -632,6 +645,7 @@ dependencies = [
"bsnext_dto",
"bsnext_example",
"bsnext_fs",
+ "bsnext_html",
"bsnext_input",
"bsnext_md",
"bsnext_output",
@@ -932,6 +946,29 @@ dependencies = [
"typenum",
]
+[[package]]
+name = "cssparser"
+version = "0.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa",
+ "phf",
+ "smallvec",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "ctor"
version = "0.2.8"
@@ -957,6 +994,17 @@ dependencies = [
"powerfmt",
]
+[[package]]
+name = "derive_more"
+version = "0.99.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "digest"
version = "0.10.7"
@@ -967,6 +1015,27 @@ dependencies = [
"crypto-common",
]
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
+name = "ego-tree"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53"
+
[[package]]
name = "either"
version = "1.12.0"
@@ -1072,6 +1141,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
[[package]]
name = "futures"
version = "0.3.30"
@@ -1161,6 +1240,15 @@ dependencies = [
"slab",
]
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -1171,6 +1259,15 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -1259,6 +1356,20 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+[[package]]
+name = "html5ever"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "http"
version = "1.1.0"
@@ -1580,6 +1691,12 @@ dependencies = [
"hashbrown",
]
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
[[package]]
name = "markdown"
version = "1.0.0-alpha.17"
@@ -1589,6 +1706,20 @@ dependencies = [
"unicode-id",
]
+[[package]]
+name = "markup5ever"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5"
+dependencies = [
+ "log",
+ "phf",
+ "phf_codegen",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
[[package]]
name = "matchers"
version = "0.1.0"
@@ -1761,6 +1892,12 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -1959,6 +2096,77 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+[[package]]
+name = "phf"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.11.2",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
+dependencies = [
+ "phf_generator 0.11.2",
+ "phf_shared 0.11.2",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
+dependencies = [
+ "phf_shared 0.11.2",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
+dependencies = [
+ "phf_generator 0.11.2",
+ "phf_shared 0.11.2",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
+dependencies = [
+ "siphasher",
+]
+
[[package]]
name = "pin-project"
version = "1.1.5"
@@ -2022,6 +2230,12 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
[[package]]
name = "proc-macro2"
version = "1.0.81"
@@ -2284,6 +2498,22 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+[[package]]
+name = "scraper"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208"
+dependencies = [
+ "ahash",
+ "cssparser",
+ "ego-tree",
+ "getopts",
+ "html5ever",
+ "precomputed-hash",
+ "selectors",
+ "tendril",
+]
+
[[package]]
name = "sct"
version = "0.7.1"
@@ -2317,6 +2547,25 @@ dependencies = [
"libc",
]
+[[package]]
+name = "selectors"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8"
+dependencies = [
+ "bitflags 2.5.0",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "new_debug_unreachable",
+ "phf",
+ "phf_codegen",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+]
+
[[package]]
name = "semver"
version = "1.0.22"
@@ -2398,6 +2647,15 @@ dependencies = [
"unsafe-libyaml",
]
+[[package]]
+name = "servo_arc"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a"
+dependencies = [
+ "stable_deref_trait",
+]
+
[[package]]
name = "sha1"
version = "0.10.6"
@@ -2454,6 +2712,12 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
[[package]]
name = "slab"
version = "0.4.9"
@@ -2501,12 +2765,44 @@ dependencies = [
"syn",
]
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "string_cache"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
+dependencies = [
+ "new_debug_unreachable",
+ "once_cell",
+ "parking_lot",
+ "phf_shared 0.10.0",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+ "proc-macro2",
+ "quote",
+]
+
[[package]]
name = "strip-ansi-escapes"
version = "0.2.0"
@@ -2622,6 +2918,17 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
[[package]]
name = "terminal_size"
version = "0.3.0"
@@ -3141,6 +3448,12 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+[[package]]
+name = "unindent"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
+
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
diff --git a/bslive.html b/bslive.html
new file mode 100644
index 0000000..6222540
--- /dev/null
+++ b/bslive.html
@@ -0,0 +1,23 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/crates/bsnext_dto/src/lib.rs b/crates/bsnext_dto/src/lib.rs
index 2227ca0..08b08ca 100644
--- a/crates/bsnext_dto/src/lib.rs
+++ b/crates/bsnext_dto/src/lib.rs
@@ -287,6 +287,7 @@ pub enum InputErrorDTO {
DirError(String),
YamlError(String),
MarkdownError(String),
+ HtmlError(String),
Io(String),
UnsupportedExtension(String),
MissingExtension(String),
@@ -305,6 +306,7 @@ impl From<&InputError> for InputErrorDTO {
e @ InputError::PortError(_) => InputErrorDTO::PortError(e.to_string()),
e @ InputError::DirError(_) => InputErrorDTO::DirError(e.to_string()),
e @ InputError::MarkdownError(_) => InputErrorDTO::MarkdownError(e.to_string()),
+ e @ InputError::HtmlError(_) => InputErrorDTO::HtmlError(e.to_string()),
e @ InputError::YamlError(_) => InputErrorDTO::YamlError(e.to_string()),
e @ InputError::Io(_) => InputErrorDTO::Io(e.to_string()),
e @ InputError::UnsupportedExtension(_) => {
diff --git a/crates/bsnext_example/Cargo.toml b/crates/bsnext_example/Cargo.toml
index caf28d6..b6922b7 100644
--- a/crates/bsnext_example/Cargo.toml
+++ b/crates/bsnext_example/Cargo.toml
@@ -8,4 +8,5 @@ edition = "2021"
[dependencies]
bsnext_input = { path = "../bsnext_input" }
bsnext_md = { path = "../bsnext_md" }
+bsnext_html = { path = "../bsnext_html" }
clap = { workspace = true }
diff --git a/crates/bsnext_example/src/md.rs b/crates/bsnext_example/src/md.rs
index 2c77b9a..ace7250 100644
--- a/crates/bsnext_example/src/md.rs
+++ b/crates/bsnext_example/src/md.rs
@@ -1,6 +1,6 @@
use bsnext_input::server_config::ServerIdentity;
-use bsnext_input::{InputSource, InputSourceKind};
-use bsnext_md::md_to_input;
+use bsnext_input::{InputCreation, InputSource, InputSourceKind};
+use bsnext_md::md_fs::MdFs;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct MdExample;
@@ -8,7 +8,8 @@ pub struct MdExample;
impl InputSource for MdExample {
fn into_input(self, identity: Option) -> InputSourceKind {
let input_str = include_str!("../../../examples/markdown/single.md");
- let mut input = md_to_input(input_str).expect("example cannot fail?");
+ let mut input =
+ MdFs::from_input_str(input_str, &Default::default()).expect("example cannot fail?");
let server = input
.servers
.first_mut()
diff --git a/crates/bsnext_example/src/playground.rs b/crates/bsnext_example/src/playground.rs
index f267313..ad92e50 100644
--- a/crates/bsnext_example/src/playground.rs
+++ b/crates/bsnext_example/src/playground.rs
@@ -1,14 +1,14 @@
+use bsnext_html::HtmlFs;
use bsnext_input::server_config::ServerIdentity;
-use bsnext_input::{InputCreation, InputSource, InputSourceKind};
-use bsnext_md::md_fs::MdFs;
+use bsnext_input::{InputCreation, InputCtx, InputSource, InputSourceKind};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PlaygroundExample;
impl InputSource for PlaygroundExample {
fn into_input(self, identity: Option) -> InputSourceKind {
- let input_str = include_str!("../../../examples/markdown/playground.md");
- let mut input = MdFs::from_input_str(input_str).unwrap();
+ let input_str = include_str!("../../../examples/html/playground.html");
+ let mut input = HtmlFs::from_input_str(input_str, &InputCtx::default()).unwrap();
// update the server identity if it was provided
if let (Some(server), Some(identity)) = (input.servers.get_mut(0), identity) {
diff --git a/crates/bsnext_html/Cargo.toml b/crates/bsnext_html/Cargo.toml
new file mode 100644
index 0000000..bb874e8
--- /dev/null
+++ b/crates/bsnext_html/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "bsnext_html"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+bsnext_input = { path = "../bsnext_input" }
+unindent = "0.2.3"
+indent = "0.1.1"
+scraper = "0.21.0"
+
+[dev-dependencies]
+anyhow = { workspace = true }
+insta = { workspace = true }
diff --git a/crates/bsnext_html/src/html_writer.rs b/crates/bsnext_html/src/html_writer.rs
new file mode 100644
index 0000000..62623d6
--- /dev/null
+++ b/crates/bsnext_html/src/html_writer.rs
@@ -0,0 +1,54 @@
+use bsnext_input::playground::Playground;
+use bsnext_input::server_config::ServerConfig;
+use bsnext_input::{Input, InputWriter};
+
+pub struct HtmlWriter;
+
+impl InputWriter for HtmlWriter {
+ fn input_to_str(&self, input: &Input) -> String {
+ if input.servers.is_empty() {
+ todo!("html requires at least 1 server definition")
+ }
+ if input.servers.len() > 1 {
+ todo!("more than 1 server not supported yet")
+ }
+ let server = input.servers.first().expect("must access first");
+ let Some(playground) = &server.playground else {
+ todo!("only playground is supported in HTML for now")
+ };
+ let mut blocks: Vec = vec![];
+ if let Some(css) = &playground.css {
+ blocks.push("".into());
+ }
+ blocks.push(playground.html.clone());
+ if let Some(js) = &playground.js {
+ blocks.push("".into());
+ }
+ blocks.join("\n")
+ }
+}
+
+#[test]
+fn test_html_writer_for_playground() {
+ let css = r#"body {
+ background: red;
+}"#;
+ let js = r#"console.log("hello world!")"#;
+ let playground = Playground {
+ html: "Hello world
".to_string(),
+ js: Some(js.to_string()),
+ css: Some(css.to_string()),
+ };
+ let mut input = Input::default();
+ let mut server = ServerConfig::default();
+ server.playground = Some(playground);
+ input.servers.push(server);
+ let output = HtmlWriter.input_to_str(&input);
+ insta::assert_snapshot!(output);
+}
diff --git a/crates/bsnext_html/src/lib.rs b/crates/bsnext_html/src/lib.rs
new file mode 100644
index 0000000..66708c6
--- /dev/null
+++ b/crates/bsnext_html/src/lib.rs
@@ -0,0 +1,96 @@
+use bsnext_input::playground::Playground;
+use bsnext_input::server_config::{ServerConfig, ServerIdentity};
+use bsnext_input::{Input, InputCreation, InputCtx, InputError};
+use std::fs::read_to_string;
+use std::path::Path;
+
+pub mod html_writer;
+
+pub struct HtmlFs;
+
+impl InputCreation for HtmlFs {
+ fn from_input_path>(path: P, ctx: &InputCtx) -> Result> {
+ let str = read_to_string(path).map_err(|e| Box::new(e.into()))?;
+ let input = playground_html_str_to_input(&str, ctx)
+ .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?;
+ Ok(input)
+ }
+
+ fn from_input_str>(content: P, ctx: &InputCtx) -> Result> {
+ let input = playground_html_str_to_input(&content.as_ref(), ctx)
+ .map_err(|e| Box::new(InputError::HtmlError(e.to_string())))?;
+ Ok(input)
+ }
+}
+
+fn playground_html_str_to_input(html: &str, ctx: &InputCtx) -> Result> {
+ use unindent::unindent;
+
+ // parse the HTML
+ let mut document = scraper::Html::parse_fragment(html);
+
+ let style = scraper::Selector::parse("style:first-of-type").unwrap();
+ let script = scraper::Selector::parse("script:first-of-type").unwrap();
+
+ let mut style_elems = document.select(&style);
+ let mut script_elems = document.select(&script);
+ let mut node_ids_to_remove = vec![];
+
+ // start an empty playground
+ let mut playground = Playground {
+ html: "".to_string(),
+ css: None,
+ js: None,
+ };
+
+ if let Some(style) = style_elems.next() {
+ node_ids_to_remove.push(style.id());
+ let t = style.text().nth(0).unwrap();
+ let unindented = unindent(t);
+ playground.css = Some(unindented);
+ }
+
+ if let Some(script) = script_elems.next() {
+ node_ids_to_remove.push(script.id());
+ let t = script.text().nth(0).unwrap();
+ let unindented = unindent(t);
+ playground.js = Some(unindented);
+ }
+
+ for node_id in node_ids_to_remove {
+ document.tree.get_mut(node_id).unwrap().detach();
+ }
+
+ // grab the HTML
+ let as_html = document.html();
+ let trimmed = as_html
+ .strip_prefix("")
+ .unwrap()
+ .strip_suffix("")
+ .unwrap();
+ let un_indented = unindent(trimmed);
+ playground.html = un_indented;
+
+ // Now start to build up the input
+ let mut input = Input::default();
+
+ // 1: first try prev
+ // 2: next try if 'port' was provided
+ // 3: finally, make one up
+ let iden = ctx
+ .first_id()
+ .or_else(|| ServerIdentity::from_port_or_named(ctx.port()).ok())
+ .unwrap_or_default();
+
+ // Create the server
+ let server = ServerConfig {
+ identity: iden,
+ playground: Some(playground),
+ ..Default::default()
+ };
+
+ // Add it to the input
+ input.servers.push(server);
+
+ Ok(input)
+}
diff --git a/crates/bsnext_html/src/snapshots/bsnext_html__html_writer__html_writer_for_playground.snap b/crates/bsnext_html/src/snapshots/bsnext_html__html_writer__html_writer_for_playground.snap
new file mode 100644
index 0000000..01cb230
--- /dev/null
+++ b/crates/bsnext_html/src/snapshots/bsnext_html__html_writer__html_writer_for_playground.snap
@@ -0,0 +1,13 @@
+---
+source: crates/bsnext_html/src/html_writer.rs
+expression: output
+---
+
+Hello world
+
diff --git a/crates/bsnext_html/tests/html_playground.rs b/crates/bsnext_html/tests/html_playground.rs
new file mode 100644
index 0000000..07e5dc2
--- /dev/null
+++ b/crates/bsnext_html/tests/html_playground.rs
@@ -0,0 +1,82 @@
+use bsnext_html::HtmlFs;
+use bsnext_input::server_config::ServerIdentity;
+use bsnext_input::{InputArgs, InputCreation, InputCtx};
+use insta::assert_debug_snapshot;
+
+const INPUT: &str = r#"
+
+
+ Test!
+
+
+"#;
+
+#[test]
+fn test_html_playground_content() -> anyhow::Result<()> {
+ let idens = vec![];
+ let ctx = InputCtx::new(&idens, None);
+ let as_input = HtmlFs::from_input_str(INPUT, &ctx)?;
+ let Some(server) = as_input.servers.get(0) else {
+ return Err(anyhow::anyhow!("no server"));
+ };
+ let routes = server.routes();
+ let html = routes.get(0).unwrap();
+ let js = routes.get(1).unwrap();
+ let css = routes.get(2).unwrap();
+ assert_debug_snapshot!(html.kind);
+ assert_debug_snapshot!(js.kind);
+ assert_debug_snapshot!(css.kind);
+ Ok(())
+}
+#[test]
+fn test_html_playground_without_server_id() -> anyhow::Result<()> {
+ let idens = vec![];
+ let ctx = InputCtx::new(&idens, None);
+ let as_input = HtmlFs::from_input_str(INPUT, &ctx)?;
+ assert_eq!(as_input.servers.len(), 1);
+ let first = as_input.servers.get(0).unwrap();
+ let is_named = matches!(first.identity, ServerIdentity::Named { .. });
+ assert_eq!(is_named, true);
+ Ok(())
+}
+#[test]
+fn test_html_playground_with_server_id() -> anyhow::Result<()> {
+ let ident = ServerIdentity::Address {
+ bind_address: String::from("127.0.0.1:8080"),
+ };
+ let ctx = InputCtx::new(&[ident.clone()], None);
+ let as_input = HtmlFs::from_input_str(INPUT, &ctx)?;
+
+ assert_eq!(as_input.servers.len(), 1);
+ let first = as_input.servers.get(0).unwrap();
+ assert_eq!(ident, first.identity);
+ Ok(())
+}
+#[test]
+fn test_html_playground_with_port() -> anyhow::Result<()> {
+ let ident = ServerIdentity::Address {
+ bind_address: String::from("0.0.0.0:8080"),
+ };
+ let input_args = InputArgs { port: Some(8080) };
+ let ctx = InputCtx::new(&[], Some(input_args));
+ let as_input = HtmlFs::from_input_str(INPUT, &ctx)?;
+ let first = as_input.servers.get(0).unwrap();
+ assert_eq!(first.identity, ident);
+ Ok(())
+}
diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap
new file mode 100644
index 0000000..19bbacc
--- /dev/null
+++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-2.snap
@@ -0,0 +1,9 @@
+---
+source: crates/bsnext_html/tests/html_playground.rs
+expression: js.kind
+---
+Raw(
+ Raw {
+ raw: "class BSlive extends HTMLElement {\n connectedCallback() {\n this.innerHTML = \"Hello world\";\n }\n}\n\ncustomElements.define(\"abc-shane\", BSlive);\n",
+ },
+)
diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap
new file mode 100644
index 0000000..81a1760
--- /dev/null
+++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content-3.snap
@@ -0,0 +1,9 @@
+---
+source: crates/bsnext_html/tests/html_playground.rs
+expression: css.kind
+---
+Raw(
+ Raw {
+ raw: "p {\n color: red;\n}\n\nabc-shane {\n color: red;\n}\n",
+ },
+)
diff --git a/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap
new file mode 100644
index 0000000..e5ac0af
--- /dev/null
+++ b/crates/bsnext_html/tests/snapshots/html_playground__html_playground_content.snap
@@ -0,0 +1,9 @@
+---
+source: crates/bsnext_html/tests/html_playground.rs
+expression: html.kind
+---
+Raw(
+ Html {
+ html: "\n\n Test!
\n \n\n",
+ },
+)
diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs
index e5f43c7..ebc3efd 100644
--- a/crates/bsnext_input/src/lib.rs
+++ b/crates/bsnext_input/src/lib.rs
@@ -10,7 +10,6 @@ pub mod client_config;
#[cfg(test)]
pub mod input_test;
pub mod path_def;
-pub mod paths;
pub mod playground;
pub mod route;
pub mod route_manifest;
@@ -90,9 +89,57 @@ pub trait InputSource {
}
}
+#[derive(Debug, Default, Clone)]
+pub struct InputArgs {
+ pub port: Option,
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct InputCtx {
+ prev_server_ids: Option>,
+ args: Option,
+}
+
+impl InputCtx {
+ pub fn new(servers: &[ServerIdentity], args: Option) -> Self {
+ let prev = if servers.is_empty() {
+ None
+ } else {
+ Some(servers.to_vec())
+ };
+ Self {
+ prev_server_ids: prev,
+ args: args.to_owned(),
+ }
+ }
+
+ pub fn server_ids(&self) -> Option<&[ServerIdentity]> {
+ self.prev_server_ids.as_deref()
+ }
+
+ pub fn first_id_or_named(&self) -> ServerIdentity {
+ self.prev_server_ids
+ .as_ref()
+ .and_then(|x| x.get(0))
+ .map(ToOwned::to_owned)
+ .unwrap_or_else(|| ServerIdentity::named())
+ }
+
+ pub fn first_id(&self) -> Option {
+ self.prev_server_ids
+ .as_ref()
+ .and_then(|x| x.get(0))
+ .map(ToOwned::to_owned)
+ }
+
+ pub fn port(&self) -> Option {
+ self.args.as_ref().and_then(|x| x.port)
+ }
+}
+
pub trait InputCreation {
- fn from_input_path>(path: P) -> Result>;
- fn from_input_str>(content: P) -> Result>;
+ fn from_input_path>(path: P, ctx: &InputCtx) -> Result>;
+ fn from_input_str>(content: P, ctx: &InputCtx) -> Result>;
}
pub trait InputWriter {
@@ -125,6 +172,8 @@ pub enum InputError {
DirError(#[from] DirError),
#[error("Markdown error: {0}")]
MarkdownError(String),
+ #[error("HTML error: {0}")]
+ HtmlError(String),
#[error("{0}")]
YamlError(#[from] YamlError),
#[error(transparent)]
diff --git a/crates/bsnext_input/src/paths.rs b/crates/bsnext_input/src/paths.rs
deleted file mode 100644
index 1b345ae..0000000
--- a/crates/bsnext_input/src/paths.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use crate::route::{DirRoute, Route, RouteKind};
-use crate::server_config::{ServerConfig, ServerIdentity};
-use crate::{Input, PathDefinition, PathDefs, PathError};
-use std::path::{Path, PathBuf};
-
-pub fn from_paths>(
- cwd: &Path,
- paths: &[T],
- identity: ServerIdentity,
-) -> Result {
- let path_defs = paths
- .iter()
- .map(|p| {
- let pb = PathBuf::from(p.as_ref());
- if pb.is_absolute() {
- return PathDefinition {
- input: p.as_ref().to_string(),
- cwd: cwd.to_path_buf(),
- absolute: pb,
- };
- } else {
- return PathDefinition {
- input: p.as_ref().to_string(),
- cwd: cwd.to_path_buf(),
- absolute: cwd.join(pb),
- };
- }
- })
- .map(|path_def| {
- let exists = path_def.absolute.exists();
- (path_def, exists)
- })
- .collect::>();
-
- let invalid = path_defs
- .into_iter()
- .filter_map(|(pb, exists)| if exists { None } else { Some(pb) })
- .collect::>();
-
- if !invalid.is_empty() {
- return Err(PathError::MissingPaths {
- paths: PathDefs(invalid),
- });
- }
-
- let server = ServerConfig {
- identity,
- routes: paths
- .iter()
- .map(|p| -> Route {
- let str = p.as_ref();
- Route {
- path: "/".to_string().parse().unwrap(),
- kind: RouteKind::Dir(DirRoute {
- dir: str.into(),
- base: None,
- }),
- ..Default::default()
- }
- })
- .collect(),
- ..Default::default()
- };
- Ok(Input {
- servers: vec![server],
- })
-}
diff --git a/crates/bsnext_input/src/target.rs b/crates/bsnext_input/src/target.rs
index 39bf79f..3d863d2 100644
--- a/crates/bsnext_input/src/target.rs
+++ b/crates/bsnext_input/src/target.rs
@@ -4,4 +4,5 @@ pub enum TargetKind {
Yaml,
Toml,
Md,
+ Html,
}
diff --git a/crates/bsnext_md/src/lib.rs b/crates/bsnext_md/src/lib.rs
index d3f73f2..fb673af 100644
--- a/crates/bsnext_md/src/lib.rs
+++ b/crates/bsnext_md/src/lib.rs
@@ -5,7 +5,7 @@ use bsnext_input::path_def::PathDef;
use bsnext_input::playground::Playground;
use bsnext_input::route::{Route, RouteKind};
use bsnext_input::server_config::ServerConfig;
-use bsnext_input::Input;
+use bsnext_input::{Input, InputCtx};
use markdown::mdast::Node;
use markdown::{Constructs, ParseOptions};
use nom::branch::alt;
@@ -220,7 +220,7 @@ enum Convert {
Playground(Playground),
}
-pub fn nodes_to_input(nodes: &[Node]) -> Result {
+pub fn nodes_to_input(nodes: &[Node], ctx: &InputCtx) -> Result {
let mut routes = vec![];
let mut input: Option = None;
let mut parser = many0(alt((
@@ -334,6 +334,7 @@ pub fn nodes_to_input(nodes: &[Node]) -> Result {
let mut input = Input::default();
let server = ServerConfig {
routes,
+ identity: ctx.first_id_or_named(),
..Default::default()
};
input.servers.push(server);
@@ -360,7 +361,7 @@ fn code_val(n: &Node) -> &str {
}
}
-fn str_to_nodes(input: &str) -> Result, MarkdownError> {
+pub(crate) fn str_to_nodes(input: &str) -> Result, MarkdownError> {
let opts = ParseOptions {
constructs: Constructs {
frontmatter: true,
@@ -377,8 +378,3 @@ fn str_to_nodes(input: &str) -> Result, MarkdownError> {
}
}
}
-
-pub fn md_to_input(input: &str) -> Result {
- let root = str_to_nodes(input)?;
- nodes_to_input(&root)
-}
diff --git a/crates/bsnext_md/src/md_fs.rs b/crates/bsnext_md/src/md_fs.rs
index 1e67db6..e668e49 100644
--- a/crates/bsnext_md/src/md_fs.rs
+++ b/crates/bsnext_md/src/md_fs.rs
@@ -1,20 +1,25 @@
-use crate::md_to_input;
-use bsnext_input::{Input, InputCreation, InputError};
+use crate::{nodes_to_input, str_to_nodes, MarkdownError};
+use bsnext_input::{Input, InputCreation, InputCtx, InputError};
use std::fs::read_to_string;
use std::path::Path;
pub struct MdFs;
impl InputCreation for MdFs {
- fn from_input_path>(path: P) -> Result> {
+ fn from_input_path>(path: P, ctx: &InputCtx) -> Result> {
let str = read_to_string(path).map_err(|e| Box::new(e.into()))?;
- let input =
- md_to_input(&str).map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?;
+ let input = md_to_input(&str, ctx)
+ .map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?;
Ok(input)
}
- fn from_input_str>(content: P) -> Result> {
- let input = md_to_input(content.as_ref())
+ fn from_input_str>(content: P, ctx: &InputCtx) -> Result> {
+ let input = md_to_input(content.as_ref(), ctx)
.map_err(|e| Box::new(InputError::MarkdownError(e.to_string())))?;
Ok(input)
}
}
+
+fn md_to_input(input: &str, ctx: &InputCtx) -> Result {
+ let root = str_to_nodes(input)?;
+ nodes_to_input(&root, ctx)
+}
diff --git a/crates/bsnext_md/src/md_writer.rs b/crates/bsnext_md/src/md_writer.rs
index 0b3f2a2..a911f3d 100644
--- a/crates/bsnext_md/src/md_writer.rs
+++ b/crates/bsnext_md/src/md_writer.rs
@@ -12,7 +12,7 @@ impl InputWriter for MdWriter {
}
}
-pub fn _input_to_str(input: &Input) -> String {
+fn _input_to_str(input: &Input) -> String {
let mut chunks = vec![];
if let Some(server_config) = input.servers.first() {
#[derive(Debug, serde::Serialize)]
diff --git a/crates/bsnext_md/tests/md.rs b/crates/bsnext_md/tests/md.rs
index 3cc73b4..b4e41a4 100644
--- a/crates/bsnext_md/tests/md.rs
+++ b/crates/bsnext_md/tests/md.rs
@@ -1,7 +1,8 @@
use bsnext_input::path_def::PathDef;
use bsnext_input::route::{Route, RouteKind};
use bsnext_input::server_config::ServerIdentity;
-use bsnext_md::md_to_input;
+use bsnext_input::InputCreation;
+use bsnext_md::md_fs::MdFs;
use std::str::FromStr;
#[test]
@@ -21,7 +22,7 @@ body {
}
```
"#;
- let config = md_to_input(&input).expect("unwrap");
+ let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap");
let server_1 = config.servers.first().unwrap();
assert_eq!(
server_1.routes[0],
@@ -61,7 +62,7 @@ body {
}
```
"#;
- let config = md_to_input(&input).expect("unwrap");
+ let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap");
let server_1 = config.servers.first().unwrap();
assert_eq!(
server_1.routes[0],
@@ -114,7 +115,7 @@ path: /abc
# Before
"#;
- let input = md_to_input(&markdown).expect("unwrap");
+ let input = MdFs::from_input_str(&markdown, &Default::default()).expect("unwrap");
let server_1 = input.servers.first().unwrap();
let expected_id = ServerIdentity::Address {
bind_address: "0.0.0.0:3001".into(),
@@ -149,7 +150,7 @@ path: /abc
}
fn default_md_assertions(input: &str) -> anyhow::Result<()> {
- let input = md_to_input(&input).expect("unwrap");
+ let input = MdFs::from_input_str(&input, &Default::default()).expect("unwrap");
let server_1 = input.servers.first().unwrap();
let expected_id = ServerIdentity::Address {
bind_address: "0.0.0.0:5001".into(),
diff --git a/crates/bsnext_md/tests/md_playground.rs b/crates/bsnext_md/tests/md_playground.rs
index 841f5c1..3906fe4 100644
--- a/crates/bsnext_md/tests/md_playground.rs
+++ b/crates/bsnext_md/tests/md_playground.rs
@@ -1,4 +1,5 @@
-use bsnext_md::md_to_input;
+use bsnext_input::InputCreation;
+use bsnext_md::md_fs::MdFs;
#[test]
fn test_md_playground() -> anyhow::Result<()> {
@@ -27,7 +28,7 @@ console.log("hello world")
```
"#;
- let config = md_to_input(&input).expect("unwrap");
+ let config = MdFs::from_input_str(&input, &Default::default()).expect("unwrap");
let first_server = config.servers.get(0).unwrap();
let routes = first_server
.playground
diff --git a/crates/bsnext_md/tests/md_serialize.rs b/crates/bsnext_md/tests/md_serialize.rs
index ab53ceb..cc2b8ae 100644
--- a/crates/bsnext_md/tests/md_serialize.rs
+++ b/crates/bsnext_md/tests/md_serialize.rs
@@ -1,14 +1,14 @@
-use bsnext_input::InputWriter;
-use bsnext_md::md_to_input;
+use bsnext_input::{InputCreation, InputWriter};
+use bsnext_md::md_fs::MdFs;
use bsnext_md::md_writer::MdWriter;
#[test]
fn test_input_to_str() -> anyhow::Result<()> {
let input_str = include_str!("../../../examples/markdown/single.md");
- let input = md_to_input(&input_str).expect("unwrap");
+ let input = MdFs::from_input_str(&input_str, &Default::default()).expect("unwrap");
let output = MdWriter.input_to_str(&input);
println!("{}", output);
- let input = md_to_input(&output).expect("unwrapped 2");
+ let input = MdFs::from_input_str(&output, &Default::default()).expect("unwrapped 2");
println!("{:#?}", input);
assert_eq!(input.servers.len(), 1);
assert_eq!(input.servers.first().unwrap().routes.len(), 2);
diff --git a/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap b/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap
index 6fc8724..adf8ecd 100644
--- a/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap
+++ b/crates/bsnext_md/tests/snapshots/md_playground__md_playground.snap
@@ -24,7 +24,7 @@ expression: routes
inner: Addition(
InjectAddition {
addition_position: Prepend(
- "\n\n \n \n \n \n Document\n \n \n \n",
+ "\n\n \n \n \n \n Browsersync Live - Playground\n \n \n \n",
),
},
),
@@ -40,7 +40,7 @@ expression: routes
inner: Addition(
InjectAddition {
addition_position: Append(
- "\n \n \n\n",
+ "\n \n \n\n",
),
},
),
diff --git a/crates/bsnext_system/Cargo.toml b/crates/bsnext_system/Cargo.toml
index 150b2f0..4ff721b 100644
--- a/crates/bsnext_system/Cargo.toml
+++ b/crates/bsnext_system/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
bsnext_input = { path = "../bsnext_input" }
bsnext_md = { path = "../bsnext_md" }
+bsnext_html = { path = "../bsnext_html" }
bsnext_yaml = { path = "../bsnext_yaml" }
bsnext_tracing = { path = "../bsnext_tracing" }
bsnext_core = { path = "../bsnext_core" }
diff --git a/crates/bsnext_system/src/input_fs.rs b/crates/bsnext_system/src/input_fs.rs
index 02a25e5..bf3e8e5 100644
--- a/crates/bsnext_system/src/input_fs.rs
+++ b/crates/bsnext_system/src/input_fs.rs
@@ -1,13 +1,14 @@
-use bsnext_input::{Input, InputCreation, InputError};
+use bsnext_input::{Input, InputCreation, InputCtx, InputError};
use std::path::Path;
-pub fn from_input_path>(path: P) -> Result> {
+pub fn from_input_path>(path: P, ctx: &InputCtx) -> Result> {
match path.as_ref().extension().and_then(|x| x.to_str()) {
None => Err(Box::new(InputError::MissingExtension(
path.as_ref().to_owned(),
))),
- Some("yml") | Some("yaml") => bsnext_yaml::yaml_fs::YamlFs::from_input_path(path),
- Some("md") | Some("markdown") => bsnext_md::md_fs::MdFs::from_input_path(path),
+ Some("yml") | Some("yaml") => bsnext_yaml::yaml_fs::YamlFs::from_input_path(path, ctx),
+ Some("md") | Some("markdown") => bsnext_md::md_fs::MdFs::from_input_path(path, ctx),
+ Some("html") => bsnext_html::HtmlFs::from_input_path(path, ctx),
Some(other) => Err(Box::new(InputError::UnsupportedExtension(
other.to_string(),
))),
diff --git a/crates/bsnext_system/src/lib.rs b/crates/bsnext_system/src/lib.rs
index 932d729..e74db75 100644
--- a/crates/bsnext_system/src/lib.rs
+++ b/crates/bsnext_system/src/lib.rs
@@ -3,7 +3,7 @@ use crate::monitor::{
};
use actix::{Actor, Addr, AsyncContext, Handler, Running};
-use bsnext_input::Input;
+use bsnext_input::{Input, InputCtx};
use std::collections::HashMap;
use actix_rt::Arbiter;
@@ -43,11 +43,17 @@ pub struct BsSystem {
self_addr: Option>,
servers_addr: Option>,
any_event_sender: Option>,
- input_monitors: Vec>,
+ input_monitors: Option,
any_monitors: HashMap,
cwd: Option,
}
+#[derive(Debug, Clone)]
+pub struct InputMonitor {
+ pub addr: Addr,
+ pub ctx: InputCtx,
+}
+
#[derive(Debug)]
pub struct EventWithSpan {
pub evt: ExternalEventsDTO,
@@ -73,7 +79,7 @@ impl BsSystem {
self_addr: None,
servers_addr: None,
any_event_sender: None,
- input_monitors: vec![],
+ input_monitors: None,
any_monitors: Default::default(),
cwd: None,
}
@@ -294,9 +300,16 @@ impl Handler for BsSystem {
match msg.kind.input(&start_context) {
Ok(SystemStartArgs::PathWithInput { path, input }) => {
tracing::debug!("PathWithInput");
+ let ids = input
+ .servers
+ .iter()
+ .map(|x| x.identity.clone())
+ .collect::>();
+ let input_ctx = InputCtx::new(&ids, None);
ctx.notify(MonitorInput {
path: path.clone(),
cwd: cwd.clone(),
+ ctx: input_ctx,
});
self.accept_watchables(&input);
@@ -314,6 +327,7 @@ impl Handler for BsSystem {
ctx.notify(MonitorInput {
path: path.clone(),
cwd: cwd.clone(),
+ ctx: InputCtx::default(),
});
self.publish_any_event(AnyEvent::Internal(InternalEvents::InputError(input_error)));
Ok(DidStart::Started)
diff --git a/crates/bsnext_system/src/monitor.rs b/crates/bsnext_system/src/monitor.rs
index 07186d9..81c3a50 100644
--- a/crates/bsnext_system/src/monitor.rs
+++ b/crates/bsnext_system/src/monitor.rs
@@ -1,4 +1,4 @@
-use crate::BsSystem;
+use crate::{BsSystem, InputMonitor};
use actix::{Actor, Addr, AsyncContext};
use std::hash::Hash;
@@ -10,7 +10,7 @@ use bsnext_fs::{
};
use bsnext_input::route::{DebounceDuration, DirRoute, FilterKind, RouteKind, Spec, SpecOpts};
use bsnext_input::server_config::ServerIdentity;
-use bsnext_input::{Input, InputError, PathDefinition, PathDefs, PathError};
+use bsnext_input::{Input, InputCtx, InputError, PathDefinition, PathDefs, PathError};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
@@ -33,6 +33,7 @@ pub struct Monitor {
pub struct MonitorInput {
pub path: PathBuf,
pub cwd: PathBuf,
+ pub ctx: InputCtx,
}
impl actix::Handler for BsSystem {
@@ -53,7 +54,11 @@ impl actix::Handler for BsSystem {
tracing::trace!("[main.rs] starting input monitor");
let input_watcher_addr = input_watcher.start();
- self.input_monitors.push(input_watcher_addr.clone());
+ let input_monitor = InputMonitor {
+ addr: input_watcher_addr.clone(),
+ ctx: msg.ctx.clone(),
+ };
+ self.input_monitors = Some(input_monitor);
input_watcher_addr.do_send(RequestWatchPath {
recipients: vec![ctx.address().recipient()],
@@ -92,14 +97,34 @@ impl BsSystem {
let _guard = span.enter();
match msg.ctx.id() {
0 => {
- tracing::info!(?inner, "InputFile file changed");
- let input = from_input_path(&inner.absolute_path);
+ tracing::info!("InputFile file changed {:?}", inner);
+
+ let ctx = self
+ .input_monitors
+ .as_ref()
+ .map(|x| x.ctx.clone())
+ .unwrap_or_default();
+
+ let input = from_input_path(&inner.absolute_path, &ctx);
let Ok(input) = input else {
let err = input.unwrap_err();
return Some(AnyEvent::Internal(InternalEvents::InputError(*err)));
};
+ if let Some(mon) = self.input_monitors.as_mut() {
+ let next = input
+ .servers
+ .iter()
+ .map(|s| s.identity.clone())
+ .collect::>();
+ let ctx = InputCtx::new(&next, None);
+ tracing::info!("will set next ids {:?}", next);
+ if !next.is_empty() {
+ mon.ctx = ctx
+ }
+ }
+
self.accept_watchables(&input);
self.resolve_servers(input);
diff --git a/crates/bsnext_system/src/start_kind.rs b/crates/bsnext_system/src/start_kind.rs
index fbd38e0..bcd0565 100644
--- a/crates/bsnext_system/src/start_kind.rs
+++ b/crates/bsnext_system/src/start_kind.rs
@@ -1,7 +1,7 @@
use crate::args::Args;
use crate::start_kind::start_from_example::StartFromExample;
use crate::start_kind::start_from_inputs::{StartFromInput, StartFromInputPaths};
-use crate::start_kind::start_from_paths::StartFromPaths;
+use crate::start_kind::start_from_paths::StartFromDirPaths;
use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs};
use bsnext_input::{Input, InputError};
@@ -14,7 +14,7 @@ pub enum StartKind {
FromInput(StartFromInput),
FromInputPaths(StartFromInputPaths),
FromExample(StartFromExample),
- FromPaths(StartFromPaths),
+ FromDirPaths(StartFromDirPaths),
}
impl StartKind {
@@ -33,7 +33,7 @@ impl StartKind {
}
if !args.paths.is_empty() {
- StartKind::FromPaths(StartFromPaths {
+ StartKind::FromDirPaths(StartFromDirPaths {
paths: args.paths,
write_input: args.write,
port: args.port,
@@ -42,6 +42,7 @@ impl StartKind {
} else {
StartKind::FromInputPaths(StartFromInputPaths {
input_paths: args.input.clone(),
+ port: args.port,
})
}
}
@@ -55,7 +56,7 @@ impl SystemStart for StartKind {
match self {
StartKind::FromInputPaths(from_inputs) => from_inputs.input(ctx),
StartKind::FromExample(from_example) => from_example.input(ctx),
- StartKind::FromPaths(from_input_paths) => from_input_paths.input(ctx),
+ StartKind::FromDirPaths(from_dir_paths) => from_dir_paths.input(ctx),
StartKind::FromInput(from_inputs) => from_inputs.input(ctx),
}
}
@@ -67,7 +68,7 @@ pub mod start_fs {
use std::path::{Path, PathBuf};
use bsnext_input::target::TargetKind;
- use bsnext_input::{DirError, Input, InputWriteError};
+ use bsnext_input::{DirError, Input, InputWriteError, InputWriter};
#[derive(Default, Debug, PartialEq)]
pub enum WriteMode {
@@ -82,14 +83,16 @@ pub mod start_fs {
write_mode: &WriteMode,
) -> Result {
let string = match target_kind {
- TargetKind::Yaml => bsnext_yaml::input_to_str(input),
+ TargetKind::Yaml => bsnext_yaml::yaml_writer::YamlWriter.input_to_str(input),
TargetKind::Toml => todo!("toml missing"),
- TargetKind::Md => bsnext_md::md_writer::_input_to_str(input),
+ TargetKind::Md => bsnext_md::md_writer::MdWriter.input_to_str(input),
+ TargetKind::Html => bsnext_html::html_writer::HtmlWriter.input_to_str(input),
};
let name = match target_kind {
TargetKind::Yaml => "bslive.yml",
TargetKind::Toml => todo!("toml missing"),
TargetKind::Md => "bslive.md",
+ TargetKind::Html => "bslive.html",
};
let next_path = cwd.join(name);
tracing::info!(
diff --git a/crates/bsnext_system/src/start_kind/start_from_example.rs b/crates/bsnext_system/src/start_kind/start_from_example.rs
index 5f76fb1..ef2945f 100644
--- a/crates/bsnext_system/src/start_kind/start_from_example.rs
+++ b/crates/bsnext_system/src/start_kind/start_from_example.rs
@@ -19,6 +19,7 @@ pub struct StartFromExample {
impl SystemStart for StartFromExample {
fn input(&self, ctx: &StartupContext) -> Result> {
+ // todo: mimic this for other kinds of startup - basically allow 'port' to be given and respected
let identity =
ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?;
let input_source_kind = self.example.into_input(Some(identity));
diff --git a/crates/bsnext_system/src/start_kind/start_from_inputs.rs b/crates/bsnext_system/src/start_kind/start_from_inputs.rs
index 9be6874..16f7517 100644
--- a/crates/bsnext_system/src/start_kind/start_from_inputs.rs
+++ b/crates/bsnext_system/src/start_kind/start_from_inputs.rs
@@ -1,21 +1,22 @@
use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs};
use crate::input_fs::from_input_path;
-use bsnext_input::{Input, InputError};
+use bsnext_input::{Input, InputArgs, InputCtx, InputError};
use std::path::{Path, PathBuf};
#[derive(Debug)]
pub struct StartFromInputPaths {
pub input_paths: Vec,
+ pub port: Option,
}
impl SystemStart for StartFromInputPaths {
fn input(&self, ctx: &StartupContext) -> Result> {
- from_yml_paths(&ctx.cwd, &self.input_paths)
+ from_input_paths(&ctx.cwd, &self.input_paths, &self.port)
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct StartFromInput {
pub input: Input,
}
@@ -28,16 +29,17 @@ impl SystemStart for StartFromInput {
}
}
-fn from_yml_paths>(
+fn from_input_paths>(
cwd: &Path,
inputs: &[T],
+ port: &Option,
) -> Result> {
let input_candidates = inputs
.iter()
.map(|path| cwd.join(path.as_ref()))
.collect::>();
- let lookups = ["bslive.yml", "bslive.yaml", "bslive.md"]
+ let lookups = ["bslive.yml", "bslive.yaml", "bslive.md", "bslive.html"]
.iter()
.map(|path| cwd.join(path))
.collect::>();
@@ -75,7 +77,11 @@ fn from_yml_paths>(
tracing::info!(?input_path);
- let result = from_input_path(input_path);
+ let input_args = InputArgs {
+ port: port.to_owned(),
+ };
+ let initial_ctx = InputCtx::new(&[], Some(input_args));
+ let result = from_input_path(input_path, &initial_ctx);
match result {
Ok(input) => Ok(SystemStartArgs::PathWithInput {
path: input_path.to_path_buf(),
diff --git a/crates/bsnext_system/src/start_kind/start_from_paths.rs b/crates/bsnext_system/src/start_kind/start_from_paths.rs
index 88114c5..32a40c8 100644
--- a/crates/bsnext_system/src/start_kind/start_from_paths.rs
+++ b/crates/bsnext_system/src/start_kind/start_from_paths.rs
@@ -1,24 +1,26 @@
use crate::start_kind::start_fs;
use crate::start_kind::start_fs::WriteMode;
-use bsnext_input::paths::from_paths;
-use bsnext_input::server_config::ServerIdentity;
+use bsnext_input::route::{DirRoute, Route, RouteKind};
+use bsnext_input::server_config::{ServerConfig, ServerIdentity};
use bsnext_input::startup::{StartupContext, SystemStart, SystemStartArgs};
use bsnext_input::target::TargetKind;
-use bsnext_input::InputError;
+use bsnext_input::{Input, InputError, PathDefinition, PathDefs, PathError};
+use std::path::{Path, PathBuf};
#[derive(Debug)]
-pub struct StartFromPaths {
+pub struct StartFromDirPaths {
pub paths: Vec,
pub write_input: bool,
pub port: Option,
pub force: bool,
}
-impl SystemStart for StartFromPaths {
+impl SystemStart for StartFromDirPaths {
fn input(&self, ctx: &StartupContext) -> Result> {
let identity =
ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?;
- let input = from_paths(&ctx.cwd, &self.paths, identity).map_err(|e| Box::new(e.into()))?;
+ let input =
+ from_dir_paths(&ctx.cwd, &self.paths, identity).map_err(|e| Box::new(e.into()))?;
let write_mode = if self.force {
WriteMode::Override
} else {
@@ -34,6 +36,69 @@ impl SystemStart for StartFromPaths {
}
}
+fn from_dir_paths>(
+ cwd: &Path,
+ paths: &[T],
+ identity: ServerIdentity,
+) -> Result {
+ let path_defs = paths
+ .iter()
+ .map(|p| {
+ let pb = PathBuf::from(p.as_ref());
+ if pb.is_absolute() {
+ return PathDefinition {
+ input: p.as_ref().to_string(),
+ cwd: cwd.to_path_buf(),
+ absolute: pb,
+ };
+ } else {
+ return PathDefinition {
+ input: p.as_ref().to_string(),
+ cwd: cwd.to_path_buf(),
+ absolute: cwd.join(pb),
+ };
+ }
+ })
+ .map(|path_def| {
+ let exists = path_def.absolute.exists();
+ (path_def, exists)
+ })
+ .collect::>();
+
+ let invalid = path_defs
+ .into_iter()
+ .filter_map(|(pb, exists)| if exists { None } else { Some(pb) })
+ .collect::>();
+
+ if !invalid.is_empty() {
+ return Err(PathError::MissingPaths {
+ paths: PathDefs(invalid),
+ });
+ }
+
+ let server = ServerConfig {
+ identity,
+ routes: paths
+ .iter()
+ .map(|p| -> Route {
+ let str = p.as_ref();
+ Route {
+ path: "/".to_string().parse().unwrap(),
+ kind: RouteKind::Dir(DirRoute {
+ dir: str.into(),
+ base: None,
+ }),
+ ..Default::default()
+ }
+ })
+ .collect(),
+ ..Default::default()
+ };
+ Ok(Input {
+ servers: vec![server],
+ })
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -41,7 +106,7 @@ mod test {
fn test() -> anyhow::Result<()> {
use tempfile::tempdir;
let tmp_dir = tempdir()?;
- let v = StartFromPaths {
+ let v = StartFromDirPaths {
paths: vec![".".into()],
write_input: false,
port: Some(3000),
diff --git a/crates/bsnext_yaml/src/lib.rs b/crates/bsnext_yaml/src/lib.rs
index 5768f57..1ee67cc 100644
--- a/crates/bsnext_yaml/src/lib.rs
+++ b/crates/bsnext_yaml/src/lib.rs
@@ -1,6 +1,2 @@
pub mod yaml_fs;
-use bsnext_input::Input;
-
-pub fn input_to_str(input: &Input) -> String {
- serde_yaml::to_string(&input).expect("create yaml?")
-}
+pub mod yaml_writer;
diff --git a/crates/bsnext_yaml/src/yaml_fs.rs b/crates/bsnext_yaml/src/yaml_fs.rs
index 0df1d03..948ad60 100644
--- a/crates/bsnext_yaml/src/yaml_fs.rs
+++ b/crates/bsnext_yaml/src/yaml_fs.rs
@@ -1,5 +1,5 @@
use bsnext_input::yml::YamlError;
-use bsnext_input::{BsLiveRulesError, Input, InputCreation, InputError};
+use bsnext_input::{BsLiveRulesError, Input, InputCreation, InputCtx, InputError};
use miette::NamedSource;
use std::fs::read_to_string;
use std::path::Path;
@@ -7,7 +7,7 @@ use std::path::Path;
pub struct YamlFs;
impl InputCreation for YamlFs {
- fn from_input_path>(path: P) -> Result> {
+ fn from_input_path>(path: P, _ctx: &InputCtx) -> Result> {
let str = read_to_string(&path).map_err(|e| Box::new(e.into()))?;
if str.trim().is_empty() {
return Err(Box::new(InputError::YamlError(YamlError::EmptyError {
@@ -32,7 +32,10 @@ impl InputCreation for YamlFs {
Ok(output)
}
- fn from_input_str>(_content: P) -> Result> {
+ fn from_input_str>(
+ _content: P,
+ _ctx: &InputCtx,
+ ) -> Result> {
todo!()
}
}
diff --git a/crates/bsnext_yaml/src/yaml_writer.rs b/crates/bsnext_yaml/src/yaml_writer.rs
new file mode 100644
index 0000000..bf686a5
--- /dev/null
+++ b/crates/bsnext_yaml/src/yaml_writer.rs
@@ -0,0 +1,13 @@
+use bsnext_input::{Input, InputWriter};
+
+pub struct YamlWriter;
+
+impl InputWriter for YamlWriter {
+ fn input_to_str(&self, input: &Input) -> String {
+ input_to_str(input)
+ }
+}
+
+fn input_to_str(input: &Input) -> String {
+ serde_yaml::to_string(&input).expect("create yaml?")
+}
diff --git a/examples/basic/public/index.html b/examples/basic/public/index.html
index 45d6b1b..081fd9d 100644
--- a/examples/basic/public/index.html
+++ b/examples/basic/public/index.html
@@ -9,7 +9,7 @@
-Edit me!
+Edit me! - a full HTML