diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fadddc..49b3b62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: - main pull_request: branches: - - '**' + - "**" name: CI @@ -15,32 +15,32 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: actions-rust-lang/audit@v1 - name: Audit Rust Dependencies + - name: "Generate Cargo.lock" + run: cargo generate-lockfile + - uses: rustsec/audit-check@v2 with: - denyWarnings: true - createIssues: false + token: ${{ secrets.GITHUB_TOKEN }} licenses: name: Licenses runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v2 + - uses: actions/checkout@v4 + - uses: EmbarkStudios/cargo-deny-action@v2 - - name: cargo-about cache - id: cargo-about-cache - uses: actions/cache@v4 - with: - path: ~/.cargo/bin/cargo-about - key: cargo-about-${{ runner.os }} + - name: cargo-about cache + id: cargo-about-cache + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/cargo-about + key: cargo-about-${{ runner.os }} - - name: cargo-about install - if: steps.cargo-about-cache.outputs.cache-hit != 'true' - run: cargo install --locked cargo-about + - name: cargo-about install + if: steps.cargo-about-cache.outputs.cache-hit != 'true' + run: cargo install --locked cargo-about - - name: cargo-about generate licenses - run: cargo about generate --workspace --features "server_side_graphviz" about.hbs > doc/src/licenses.html + - name: cargo-about generate licenses + run: cargo about generate --workspace about.hbs > doc/src/licenses.html fmt: name: Rustfmt @@ -82,14 +82,14 @@ jobs: toolchain: nightly components: clippy - - name: 'Run clippy (client side graphviz)' + - name: "Run clippy (client side graphviz)" run: | cargo clippy --workspace -- -D warnings - - name: 'Run clippy (server side graphviz)' + - name: "Run clippy (server side graphviz)" # we cannot use `--all-features` because `dot_ix` has features that are mutually exclusive. run: | - cargo clippy --workspace --features "server_side_graphviz" -- -D warnings + cargo clippy --workspace -- -D warnings coverage: name: Coverage @@ -104,14 +104,14 @@ jobs: - uses: taiki-e/install-action@cargo-llvm-cov - - name: 'Collect coverage' + - name: "Collect coverage" run: ./coverage.sh - - name: 'Print directory sizes' + - name: "Print directory sizes" run: du -sh target/coverage target/llvm-cov-target - name: Upload to codecov.io - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: files: ./target/coverage/lcov.info token: ${{ secrets.CODECOV_TOKEN }} @@ -124,11 +124,11 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: 'Build and test' + - name: "Build and test" run: cargo test --workspace - build_playground_linux: - name: Build Playground (Linux) + build_playground_linux_leptos: + name: Build Playground (Linux) - cargo-leptos # On `ubuntu-latest`, this job fails because the CI runner is CPU bound # when `monaco` is built. # @@ -137,32 +137,82 @@ jobs: # See runs-on: ubuntu-22.04 timeout-minutes: 25 + env: + # Seems to solve this issue: + # + # ```text + # 0: Unexpected token Semicolon at :1360:9 + # ``` + LEPTOS_TAILWIND_VERSION: "v4.1.4" steps: - uses: actions/checkout@v4 - - name: 'Install Rust' + - name: "Install Rust" uses: dtolnay/rust-toolchain@master with: toolchain: stable targets: wasm32-unknown-unknown - - name: cargo-leptos cache - id: cargo-leptos-cache - uses: actions/cache@v4 + - name: cargo-leptos cache restore + id: cargo_leptos_cache_restore + uses: actions/cache/restore@v4 with: path: ~/.cargo/bin/cargo-leptos - key: cargo-leptos-${{ runner.os }} + key: ${{ runner.os }}-cargo-leptos + + - run: cargo install cargo-leptos + if: steps.cargo_leptos_cache_restore.outputs.cache-hit != 'true' - - name: cargo-leptos install - if: steps.cargo-leptos-cache.outputs.cache-hit != 'true' - run: cargo install cargo-leptos + - name: cargo-leptos cache save + id: cargo_leptos_cache_save + uses: actions/cache/save@v4 + if: always() && steps.cargo_leptos_cache_restore.outputs.cache-hit != 'true' + with: + path: ~/.cargo/bin/cargo-leptos + key: ${{ runner.os }}-cargo-leptos - - name: 'Build playground' + - name: "Build playground" working-directory: ./playground run: cargo leptos build -v - - name: 'Build playground (server side graphviz)' + build_playground_linux_trunk: + name: Build Playground (Linux) - trunk + # On `ubuntu-latest`, this job fails because the CI runner is CPU bound + # when `monaco` is built. + # + # Trying 22.04 to see if it alleviates the problem. + # + # See + runs-on: ubuntu-22.04 + timeout-minutes: 25 + steps: + - uses: actions/checkout@v4 + - name: "Install Rust" + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + targets: wasm32-unknown-unknown + + - name: trunk cache restore + id: trunk_cache_restore + uses: actions/cache/restore@v4 + with: + path: ~/.cargo/bin/trunk + key: ${{ runner.os }}-trunk + + - run: cargo install trunk + if: steps.trunk_cache_restore.outputs.cache-hit != 'true' + + - name: trunk cache save + id: trunk_cache_save + uses: actions/cache/save@v4 + if: always() && steps.trunk_cache_restore.outputs.cache-hit != 'true' + with: + path: ~/.cargo/bin/trunk + key: ${{ runner.os }}-trunk + + - name: "Build playground" working-directory: ./playground - run: cargo leptos build --features "server_side_graphviz" -v + run: trunk build -v build_and_test_windows: name: Build and Test (Windows) @@ -175,11 +225,11 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: 'Build and test' + - name: "Build and test" run: cargo test --workspace - build_playground_windows: - name: Build Playground (Windows) + build_playground_windows_trunk: + name: Build Playground (Windows) - trunk runs-on: windows-latest timeout-minutes: 30 steps: @@ -187,29 +237,30 @@ jobs: run: git config --global core.symlinks true - uses: actions/checkout@v4 - - name: 'Install Rust' + - name: "Install Rust" uses: dtolnay/rust-toolchain@master with: toolchain: stable targets: wasm32-unknown-unknown - - name: cargo-leptos cache - id: cargo-leptos-cache - uses: actions/cache@v4 + - name: trunk cache restore + id: trunk_cache_restore + uses: actions/cache/restore@v4 with: - path: 'C:\Users\runneradmin\.cargo\bin\cargo-leptos.exe' - key: cargo-leptos-${{ runner.os }} + path: 'C:\Users\runneradmin\.cargo\bin\trunk.exe' + key: ${{ runner.os }}-trunk - - name: cargo-leptos install - if: steps.cargo-leptos-cache.outputs.cache-hit != 'true' - # --locked: fix is in-progress at https://github.com/leptos-rs/cargo-leptos/pull/274 - run: |- - cargo install cargo-leptos --locked + - run: cargo install trunk + if: steps.trunk_cache_restore.outputs.cache-hit != 'true' - - name: 'Build playground' - working-directory: ./playground - run: cargo leptos build -v + - name: trunk cache save + id: trunk_cache_save + uses: actions/cache/save@v4 + if: always() && steps.trunk_cache_restore.outputs.cache-hit != 'true' + with: + path: 'C:\Users\runneradmin\.cargo\bin\trunk.exe' + key: ${{ runner.os }}-trunk - - name: 'Build playground (server side graphviz)' + - name: "Build playground" working-directory: ./playground - run: cargo leptos build --features "server_side_graphviz" -v + run: trunk build -v diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 261fc88..82463c3 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -32,29 +32,15 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: 'Install Rust' + - name: "Install Rust" uses: dtolnay/rust-toolchain@master with: toolchain: nightly targets: wasm32-unknown-unknown - - name: cargo-leptos cache - id: cargo-leptos-cache - uses: actions/cache@v4 - with: - path: ~/.cargo/bin/cargo-leptos - key: cargo-leptos-${{ runner.os }} - - - name: cargo-leptos install - if: steps.cargo-leptos-cache.outputs.cache-hit != 'true' - run: cargo install cargo-leptos - - name: Setup Pages uses: actions/configure-pages@v5 - - name: Download and install tailwindcss binary - run: npm install -D tailwindcss - - name: Download and install Trunk binary run: wget -qO- https://github.com/thedodd/trunk/releases/latest/download/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- @@ -81,7 +67,7 @@ jobs: uses: actions/upload-pages-artifact@v3 with: # Upload playground/dist directory - path: 'playground/dist' + path: "playground/dist" - name: Deploy to GitHub Pages id: deployment diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca2d860..c9bbca1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,7 +1,7 @@ on: push: tags: - - '*' + - "*" name: Publish @@ -12,11 +12,11 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v4 - - uses: actions-rust-lang/audit@v1 - name: Audit Rust Dependencies + - name: "Generate Cargo.lock" + run: cargo generate-lockfile + - uses: rustsec/audit-check@v2 with: - denyWarnings: true - createIssues: false + token: ${{ secrets.GITHUB_TOKEN }} build_and_test_linux: name: Build and Test (Linux) @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: 'Build and test' + - name: "Build and test" run: cargo test --workspace build_and_test_windows: @@ -40,7 +40,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: 'Build and test' + - name: "Build and test" run: cargo test --workspace crates_io_publish: @@ -56,34 +56,30 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - name: cargo-release Cache - id: cargo_release_cache - uses: actions/cache@v4 + - name: cargo-release cache restore + id: cargo_release_cache_restore + uses: actions/cache/restore@v4 with: path: ~/.cargo/bin/cargo-release key: ${{ runner.os }}-cargo-release - run: cargo install cargo-release - if: steps.cargo_release_cache.outputs.cache-hit != 'true' + if: steps.cargo_release_cache_restore.outputs.cache-hit != 'true' + + - name: cargo-release cache save + id: cargo_release_cache_save + uses: actions/cache/save@v4 + if: always() && steps.cargo_release_cache_restore.outputs.cache-hit != 'true' + with: + path: ~/.cargo/bin/cargo-release + key: ${{ runner.os }}-cargo-release - name: cargo login - run: cargo login ${{ secrets.CRATES_IO_API_TOKEN }} + run: |- + echo "${{ secrets.CRATES_IO_API_TOKEN }}" | cargo login # allow-branch HEAD is because GitHub actions switches # to the tag while building, which is a detached head - - # Publishing is currently messy, because: - # - # * `peace_rt_model_core` exports `NativeError` or `WebError` depending on the target. - # * `peace_rt_model_web` fails to build when publishing the workspace for a native target. - # * `peace_rt_model_web` still needs its dependencies to be published before it can be - # published. - # * `peace_rt_model_hack` needs `peace_rt_model_web` to be published before it can be - # published. - # - # We *could* pass through `--no-verify` so `cargo` doesn't build the crate before publishing, - # which is reasonable, since this job only runs after the Linux, Windows, and WASM builds - # have passed. - name: "cargo release publish" run: |- cargo release \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 486a492..029d0ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.10.0 (2025-04-18) + +* Update crate rust edition to 2024. +* ***Breaking:*** Remove `"server_side_graphviz"` feature. +* ***Breaking:*** Upgrade to `leptos 0.8.0-rc1`. + + ## 0.9.2 (2025-01-27) * Update dependency versions and address `RUSTSEC-2024-0370`. diff --git a/Cargo.toml b/Cargo.toml index eccf3ee..a6e2165 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,27 +24,19 @@ dot_ix_rt = { workspace = true, optional = true } dot_ix_web_components = { workspace = true, optional = true } [features] -default = [ - "rt", - "web_components", -] +default = ["rt", "web_components"] rt = ["dep:dot_ix_rt"] web_components = ["dep:dot_ix_web_components"] ssr = ["dot_ix_web_components?/ssr"] -server_side_graphviz = ["dot_ix_web_components?/server_side_graphviz"] [workspace] -members = [ - "crate/*", - "playground", - "workspace_tests", -] +members = ["crate/*", "playground", "workspace_tests"] [workspace.package] -version = "0.9.2" +version = "0.10.0" authors = ["Azriel Hoh "] -edition = "2021" +edition = "2024" homepage = "https://github.com/azriel91/dot_ix" repository = "https://github.com/azriel91/dot_ix" readme = "README.md" @@ -54,44 +46,45 @@ license = "MIT OR Apache-2.0" [workspace.dependencies] # dot_ix crates -dot_ix = { version = "0.9.2", path = "." } -dot_ix_model = { version = "0.9.2", path = "crate/model" } -dot_ix_rt = { version = "0.9.2", path = "crate/rt" } -dot_ix_static_check_macros = { version = "0.9.2", path = "crate/static_check_macros" } -dot_ix_web_components = { version = "0.9.2", path = "crate/web_components" } +dot_ix = { version = "0.10.0", path = "." } +dot_ix_model = { version = "0.10.0", path = "crate/model" } +dot_ix_rt = { version = "0.10.0", path = "crate/rt" } +dot_ix_static_check_macros = { version = "0.10.0", path = "crate/static_check_macros" } +dot_ix_web_components = { version = "0.10.0", path = "crate/web_components" } # external crates -axum = "0.7.9" +axum = "0.8.3" cfg-if = "1" console_error_panic_hook = "0.1" console_log = "1" gloo-net = "0.6.0" -indexmap = "2.7.1" -indoc = "2.0.5" +id_newtype = "0.1.0" +indexmap = "2.9.0" +indoc = "2.0.6" js-sys = "0.3.77" web-sys = "0.3.77" -leptos = { version = "0.7.4" } -leptos_axum = "0.7.4" -leptos_meta = { version = "0.7.4" } -leptos_router = { version = "0.7.4" } -leptos_router_macro = { version = "0.7.4" } -leptos-use = "0.15.5" +leptos = { version = "0.8.0-rc1" } +leptos_axum = "0.8.0-rc1" +leptos_meta = { version = "0.8.0-rc1" } +leptos_router = { version = "0.8.0-rc1" } +leptos_router_macro = { version = "0.8.0-rc1" } +leptos-use = "0.16.0-beta" log = "0.4" log4rs = { version = "1.3.0", default-features = false } monaco = "0.5.0" -serde = "1.0.217" -tempfile = "3.15.0" -tokio = "1.43.0" +serde = "1.0.219" +tempfile = "3.19.1" +tokio = "1.44.2" tower = "0.5.2" wasm-bindgen = "0.2.100" tailwind-css = "0.13.0" -thiserror = "2.0.11" +thiserror = "2.0.12" tracing = "0.1.41" -http = "1.2.0" -proc-macro2 = "1.0.93" -quote = "1.0.38" -reqwest = "0.12.12" -syn = "2.0.96" +http = "1.3.1" +proc-macro2 = "1.0.95" +quote = "1.0.40" +reqwest = "0.12.15" +syn = "2.0.100" serde_yaml = "0.9.34" [workspace.lints.rust] diff --git a/README.md b/README.md index b683ed1..b6a8091 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,10 @@ https://user-images.githubusercontent.com/2993230/253878816-0729970f-651f-45ef-a Add the following to `Cargo.toml` ```toml -dot_ix = "0.9.2" +dot_ix = "0.10.0" # Enables the `FlexDiag` web component. -dot_ix = { version = "0.9.2", features = ["flex_diag"] } - -# Enables server side dot generation. -# Requires graphviz `dot` to be installed server side. -dot_ix = { version = "0.9.2", features = ["server_side_graphviz"] } +dot_ix = { version = "0.10.0", features = ["flex_diag"] } ``` @@ -46,20 +42,8 @@ cargo install cargo-leptos # Then, one of: # * client side rendering -- uses WASM compiled graphviz to generate the graph. cargo leptos watch -# * server side rendering -- runs `dot` on the server to generate the graph. -# Requires `graphviz` to be installed. -cargo leptos watch --features "server_side_graphviz" -v ``` -For server side rendering, the `"server_side_graphviz"` feature needs to be passed in separately because that feature still needs to be enabled for the lib compilation, i.e. - -* server side rendering: - - lib features: `"server_side_graphviz"` - - bin features: `"ssr,server_side_graphviz"` -* client side rendering: - - lib features: `""` - - bin features: `""` - ## To Do diff --git a/crate/model/Cargo.toml b/crate/model/Cargo.toml index 89c7729..74fd612 100644 --- a/crate/model/Cargo.toml +++ b/crate/model/Cargo.toml @@ -17,6 +17,7 @@ doctest = false test = false [dependencies] +id_newtype = { workspace = true } indexmap = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } dot_ix_static_check_macros = { workspace = true } diff --git a/crate/model/src/common.rs b/crate/model/src/common.rs index 5323eb1..14d0d77 100644 --- a/crate/model/src/common.rs +++ b/crate/model/src/common.rs @@ -1,4 +1,3 @@ -pub(crate) use self::id_newtype::id_newtype; pub use self::{ any_id::{AnyId, AnyIdInvalidFmt}, dot_src_and_styles::DotSrcAndStyles, @@ -34,7 +33,6 @@ mod edge_descs; mod edge_id; mod edge_tags_set; mod edges; -mod id_newtype; mod image_id; mod images; mod node_descs; diff --git a/crate/model/src/common/any_id.rs b/crate/model/src/common/any_id.rs index 630625d..6217df9 100644 --- a/crate/model/src/common/any_id.rs +++ b/crate/model/src/common/any_id.rs @@ -12,7 +12,7 @@ use crate::common::{EdgeId, NodeId, TagId}; #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct AnyId(Cow<'static, str>); -crate::common::id_newtype!(AnyId, AnyIdInvalidFmt, node_id); +id_newtype::id_newtype!(AnyId, AnyIdInvalidFmt, node_id); impl From for AnyId { fn from(node_id: NodeId) -> Self { diff --git a/crate/model/src/common/edge_id.rs b/crate/model/src/common/edge_id.rs index 096252f..f1a3146 100644 --- a/crate/model/src/common/edge_id.rs +++ b/crate/model/src/common/edge_id.rs @@ -23,7 +23,7 @@ use crate::common::AnyId; #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct EdgeId(Cow<'static, str>); -crate::common::id_newtype!(EdgeId, EdgeIdInvalidFmt, edge_id); +id_newtype::id_newtype!(EdgeId, EdgeIdInvalidFmt, edge_id); impl From for EdgeId { fn from(any_id: AnyId) -> Self { diff --git a/crate/model/src/common/id_newtype.rs b/crate/model/src/common/id_newtype.rs deleted file mode 100644 index c18c27d..0000000 --- a/crate/model/src/common/id_newtype.rs +++ /dev/null @@ -1,186 +0,0 @@ -/// Implements common behaviour for an ID type. -/// -/// The implemented behaviour includes: -/// -/// * `IdType::new` -/// * `IdType::new_unchecked` -/// * `IdType::is_valid_id` -/// * `IdType::into_inner` -/// * `std::ops::Deref` -/// * `std::ops::DerefMut` -/// * `std::fmt::Display` -/// * `std::str::FromStr` -/// * `TryFrom` -/// * `TryFrom<&'static str>` -/// -/// A separate error type is also generated, which indicates an invalid value -/// when the ID type is instantiated with `new`. -/// -/// # Usage -/// -/// ```rust -/// use std::borrow::Cow; -/// -/// // replace this with your ID type's macro -/// use dot_ix_static_check_macros::my_id_type; -/// use serde::{Deserialize, Serialize}; -/// -/// // Rename your ID type -/// #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] -/// pub struct MyIdType(Cow<'static, str>); -/// -/// crate::id_newtype!( -/// MyIdType, // Name of the ID type -/// MyIdTypeInvalidFmt, // Name of the invalid value error -/// my_id_type, // Name of the static check macro -/// ); -/// ``` -macro_rules! id_newtype { - ($ty_name:ident, $ty_err_name:ident, $macro_name:ident) => { - impl $ty_name { - #[doc = concat!("Returns a new `", stringify!($ty_name), "` if the given `&str` is valid.")] - /// - #[doc = concat!("Most users should use the [`", stringify!($macro_name), "!`] macro as this provides")] - /// compile time checks and returns a `const` value. - /// - #[doc = concat!("[`", stringify!($macro_name), "!`]: dot_ix_static_check_macros::profile")] - pub fn new(s: &'static str) -> Result> { - Self::try_from(s) - } - - #[doc = concat!("Returns a new `", stringify!($ty_name), "`.")] - /// - #[doc = concat!("Most users should use the [`", stringify!($macro_name), "!`] macro as this provides")] - /// compile time checks and returns a `const` value. - /// - #[doc = concat!("[`", stringify!($macro_name), "!`]: dot_ix_static_check_macros::profile")] - #[doc(hidden)] - pub const fn new_unchecked(s: &'static str) -> Self { - Self(std::borrow::Cow::Borrowed(s)) - } - - /// Returns whether the provided `&str` is a valid station identifier. - pub fn is_valid_id(proposed_id: &str) -> bool { - let mut chars = proposed_id.chars(); - let first_char = chars.next(); - let first_char_valid = first_char - .map(|c| c.is_ascii_alphabetic() || c == '_') - .unwrap_or(false); - let remainder_chars_valid = - chars.all(|c| c.is_ascii_alphabetic() || c == '_' || c.is_ascii_digit()); - - first_char_valid && remainder_chars_valid - } - - /// Returns the inner `Cow<'static, str>`. - pub fn into_inner(self) -> Cow<'static, str> { - self.0 - } - - /// Returns the `&str` held by this ID. - pub fn as_str(&self) -> &str { - &self.0 - } - } - - impl std::ops::Deref for $ty_name { - type Target = std::borrow::Cow<'static, str>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl std::fmt::Display for $ty_name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.0) - } - } - - impl TryFrom for $ty_name { - type Error = $ty_err_name<'static>; - - fn try_from(s: String) -> Result<$ty_name, $ty_err_name<'static>> { - if Self::is_valid_id(&s) { - Ok($ty_name(std::borrow::Cow::Owned(s))) - } else { - let s = std::borrow::Cow::Owned(s); - Err($ty_err_name::new(s)) - } - } - } - - impl TryFrom<&'static str> for $ty_name { - type Error = $ty_err_name<'static>; - - fn try_from(s: &'static str) -> Result<$ty_name, $ty_err_name<'static>> { - if Self::is_valid_id(s) { - Ok($ty_name(std::borrow::Cow::Borrowed(s))) - } else { - let s = std::borrow::Cow::Borrowed(s); - Err($ty_err_name::new(s)) - } - } - } - - impl std::str::FromStr for $ty_name { - type Err = $ty_err_name<'static>; - - fn from_str(s: &str) -> Result<$ty_name, $ty_err_name<'static>> { - if Self::is_valid_id(s) { - Ok($ty_name(std::borrow::Cow::Owned(String::from(s)))) - } else { - let s = std::borrow::Cow::Owned(String::from(s)); - Err($ty_err_name::new(s)) - } - } - } - - impl std::borrow::Borrow for $ty_name { - fn borrow(&self) -> &str { - &self.0 - } - } - - impl<'s> std::borrow::Borrow for &'s $ty_name { - fn borrow(&self) -> &str { - &self.0 - } - } - - #[doc = concat!("Error indicating `", stringify!($ty_name), "` provided is not in the correct format.")] - #[derive(Debug, PartialEq, Eq)] - pub struct $ty_err_name<'s> { - /// String that was provided for the `$ty_name`. - value: std::borrow::Cow<'s, str>, - } - - impl<'s> $ty_err_name<'s> { - #[doc = concat!("Returns a new `", stringify!($ty_err_name), "` error.")] - pub fn new(value: std::borrow::Cow<'s, str>) -> Self { - Self { value } - } - - #[doc = concat!("Returns the value that failed to be parsed as a [`", stringify!($ty_name), "`].")] - pub fn value(&self) -> &std::borrow::Cow<'s, str> { - &self.value - } - } - - impl<'s> std::fmt::Display for $ty_err_name<'s> { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "`{value}` is not a valid `{ty_name}`.\n\ - `{ty_name}`s must begin with a letter or underscore, and contain only letters, numbers, or underscores.", - ty_name = stringify!($ty_name), - value = self.value - ) - } - } - - impl<'s> std::error::Error for $ty_err_name<'s> {} - }; -} - -pub(crate) use id_newtype; diff --git a/crate/model/src/common/image_id.rs b/crate/model/src/common/image_id.rs index a6f209f..167e55c 100644 --- a/crate/model/src/common/image_id.rs +++ b/crate/model/src/common/image_id.rs @@ -23,7 +23,7 @@ use crate::common::AnyId; #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct ImageId(Cow<'static, str>); -crate::common::id_newtype!(ImageId, ImageIdInvalidFmt, image_id); +id_newtype::id_newtype!(ImageId, ImageIdInvalidFmt, image_id); impl From for ImageId { fn from(any_id: AnyId) -> Self { diff --git a/crate/model/src/common/node_id.rs b/crate/model/src/common/node_id.rs index 7ef0c57..2e10ed4 100644 --- a/crate/model/src/common/node_id.rs +++ b/crate/model/src/common/node_id.rs @@ -23,7 +23,7 @@ use crate::common::AnyId; #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct NodeId(Cow<'static, str>); -crate::common::id_newtype!(NodeId, NodeIdInvalidFmt, node_id); +id_newtype::id_newtype!(NodeId, NodeIdInvalidFmt, node_id); impl From for NodeId { fn from(any_id: AnyId) -> Self { diff --git a/crate/model/src/common/tag_id.rs b/crate/model/src/common/tag_id.rs index 55df28e..4929f94 100644 --- a/crate/model/src/common/tag_id.rs +++ b/crate/model/src/common/tag_id.rs @@ -23,7 +23,7 @@ use crate::common::AnyId; #[derive(Clone, Debug, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct TagId(Cow<'static, str>); -crate::common::id_newtype!(TagId, TagIdInvalidFmt, tag_id); +id_newtype::id_newtype!(TagId, TagIdInvalidFmt, tag_id); impl From for TagId { fn from(any_id: AnyId) -> Self { diff --git a/crate/model/src/lib.rs b/crate/model/src/lib.rs index 9ac577a..b362f24 100644 --- a/crate/model/src/lib.rs +++ b/crate/model/src/lib.rs @@ -129,6 +129,9 @@ //! - tag_2: "Tag 2" //! ``` +#[macro_use] +extern crate id_newtype; + // Re-exports pub use dot_ix_static_check_macros::{edge_id, node_id, tag_id}; pub use indexmap::IndexMap; diff --git a/crate/web_components/Cargo.toml b/crate/web_components/Cargo.toml index 02d4c79..733714e 100644 --- a/crate/web_components/Cargo.toml +++ b/crate/web_components/Cargo.toml @@ -24,14 +24,11 @@ http = { workspace = true } leptos = { workspace = true } leptos_axum = { workspace = true, optional = true } leptos_meta = { workspace = true } -serde = { workspace = true, optional = true, features = ["derive"] } tempfile = { workspace = true, optional = true } -tokio = { workspace = true, optional = true } thiserror = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = { workspace = true } -serde = { workspace = true, optional = true, features = ["derive"] } serde-wasm-bindgen = { version = "0.6.5" } wasm-bindgen = { workspace = true } web-sys = { workspace = true, features = ["Document", "Element", "HtmlElement", "Url", "UrlSearchParams", "Window"] } @@ -44,7 +41,3 @@ ssr = [ "leptos/ssr", "leptos_meta/ssr", ] -server_side_graphviz = [ - "dep:serde", - "dep:tokio", -] diff --git a/crate/web_components/src/dot_svg.rs b/crate/web_components/src/dot_svg.rs index fee8b17..3a3e1b1 100644 --- a/crate/web_components/src/dot_svg.rs +++ b/crate/web_components/src/dot_svg.rs @@ -1,36 +1,18 @@ +use dot_ix_model::{common::DotSrcAndStyles, info_graph::InfoGraph}; use leptos::{ component, - prelude::{ClassAttribute, ElementChild, Get, Signal}, + html::Div, + prelude::{ + ClassAttribute, Effect, ElementChild, Get, GlobalAttributes, GlobalOnAttributes, NodeRef, + NodeRefAttribute, Signal, + }, view, IntoView, }; +use leptos_meta::Script; -#[cfg(not(feature = "server_side_graphviz"))] -use leptos::prelude::{Effect, NodeRef}; - -use dot_ix_model::{common::DotSrcAndStyles, info_graph::InfoGraph}; - -#[cfg(feature = "server_side_graphviz")] -use leptos::{ - prelude::{InnerHtmlAttribute, Resource, ServerFnError}, - server, - server_fn::error::NoCustomError, - suspense::Suspense, -}; - -#[cfg(any( - all(feature = "ssr", feature = "server_side_graphviz"), - target_arch = "wasm32" -))] +#[cfg(target_arch = "wasm32")] use dot_ix_model::common::{dot_src_and_styles::GraphvizImage, Images}; -#[cfg(not(feature = "server_side_graphviz"))] -use leptos::prelude::{GlobalAttributes, GlobalOnAttributes, NodeRefAttribute}; - -#[cfg(not(feature = "server_side_graphviz"))] -use leptos::html::Div; -#[cfg(not(feature = "server_side_graphviz"))] -use leptos_meta::Script; - cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; @@ -41,74 +23,14 @@ cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { } }} -#[cfg(not(feature = "server_side_graphviz"))] const SVG_WRITE_TO_CLIPBOARD: &str = include_str!("dot_svg/svg_write_to_clipboard.js"); -#[cfg(feature = "server_side_graphviz")] -#[server] -pub async fn dot_svg( - info_graph: InfoGraph, - dot_src_and_styles: DotSrcAndStyles, -) -> Result<(String, String), ServerFnError> { - use std::process::Stdio; - use tokio::io::{AsyncReadExt, AsyncWriteExt}; - - let DotSrcAndStyles { - dot_src, - styles: _, - opts: _, - theme_warnings: _, - } = dot_src_and_styles; - - let mut dot_process = tokio::process::Command::new("dot") - .arg("-Tsvg") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .expect("Failed to spawn dot command"); - - if let Some(mut stdin) = dot_process.stdin.take() { - stdin - .write_all(dot_src.as_bytes()) - .await - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - } - - let mut dot_svg = String::with_capacity(dot_src.len()); - if let Some(mut stdout) = dot_process.stdout.take() { - stdout - .read_to_string(&mut dot_svg) - .await - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - } - - let mut dot_stderr = String::new(); - if let Some(mut stderr) = dot_process.stderr.take() { - stderr - .read_to_string(&mut dot_stderr) - .await - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - } - - dot_process.wait().await.map_err(|error| { - ServerFnError::::ServerError(format!("{dot_stderr}{error}")) - })?; - - let styles = dot_svg_styles(&dot_src).await?; - - let images = info_graph.images(); - let image_defs = svg_image_defs(images); - dot_svg = dot_svg_sanitize(&dot_svg, &styles, images, &image_defs); - dot_svg = dot_svg_append_extra(&dot_svg, info_graph.svg_extra()); - - Ok((dot_svg, dot_stderr)) +#[cfg(target_arch = "wasm32")] +fn dot_svg_append_extra(dot_svg: &str, svg_extra: &str) -> String { + dot_svg.replacen(" String { images .iter() @@ -138,10 +60,7 @@ fn svg_image_defs(images: &Images) -> String { /// * `dot_svg`: The SVG generated by `dot`. /// * `styles`: The CSS styles to include within the `` /// element. -#[cfg(any( - all(feature = "ssr", feature = "server_side_graphviz"), - target_arch = "wasm32" -))] +#[cfg(target_arch = "wasm32")] fn dot_svg_sanitize(dot_svg: &str, styles: &str, images: &Images, images_defs: &str) -> String { let dot_svg = dot_svg .replacen("{styles}\n String { - dot_svg.replacen(" Result> { - use std::process::Stdio; - use tokio::io::AsyncReadExt; - - let tempdir = tempfile::tempdir() - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - - let dot_path = tempdir.path().join("dot.dot"); - let dot_write = tokio::fs::write(&dot_path, dot_src); - - let tailwind_config_path = tempdir.path().join("tailwind.config.js"); - let tailwind_config_write = tokio::fs::write( - &tailwind_config_path, - b"/** @type {import('tailwindcss').Config} */\nmodule.exports = { content: ['./dot.dot'] }", - ); - - let tailwind_css_path = tempdir.path().join("tailwind.css"); - let tailwind_css_write = tokio::fs::write( - &tailwind_css_path, - b"\n@tailwind components;\n@tailwind utilities;\n", - ); - - let ((), (), ()) = tokio::try_join!(tailwind_config_write, dot_write, tailwind_css_write,) - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - - let mut tailwind_process = tokio::process::Command::new("tailwind") - .current_dir(tempdir.path()) - .arg("-i") - .arg(&tailwind_css_path) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - - let mut svg_styles = String::with_capacity(dot_src.len()); - if let Some(mut stdout) = tailwind_process.stdout.take() { - stdout - .read_to_string(&mut svg_styles) - .await - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - } - - let mut tailwind_stderr = String::new(); - if let Some(mut stderr) = tailwind_process.stderr.take() { - stderr - .read_to_string(&mut tailwind_stderr) - .await - .map_err(|error| ServerFnError::::ServerError(format!("{error}")))?; - } - - tailwind_process.wait().await.map_err(|error| { - ServerFnError::::ServerError(format!("{tailwind_stderr}{error}")) - })?; - - Ok(svg_styles) -} - -/// Renders a graphviz graph as an SVG. -#[cfg(feature = "server_side_graphviz")] -#[component] -pub fn DotSvg( - info_graph: Signal, - dot_src_and_styles: Signal>, - #[prop(default = Signal::from(false))] diagram_only: Signal, -) -> impl IntoView { - let _diagram_only = diagram_only; - let dot_svg_and_error_resource = Resource::new( - move || dot_src_and_styles.get(), - move |dot_src_and_styles| async move { - if let Some(dot_src_and_styles) = dot_src_and_styles { - if !dot_src_and_styles.dot_src.is_empty() { - let info_graph = info_graph.get().clone(); - match dot_svg(info_graph, dot_src_and_styles).await { - Ok((dot_svg, error_text)) => (dot_svg, error_text), - Err(error) => (String::new(), format!("{error}")), - } - } else { - (String::new(), String::new()) - } - } else { - (String::new(), String::new()) - } - }, - ); - - view! { - "Loading..."

} - > - { move || { - dot_svg_and_error_resource.get() - .map(|(dot_svg, error_text)| { - let error_text_empty = error_text.is_empty(); - view! { -
-
- -
{error_text}
-
- } - }) - }} - - } -} - /// Renders a graphviz graph as an SVG. /// /// TODO: Use `postcss` to generate styles on the client side via function. @@ -338,7 +117,6 @@ pub fn DotSvg( /// /// const css = generateTailwindCss('
HTML content
') /// ``` -#[cfg(not(feature = "server_side_graphviz"))] #[component] pub fn DotSvg( info_graph: Signal, diff --git a/deny.toml b/deny.toml index 8b38dad..f1ded20 100644 --- a/deny.toml +++ b/deny.toml @@ -46,7 +46,7 @@ all-features = true no-default-features = false # If set, these feature will be enabled when collecting metadata. If `--features` # is specified on the cmd line they will take precedence over this option. -features = ["server_side_graphviz"] +features = [] [output] # When outputting inclusion graphs in diagnostics that include features, this @@ -70,11 +70,11 @@ yanked = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - # `derivative` is unmaintained. + # `paste` is unmaintained. # - # Transitive dependency of `log4rs`. - # Pending . - "RUSTSEC-2024-0388", + # Transitive dependency of `leptos` -> `either_of` -> `paste`. + # . + "RUSTSEC-2024-0436", ] # If this is true, then cargo deny will use the git executable to fetch advisory database. @@ -96,7 +96,7 @@ version = 2 allow = [ "Apache-2.0", "BSD-2-Clause", - "BSD-3-Clause", # wasm + "BSD-3-Clause", # wasm "BSL-1.0", "MIT", "Unicode-DFS-2016", @@ -137,8 +137,8 @@ exceptions = [ # and the crate will be checked normally, which may produce warnings or errors # depending on the rest of your configuration #license-files = [ - # Each entry is a crate relative path, and the (opaque) hash of its contents - #{ path = "LICENSE", hash = 0xbd0eed23 } +# Each entry is a crate relative path, and the (opaque) hash of its contents +#{ path = "LICENSE", hash = 0xbd0eed23 } #] [licenses.private] diff --git a/playground/Cargo.toml b/playground/Cargo.toml index 45ae046..45597de 100644 --- a/playground/Cargo.toml +++ b/playground/Cargo.toml @@ -20,7 +20,7 @@ axum = { workspace = true, optional = true } console_error_panic_hook = { workspace = true } console_log = { workspace = true } cfg-if = { workspace = true } -dot_ix = { version = "0.9.2", path = ".." } +dot_ix = { version = "0.10.0", path = ".." } gloo-net = { workspace = true, features = ["http"] } leptos = { workspace = true } leptos_axum = { workspace = true, optional = true } @@ -69,7 +69,6 @@ ssr = [ csr = [ "leptos/csr", ] -server_side_graphviz = ["dot_ix/server_side_graphviz"] [package.metadata.cargo-all-features] denylist = ["axum", "tokio", "tower", "tower-http", "leptos_axum"] @@ -89,7 +88,7 @@ site-pkg-dir = "pkg" # [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to //app.css # style-file = "style/main.scss" tailwind-input-file = "style/tailwind.css" -tailwind-config-file = "style/tailwind.config.js" +# tailwind-config-file = "style/tailwind.config.js" # Assets source dir. All files found here will be copied and synchronized to site-root. # The assets-dir cannot have a sub directory with the same name/path as site-pkg-dir. # diff --git a/playground/Trunk.toml b/playground/Trunk.toml deleted file mode 100644 index c790f43..0000000 --- a/playground/Trunk.toml +++ /dev/null @@ -1,6 +0,0 @@ -[[hooks]] -stage = "pre_build" -command = "cargo" -command_arguments = ["leptos", "build", "-v"] -# command = "sh" -# command_arguments = ["-c", "npx tailwindcss -c style/tailwind.config.js -i style/tailwind.css -o target/site/pkg/dot_ix.css"] diff --git a/playground/index.html b/playground/index.html index 9b2f066..7b0f67a 100644 --- a/playground/index.html +++ b/playground/index.html @@ -13,11 +13,10 @@ Would be good if we can just specify `target/site`, and `trunk` copies the contents of the `site` directory. --> - - - - - + + + + diff --git a/playground/src/lib.rs b/playground/src/lib.rs index 6a676d0..be404d7 100644 --- a/playground/src/lib.rs +++ b/playground/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + pub mod app; cfg_if::cfg_if! { if #[cfg(feature = "hydrate")] { diff --git a/playground/src/main.rs b/playground/src/main.rs index cd9113c..e2439d6 100644 --- a/playground/src/main.rs +++ b/playground/src/main.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + mod app; #[cfg(feature = "ssr")] diff --git a/playground/style/tailwind.config.js b/playground/style/tailwind.config.js deleted file mode 100644 index a7a6f2b..0000000 --- a/playground/style/tailwind.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - // Relative to workspace root / where you run `cargo leptos` from. - '**/src/**/*.rs', - ], - theme: { - extend: {}, - }, - plugins: [], -} diff --git a/playground/style/tailwind.css b/playground/style/tailwind.css index b5c61c9..f1d8c73 100644 --- a/playground/style/tailwind.css +++ b/playground/style/tailwind.css @@ -1,3 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; diff --git a/rustfmt.toml b/rustfmt.toml index 008ee43..51fa082 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,7 +1,7 @@ -imports_granularity = 'crate' +edition = "2024" +format_code_in_doc_comments = true +imports_granularity = "Crate" reorder_impl_items = true +style_edition = "2021" use_field_init_shorthand = true -format_code_in_doc_comments = true wrap_comments = true -edition = "2021" -style_edition = "2021"