Compare commits

..

9 Commits

Author SHA1 Message Date
Greg Johnston
05055d5015 fmt 2024-11-11 19:58:17 -05:00
Greg Johnston
b9dd0ffbd2 Merge branch 'main' into 0.7.0-docs 2024-11-11 17:37:06 -05:00
Greg Johnston
3b2e541133 Update router/src/ssr_mode.rs
Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
2024-11-11 17:36:41 -05:00
Greg Johnston
22a1aecaa9 Update router/src/ssr_mode.rs
Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
2024-11-11 17:36:35 -05:00
Greg Johnston
5327fea1a7 Update router/src/ssr_mode.rs
Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
2024-11-11 17:36:31 -05:00
Greg Johnston
1b4a16bc86 Update router/src/ssr_mode.rs
Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
2024-11-11 17:36:27 -05:00
Greg Johnston
8ee7a2072e Update router/src/ssr_mode.rs
Co-authored-by: Rakshith Ravi <rakshith.ravi@gmx.com>
2024-11-11 17:36:23 -05:00
Greg Johnston
64debdc29a fmt 2024-11-09 19:38:08 -05:00
Greg Johnston
bb271c0727 chore: add missing docs for 0.7 2024-11-08 16:20:12 -05:00
41 changed files with 225 additions and 5602 deletions

View File

@@ -1,49 +0,0 @@
name: autofix.ci
on:
pull_request:
# Running this workflow on main branch pushes requires write permission to apply changes.
# Leave it alone for future uses.
# push:
# branches: ["main"]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
autofix:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
with: {toolchain: nightly, components: "rustfmt, clippy", target: "wasm32-unknown-unknown", rustflags: ""}
- name: Install jq
run: sudo apt-get install jq
- run: |
echo "Formatting the workspace"
cargo fmt --all
echo "Running Clippy against each member's features (default features included)"
for member in $(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | .name'); do
echo "Working on member $member":
echo -e "\tdefault-features/no-features:"
# this will also run on members with no features or default features
cargo clippy --allow-dirty --fix --lib --package "$member"
features=$(cargo metadata --no-deps --format-version 1 | jq -r ".packages[] | select(.name == \"$member\") | .features | keys[]")
for feature in $features; do
if [ "$feature" = "default" ]; then
continue
fi
echo -e "\tfeature $feature"
cargo clippy --allow-dirty --fix --lib --package "$member" --features "$feature"
done
done
- uses: autofix-ci/action@v1.3.1
if: ${{ always() }}
with:
fail-fast: false

1
.gitignore vendored
View File

@@ -14,4 +14,3 @@ blob.rs
.vscode
vendor
hash.txt

48
Cargo.lock generated
View File

@@ -278,7 +278,7 @@ dependencies = [
"async-executor",
"futures",
"glib",
"thiserror 2.0.3",
"thiserror 2.0.0",
"tokio",
"tracing",
"wasm-bindgen-futures",
@@ -400,9 +400,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "axum"
version = "0.7.8"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49c41b948da08fb481a94546cd874843adc1142278b0af4badf9b1b78599d68d"
checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
dependencies = [
"async-trait",
"axum-core",
@@ -1171,9 +1171,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gio-sys"
version = "0.20.6"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b965df6f3534c84816b5c1a7d9efcb5671ae790822de5abe8e299797039529bc"
checksum = "217f464cad5946ae4369c355155e2d16b488c08920601083cb4891e352ae777b"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -1184,9 +1184,9 @@ dependencies = [
[[package]]
name = "glib"
version = "0.20.6"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86bd3e4ee7998ab5a135d900db56930cc19ad16681adf245daff54f618b9d5e1"
checksum = "358431b0e0eb15b9d02db52e1f19c805b953c5c168099deb3de88beab761768c"
dependencies = [
"bitflags",
"futures-channel",
@@ -1218,9 +1218,9 @@ dependencies = [
[[package]]
name = "glib-sys"
version = "0.20.6"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0b1827e8621fc42c0dfb228e5d57ff6a71f9699e666ece8113f979ad87c2de"
checksum = "8a5911863ab7ecd4a6f8d5976f12eeba076b23669c49b066d877e742544aa389"
dependencies = [
"libc",
"system-deps",
@@ -1792,7 +1792,7 @@ dependencies = [
"server_fn",
"slotmap",
"tachys",
"thiserror 2.0.3",
"thiserror 2.0.0",
"throw_error",
"tracing",
"typed-builder",
@@ -1870,7 +1870,7 @@ dependencies = [
"serde",
"temp-env",
"tempfile",
"thiserror 2.0.3",
"thiserror 2.0.0",
"tokio",
"typed-builder",
]
@@ -1981,7 +1981,7 @@ dependencies = [
"reactive_graph",
"send_wrapper",
"tachys",
"thiserror 2.0.3",
"thiserror 2.0.0",
"tracing",
"url",
"wasm-bindgen",
@@ -2291,7 +2291,7 @@ version = "0.2.0"
dependencies = [
"serde",
"serde_json",
"thiserror 2.0.3",
"thiserror 2.0.0",
]
[[package]]
@@ -2739,7 +2739,7 @@ dependencies = [
"send_wrapper",
"serde",
"slotmap",
"thiserror 2.0.3",
"thiserror 2.0.0",
"tokio",
"tokio-test",
"tracing",
@@ -3156,9 +3156,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.215"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
@@ -3197,9 +3197,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.215"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
@@ -3290,7 +3290,7 @@ dependencies = [
"serde_json",
"serde_qs",
"server_fn_macro_default",
"thiserror 2.0.3",
"thiserror 2.0.0",
"throw_error",
"tower",
"tower-layer",
@@ -3627,11 +3627,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.3"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
checksum = "15291287e9bff1bc6f9ff3409ed9af665bec7a5fc8ac079ea96be07bca0e2668"
dependencies = [
"thiserror-impl 2.0.3",
"thiserror-impl 2.0.0",
]
[[package]]
@@ -3647,9 +3647,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "2.0.3"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
checksum = "22efd00f33f93fa62848a7cab956c3d38c8d43095efda1decfc2b3a5dc0b8972"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -11,7 +11,7 @@ edition.workspace = true
[dependencies]
async-executor = { version = "1.13.1", optional = true }
futures = "0.3.31"
glib = { version = "0.20.6", optional = true }
glib = { version = "0.20.5", optional = true }
thiserror = "2.0"
tokio = { version = "1.41", optional = true, default-features = false, features = [
"rt",

View File

@@ -12,7 +12,7 @@ lto = true
[dependencies]
console_error_panic_hook = "0.1.7"
leptos = { path = "../../leptos", features = ["islands"] }
leptos = { path = "../../leptos", features = ["experimental-islands"] }
leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta" }
leptos_router = { path = "../../router" }

View File

@@ -12,7 +12,7 @@ futures = "0.3.30"
http = "1.1"
leptos = { path = "../../leptos", features = [
"tracing",
"islands",
"experimental-islands",
] }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }
leptos_axum = { path = "../../integrations/axum", optional = true }

View File

@@ -12,7 +12,7 @@ futures = "0.3.30"
http = "1.1"
leptos = { path = "../../leptos", features = [
"tracing",
"islands",
"experimental-islands",
] }
leptos_router = { path = "../../router" }
server_fn = { path = "../../server_fn", features = ["serde-lite"] }

View File

@@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
actix-files = { version = "0.6.6", optional = true }
actix-web = { version = "4.8", optional = true, features = ["macros"] }
console_error_panic_hook = "0.1.7"
js-sys = { version = "0.3.72" }
js-sys = { version = "0.3.70", optional = true }
leptos = { path = "../../leptos" }
leptos_actix = { path = "../../integrations/actix", optional = true }
leptos_router = { path = "../../router" }
@@ -21,7 +21,7 @@ tokio = { version = "1.39", features = ["time", "rt"], optional = true }
[features]
hydrate = [
"dep:js-sys",
"leptos/hydrate",
]
ssr = [

View File

@@ -1,12 +0,0 @@
@check_aria_current
Feature: Check aria-current being applied to make links bolded
Background:
Given I see the app
Scenario: Should see the base case working
Then I see the link Out-of-Order being bolded
Then I see the following links being bolded
| Out-of-Order |
| Nested |

View File

@@ -81,12 +81,3 @@ pub async fn instrumented_counts(
Ok(())
}
pub async fn link_text_is_aria_current(client: &Client, text: &str) -> Result<()> {
let link = find::link_with_text(client, text).await?;
link.attr("aria-current").await?
.expect(format!("aria-current missing for {text}").as_str());
Ok(())
}

View File

@@ -124,12 +124,3 @@ async fn component_message(client: &Client, id: &str) -> Result<String> {
Ok(text)
}
pub async fn link_with_text(client: &Client, text: &str) -> Result<Element> {
let link = client
.wait()
.for_element(Locator::LinkText(text))
.await
.expect(format!("Link not found by `{}`", text).as_str());
Ok(link)
}

View File

@@ -80,32 +80,6 @@ async fn i_see_the_second_count_is(
Ok(())
}
#[then(regex = r"^I see the link (.*) being bolded$")]
async fn i_see_the_link_being_bolded(
world: &mut AppWorld,
text: String,
) -> Result<()> {
let client = &world.client;
check::link_text_is_aria_current(client, &text).await?;
Ok(())
}
#[then(expr = "I see the following links being bolded")]
async fn i_see_the_following_links_being_bolded(
world: &mut AppWorld,
step: &Step,
) -> Result<()> {
let client = &world.client;
if let Some(table) = step.table.as_ref() {
for row in table.rows.iter() {
check::link_text_is_aria_current(client, &row[0]).await?;
}
}
Ok(())
}
#[then(expr = "I see the following counters under section")]
#[then(expr = "the following counters under section")]
async fn i_see_the_following_counters_under_section(

View File

@@ -11,7 +11,7 @@ edition.workspace = true
[dependencies]
any_spawner = { workspace = true, features = ["tokio"] }
hydration_context = { workspace = true }
axum = { version = "0.7.8", default-features = false, features = [
axum = { version = "0.7.7", default-features = false, features = [
"matched-path",
] }
dashmap = "6"
@@ -30,7 +30,7 @@ tower-http = "0.6.1"
tracing = { version = "0.1.40", optional = true }
[dev-dependencies]
axum = "0.7.8"
axum = "0.7.7"
tokio = { version = "1.41", features = ["net", "rt-multi-thread"] }
[features]

View File

@@ -16,7 +16,7 @@
//! - `default`: supports running in a typical native Tokio/Axum environment
//! - `wasm`: with `default-features = false`, supports running in a JS Fetch-based
//! environment
//! - `islands`: activates Leptos [islands mode](https://leptos-rs.github.io/leptos/islands.html)
//! - `experimental-islands`: activates Leptos [islands mode](https://leptos-rs.github.io/leptos/islands.html)
//!
//! ### Important Note
//! Prior to 0.5, using `default-features = false` on `leptos_axum` simply did nothing. Now, it actively

View File

@@ -86,7 +86,7 @@ tracing = [
]
nonce = ["base64", "rand"]
spin = ["leptos-spin-macro"]
islands = ["leptos_macro/islands", "dep:serde_json"]
experimental-islands = ["leptos_macro/experimental-islands", "dep:serde_json"]
trace-component-props = [
"leptos_macro/trace-component-props",
"leptos_dom/trace-component-props"
@@ -104,7 +104,7 @@ denylist = [
"rkyv", # was causing clippy issues on nightly
"trace-component-props",
"spin",
"islands",
"experimental-islands",
]
skip_feature_sets = [
["csr", "ssr"],

View File

@@ -223,14 +223,14 @@ mod tests {
#[test]
fn clone_callback() {
let callback = Callback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback;
let _cloned = callback.clone();
}
#[test]
fn clone_unsync_callback() {
let callback =
UnsyncCallback::new(move |_no_clone: NoClone| NoClone {});
let _cloned = callback;
let _cloned = callback.clone();
}
#[test]

View File

@@ -47,7 +47,7 @@ pub fn AutoReload(
pub fn HydrationScripts(
/// Configuration options for this project.
options: LeptosOptions,
/// Should be `true` to hydrate in `islands` mode.
/// Should be `true` to hydrate in `experimental-islands` mode.
#[prop(optional)]
islands: bool,
/// A base url, not including a trailing slash

View File

@@ -318,10 +318,10 @@ pub mod task {
}
// these reexports are used in islands
#[cfg(feature = "islands")]
#[cfg(feature = "experimental-islands")]
#[doc(hidden)]
pub use serde;
#[cfg(feature = "islands")]
#[cfg(feature = "experimental-islands")]
#[doc(hidden)]
pub use serde_json;
#[cfg(feature = "tracing")]

File diff suppressed because it is too large Load Diff

View File

@@ -30,14 +30,14 @@ fn ws_from_str_test() {
#[test]
fn env_w_default_test() {
temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("custom")
);
});
temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(
env_w_default("LEPTOS_CONFIG_ENV_TEST", "default").unwrap(),
String::from("default")
@@ -47,14 +47,14 @@ fn env_w_default_test() {
#[test]
fn env_wo_default_test() {
temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
_ = temp_env::with_var("LEPTOS_CONFIG_ENV_TEST", Some("custom"), || {
assert_eq!(
env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(),
Some(String::from("custom"))
);
});
temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
_ = temp_env::with_var_unset("LEPTOS_CONFIG_ENV_TEST", || {
assert_eq!(env_wo_default("LEPTOS_CONFIG_ENV_TEST").unwrap(), None);
});
}

View File

@@ -44,7 +44,7 @@ hydrate = []
ssr = ["server_fn_macro/ssr", "leptos/ssr"]
nightly = ["server_fn_macro/nightly"]
tracing = ["dep:tracing"]
islands = []
experimental-islands = []
trace-component-props = []
actix = ["server_fn_macro/actix"]
axum = ["server_fn_macro/axum"]

View File

@@ -559,10 +559,10 @@ pub fn component(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
}
/// Defines a component as an interactive island when you are using the
/// `islands` feature of Leptos. Apart from the macro name,
/// `experimental-islands` feature of Leptos. Apart from the macro name,
/// the API is the same as the [`component`](macro@component) macro.
///
/// When you activate the `islands` feature, every `#[component]`
/// When you activate the `experimental-islands` feature, every `#[component]`
/// is server-only by default. This "default to server" behavior is important:
/// you opt into shipping code to the client, rather than opting out. You can
/// opt into client-side interactivity for any given component by changing from

View File

@@ -197,7 +197,7 @@ enum InertElementBuilder<'a> {
},
}
impl ToTokens for InertElementBuilder<'_> {
impl<'a> ToTokens for InertElementBuilder<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
InertElementBuilder::GlobalClass { strs, .. } => {
@@ -219,7 +219,7 @@ enum GlobalClassItem<'a> {
String(String),
}
impl ToTokens for GlobalClassItem<'_> {
impl<'a> ToTokens for GlobalClassItem<'a> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let addl_tokens = match self {
GlobalClassItem::Global(v) => v.to_token_stream(),

View File

@@ -70,7 +70,7 @@ pub enum Oco<'a, T: ?Sized + ToOwned + 'a> {
Owned(<T as ToOwned>::Owned),
}
impl<T: ?Sized + ToOwned> Oco<'_, T> {
impl<'a, T: ?Sized + ToOwned> Oco<'a, T> {
/// Converts the value into an owned value.
pub fn into_owned(self) -> <T as ToOwned>::Owned {
match self {
@@ -339,7 +339,7 @@ where
}
}
impl<'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'_, A>
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialEq<Oco<'b, B>> for Oco<'a, A>
where
A: PartialEq<B>,
A: ToOwned,
@@ -352,7 +352,7 @@ where
impl<T: ?Sized + ToOwned + Eq> Eq for Oco<'_, T> {}
impl<'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'_, A>
impl<'a, 'b, A: ?Sized, B: ?Sized> PartialOrd<Oco<'b, B>> for Oco<'a, A>
where
A: PartialOrd<B>,
A: ToOwned,
@@ -551,7 +551,7 @@ impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, &'b [T
impl_slice_eq!([T: PartialEq] (where [T]: ToOwned), Oco<'_, [T]>, Vec<T>);
impl_slice_eq!(['a, 'b, T: PartialEq] (where [T]: ToOwned), Oco<'a, [T]>, Cow<'b, [T]>);
impl<'b> Add<&'b str> for Oco<'_, str> {
impl<'a, 'b> Add<&'b str> for Oco<'a, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: &'b str) -> Self::Output {
@@ -559,7 +559,7 @@ impl<'b> Add<&'b str> for Oco<'_, str> {
}
}
impl<'b> Add<Cow<'b, str>> for Oco<'_, str> {
impl<'a, 'b> Add<Cow<'b, str>> for Oco<'a, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: Cow<'b, str>) -> Self::Output {
@@ -567,7 +567,7 @@ impl<'b> Add<Cow<'b, str>> for Oco<'_, str> {
}
}
impl<'b> Add<Oco<'b, str>> for Oco<'_, str> {
impl<'a, 'b> Add<Oco<'b, str>> for Oco<'a, str> {
type Output = Oco<'static, str>;
fn add(self, rhs: Oco<'b, str>) -> Self::Output {

View File

@@ -21,6 +21,6 @@ CREATE TABLE IF NOT EXISTS google_tokens (
access_secret TEXT NOT NULL,
refresh_secret TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
FOREIGN KEY (user_id) REFERENCES users(id) CONFLICT REPLACE
);

View File

@@ -64,7 +64,7 @@ pub async fn refresh_token(email: String) -> Result<u64, ServerFnError> {
.execute(&pool)
.await?;
sqlx::query(
"INSERT OR REPLACE INTO google_tokens (user_id,access_secret,refresh_secret) \
"INSERT INTO google_tokens (user_id,access_secret,refresh_secret) \
VALUES (?,?,?)",
)
.bind(user.id)

View File

@@ -96,7 +96,7 @@ async fn main() {
let client = oauth2::basic::BasicClient::new(
oauth2::ClientId::new(
std::env::var("G_AUTH_CLIENT_ID")
.expect("G_AUTH_CLIENT_ID Env var to be set."),
.expect("G_AUTH_CLIENT Env var to be set."),
),
Some(oauth2::ClientSecret::new(
std::env::var("G_AUTH_SECRET")

View File

@@ -64,7 +64,10 @@ where
} else {
(base.as_ref(), path)
};
path.strip_prefix(base)?
match path.strip_prefix(base) {
Some(path) => path,
None => return None,
}
}
};

View File

@@ -44,8 +44,8 @@ pub enum SsrMode {
/// of the page will not be interactive until the suspended chunks have loaded.
InOrder,
/// **`Async`**: Load all resources on the server. Wait until all data are loaded, and render HTML in one sweep.
/// - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.
/// - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.
/// - *Pros*: Better handling for meta tags (because you know async data even before you render the `<head>`). Faster complete load than **synchronous** because async resources begin loading on server.
/// - *Cons*: Slower load time/TTFB: you need to wait for all async resources to load before displaying anything on the client.
Async,
/// **`Static`**: Renders the page when the server starts up, or incrementally, using the
/// configuration provided by a [`StaticRoute`].

2555
server_fn/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ once_cell = "1.20"
actix-web = { version = "4.9", optional = true }
# axum
axum = { version = "0.7.8", optional = true, default-features = false, features = [
axum = { version = "0.7.7", optional = true, default-features = false, features = [
"multipart",
] }
tower = { version = "0.5.1", optional = true }

View File

@@ -5,11 +5,18 @@ use std::{
str::FromStr,
};
use thiserror::Error;
use throw_error::Error;
use url::Url;
/// A custom header that can be used to indicate a server function returned an error.
pub const SERVER_FN_ERROR_HEADER: &str = "serverfnerror";
impl From<ServerFnError> for Error {
fn from(e: ServerFnError) -> Self {
Error::from(ServerFnErrorErr::from(e))
}
}
/// An empty value indicating that there is no custom error type associated
/// with this server function.
#[derive(
@@ -129,39 +136,30 @@ impl<E> ViaError<E> for WrapError<E> {
/// Unlike [`ServerFnErrorErr`], this does not implement [`Error`](trait@std::error::Error).
/// This means that other error types can easily be converted into it using the
/// `?` operator.
#[derive(Debug, Error, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum ServerFnError<E = NoCustomError> {
/// A user-defined custom error type, which defaults to [`NoCustomError`].
#[error("internal error: {0}")]
WrappedServerError(E),
/// Error while trying to register the server function (only occurs in case of poisoned RwLock).
#[error("error while trying to register the server function: {0}")]
Registration(String),
/// Occurs on the client if there is a network error while trying to run function on server.
#[error("error reaching server to call server function: {0}")]
Request(String),
/// Occurs on the server if there is an error creating an HTTP response.
Response(String),
/// Occurs when there is an error while actually running the function on the server.
#[error("error running server function: {0}")]
ServerError(String),
/// Occurs on the client if there is an error deserializing the server's response.
#[error("error deserializing server function results: {0}")]
Deserialization(String),
/// Occurs on the client if there is an error serializing the server function arguments.
#[error("error serializing server function arguments: {0}")]
Serialization(String),
/// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
#[error("error deserializing server function arguments: {0}")]
Args(String),
/// Occurs on the server if there's a missing argument.
#[error("missing argument {0}")]
MissingArg(String),
/// Occurs on the server if there is an error creating an HTTP response.
#[error("error creating response {0}")]
Response(String),
}
impl ServerFnError<NoCustomError> {
@@ -177,6 +175,45 @@ impl<CustErr> From<CustErr> for ServerFnError<CustErr> {
}
}
impl<E: std::error::Error> From<E> for ServerFnError {
fn from(value: E) -> Self {
ServerFnError::ServerError(value.to_string())
}
}
impl<CustErr> Display for ServerFnError<CustErr>
where
CustErr: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
ServerFnError::Registration(s) => format!(
"error while trying to register the server function: {s}"
),
ServerFnError::Request(s) => format!(
"error reaching server to call server function: {s}"
),
ServerFnError::ServerError(s) =>
format!("error running server function: {s}"),
ServerFnError::Deserialization(s) =>
format!("error deserializing server function results: {s}"),
ServerFnError::Serialization(s) =>
format!("error serializing server function arguments: {s}"),
ServerFnError::Args(s) => format!(
"error deserializing server function arguments: {s}"
),
ServerFnError::MissingArg(s) => format!("missing argument {s}"),
ServerFnError::Response(s) =>
format!("error generating HTTP response: {s}"),
ServerFnError::WrappedServerError(e) => format!("{e}"),
}
)
}
}
/// A serializable custom server function error type.
///
/// This is implemented for all types that implement [`FromStr`] + [`Display`].
@@ -261,6 +298,87 @@ where
}
}
impl<E> std::error::Error for ServerFnError<E>
where
E: std::error::Error + 'static,
ServerFnError<E>: std::fmt::Display,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ServerFnError::WrappedServerError(e) => Some(e),
_ => None,
}
}
}
/// Type for errors that can occur when using server functions.
///
/// Unlike [`ServerFnError`], this implements [`std::error::Error`]. This means
/// it can be used in situations in which the `Error` trait is required, but its
/// not possible to create a blanket implementation that converts other errors into
/// this type.
///
/// [`ServerFnError`] and [`ServerFnErrorErr`] mutually implement [`From`], so
/// it is easy to convert between the two types.
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum ServerFnErrorErr<E = NoCustomError> {
/// A user-defined custom error type, which defaults to [`NoCustomError`].
#[error("internal error: {0}")]
WrappedServerError(E),
/// Error while trying to register the server function (only occurs in case of poisoned RwLock).
#[error("error while trying to register the server function: {0}")]
Registration(String),
/// Occurs on the client if there is a network error while trying to run function on server.
#[error("error reaching server to call server function: {0}")]
Request(String),
/// Occurs when there is an error while actually running the function on the server.
#[error("error running server function: {0}")]
ServerError(String),
/// Occurs on the client if there is an error deserializing the server's response.
#[error("error deserializing server function results: {0}")]
Deserialization(String),
/// Occurs on the client if there is an error serializing the server function arguments.
#[error("error serializing server function arguments: {0}")]
Serialization(String),
/// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
#[error("error deserializing server function arguments: {0}")]
Args(String),
/// Occurs on the server if there's a missing argument.
#[error("missing argument {0}")]
MissingArg(String),
/// Occurs on the server if there is an error creating an HTTP response.
#[error("error creating response {0}")]
Response(String),
}
impl<CustErr> From<ServerFnError<CustErr>> for ServerFnErrorErr<CustErr> {
fn from(value: ServerFnError<CustErr>) -> Self {
match value {
ServerFnError::Registration(value) => {
ServerFnErrorErr::Registration(value)
}
ServerFnError::Request(value) => ServerFnErrorErr::Request(value),
ServerFnError::ServerError(value) => {
ServerFnErrorErr::ServerError(value)
}
ServerFnError::Deserialization(value) => {
ServerFnErrorErr::Deserialization(value)
}
ServerFnError::Serialization(value) => {
ServerFnErrorErr::Serialization(value)
}
ServerFnError::Args(value) => ServerFnErrorErr::Args(value),
ServerFnError::MissingArg(value) => {
ServerFnErrorErr::MissingArg(value)
}
ServerFnError::WrappedServerError(value) => {
ServerFnErrorErr::WrappedServerError(value)
}
ServerFnError::Response(value) => ServerFnErrorErr::Response(value),
}
}
}
/// Associates a particular server function error with the server function
/// found at a particular path.
///
@@ -337,3 +455,9 @@ impl<CustErr> From<ServerFnUrlError<CustErr>> for ServerFnError<CustErr> {
error.error
}
}
impl<CustErr> From<ServerFnUrlError<CustErr>> for ServerFnErrorErr<CustErr> {
fn from(error: ServerFnUrlError<CustErr>) -> Self {
error.error.into()
}
}

View File

@@ -1,5 +1,7 @@
use super::Res;
use crate::error::{ServerFnError, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER};
use crate::error::{
ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER,
};
use actix_web::{
http::{
header,
@@ -73,7 +75,7 @@ where
builder
.insert_header((header::CONTENT_TYPE, content_type))
.streaming(
data.map(|data| data.map_err(|e| server_fn_error!(e))),
data.map(|data| data.map_err(ServerFnErrorErr::from)),
),
)))
}

View File

@@ -13,7 +13,9 @@
//! crate under the hood.
use super::Res;
use crate::error::{ServerFnError, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER};
use crate::error::{
ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER,
};
use bytes::Bytes;
use futures::{Stream, TryStreamExt};
use http::{header, HeaderValue, Response, StatusCode};
@@ -81,7 +83,7 @@ where
.status(200)
.header(http::header::CONTENT_TYPE, content_type)
.body(Body::Async(Box::pin(
data.map_err(|e| server_fn_error!(e)).map_err(Error::from),
data.map_err(ServerFnErrorErr::from).map_err(Error::from),
)))
.map_err(|e| ServerFnError::Response(e.to_string()))
}

View File

@@ -1,5 +1,7 @@
use super::Res;
use crate::error::{ServerFnError, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER};
use crate::error::{
ServerFnError, ServerFnErrorErr, ServerFnErrorSerde, SERVER_FN_ERROR_HEADER,
};
use axum::body::Body;
use bytes::Bytes;
use futures::{Stream, StreamExt};
@@ -44,7 +46,7 @@ where
+ 'static,
) -> Result<Self, ServerFnError<CustErr>> {
let body =
Body::from_stream(data.map(|n| n.map_err(|e| server_fn_error!(e))));
Body::from_stream(data.map(|n| n.map_err(ServerFnErrorErr::from)));
let builder = http::Response::builder();
builder
.status(200)

View File

@@ -267,7 +267,7 @@ impl<T: IntoClass> IntoClass for Option<T> {
}
}
impl IntoClass for &str {
impl<'a> IntoClass for &'a str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;

View File

@@ -308,7 +308,7 @@ impl InnerHtmlValue for Arc<str> {
}
}
impl InnerHtmlValue for &str {
impl<'a> InnerHtmlValue for &'a str {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Self);
type Cloneable = Self;

View File

@@ -606,7 +606,7 @@ impl<'a> IntoStyle for (&'a str, String) {
}
#[cfg(feature = "nightly")]
impl<const V: &'static str> IntoStyle for (&str, Static<V>) {
impl<'a, const V: &'static str> IntoStyle for (&'a str, Static<V>) {
type AsyncOutput = Self;
type State = ();
type Cloneable = (Arc<str>, Static<V>);

View File

@@ -209,6 +209,7 @@ pub trait DomRenderer: Renderer {
/// This works in a similar way to `TryFrom`. We implement it as a separate trait
/// simply so we don't have to create wrappers for the `web_sys` types; it can't be
/// implemented on them directly because of the orphan rules.
pub trait CastFrom<T>
where
Self: Sized,

View File

@@ -197,6 +197,7 @@ where
/// Renders a view to an out-of-order stream of HTML with branch markers. This can be used to support libraries that diff
/// HTML pages against one another, by marking sections of the view that branch to different
/// types with marker comments.
fn to_html_stream_out_of_order_branching(self) -> StreamBuilder
where
Self: Sized,
@@ -371,7 +372,7 @@ pub trait ToTemplate {
/// The `style` attribute content known at compile time.
const STYLE: &'static str = "";
/// The length of the template.
const LEN: usize = Self::TEMPLATE.len();
const LEN: usize = Self::TEMPLATE.as_bytes().len();
/// Renders a view type to a template. This does not take actual view data,
/// but can be used for constructing part of an HTML `<template>` that corresponds

View File

@@ -36,7 +36,7 @@ impl<'a> Render for &'a str {
}
}
impl RenderHtml for &str {
impl<'a> RenderHtml for &'a str {
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@@ -102,7 +102,7 @@ impl RenderHtml for &str {
}
}
impl ToTemplate for &str {
impl<'a> ToTemplate for &'a str {
const TEMPLATE: &'static str = " <!>";
fn to_template(
@@ -120,7 +120,7 @@ impl ToTemplate for &str {
}
}
impl Mountable for StrState<'_> {
impl<'a> Mountable for StrState<'a> {
fn unmount(&mut self) {
self.node.unmount()
}
@@ -451,7 +451,7 @@ impl<'a> Render for Cow<'a, str> {
}
}
impl RenderHtml for Cow<'_, str> {
impl<'a> RenderHtml for Cow<'a, str> {
type AsyncOutput = Self;
const MIN_LENGTH: usize = 0;
@@ -494,7 +494,7 @@ impl RenderHtml for Cow<'_, str> {
}
}
impl ToTemplate for Cow<'_, str> {
impl<'a> ToTemplate for Cow<'a, str> {
const TEMPLATE: &'static str = <&str as ToTemplate>::TEMPLATE;
fn to_template(
@@ -510,7 +510,7 @@ impl ToTemplate for Cow<'_, str> {
}
}
impl Mountable for CowStrState<'_> {
impl<'a> Mountable for CowStrState<'a> {
fn unmount(&mut self) {
self.node.unmount()
}