diff --git a/packages/perseus-actix-web/Cargo.toml b/packages/perseus-actix-web/Cargo.toml index 0432edc0d7..25d11ebb98 100644 --- a/packages/perseus-actix-web/Cargo.toml +++ b/packages/perseus-actix-web/Cargo.toml @@ -28,5 +28,6 @@ futures = "0.3" sycamore = { version = "=0.8.0-beta.7", features = ["ssr"] } [features] +default = [ "dflt-server" ] # Enables the default server configuration, which provides a convenience function if you're not adding any extra routes dflt-server = [ "perseus/builder" ] diff --git a/packages/perseus-actix-web/src/configurer.rs b/packages/perseus-actix-web/src/configurer.rs index 90ada52023..8687a8dd7b 100644 --- a/packages/perseus-actix-web/src/configurer.rs +++ b/packages/perseus-actix-web/src/configurer.rs @@ -45,7 +45,6 @@ pub async fn configurer, ) -> impl FnOnce(&mut actix_web::web::ServiceConfig) { - let opts = Rc::new(opts); // TODO Find a more efficient way of doing this let render_cfg = get_render_cfg(&immutable_store) .await .expect("Couldn't get render configuration!"); diff --git a/packages/perseus-actix-web/src/dflt_server.rs b/packages/perseus-actix-web/src/dflt_server.rs index 85fe50e8a0..1af16dd24f 100644 --- a/packages/perseus-actix-web/src/dflt_server.rs +++ b/packages/perseus-actix-web/src/dflt_server.rs @@ -3,7 +3,6 @@ use actix_web::{App, HttpServer}; use futures::executor::block_on; use perseus::{ internal::i18n::TranslationsManager, internal::serve::ServerProps, stores::MutableStore, - PerseusAppBase, SsrNode, }; /// Creates and starts the default Perseus server using Actix Web. This should be run in a `main()` function annotated with `#[tokio::main]` (which requires the `macros` and @@ -18,7 +17,7 @@ pub async fn dflt_server( req: HttpRequest, - opts: web::Data>, + opts: web::Data, html_shell: web::Data, render_cfg: web::Data>, immutable_store: web::Data, diff --git a/packages/perseus-actix-web/src/page_data.rs b/packages/perseus-actix-web/src/page_data.rs index 4775890be6..28c0ce9aca 100644 --- a/packages/perseus-actix-web/src/page_data.rs +++ b/packages/perseus-actix-web/src/page_data.rs @@ -10,7 +10,6 @@ use perseus::{ stores::{ImmutableStore, MutableStore}, }; use serde::Deserialize; -use std::rc::Rc; #[derive(Deserialize)] pub struct PageDataReq { @@ -22,7 +21,7 @@ pub struct PageDataReq { #[allow(clippy::too_many_arguments)] pub async fn page_data( req: HttpRequest, - opts: web::Data>, + opts: web::Data, immutable_store: web::Data, mutable_store: web::Data, translations_manager: web::Data, diff --git a/packages/perseus-actix-web/src/translations.rs b/packages/perseus-actix-web/src/translations.rs index 686fac1318..50464a79d4 100644 --- a/packages/perseus-actix-web/src/translations.rs +++ b/packages/perseus-actix-web/src/translations.rs @@ -2,13 +2,12 @@ use actix_web::{web, HttpRequest, HttpResponse}; use fmterr::fmt_err; use perseus::internal::i18n::TranslationsManager; use perseus::internal::serve::ServerOptions; -use std::rc::Rc; /// The handler for calls to `.perseus/translations/{locale}`. This will manage returning errors and the like. THe JSON body returned /// from this does NOT include the `locale` key, just a `HashMap` of the translations themselves. pub async fn translations( req: HttpRequest, - opts: web::Data>, + opts: web::Data, translations_manager: web::Data, ) -> HttpResponse { let locale = req.match_info().query("locale"); diff --git a/packages/perseus/src/engine/serve.rs b/packages/perseus/src/engine/serve.rs index 43cda1d1b6..cada3912d1 100644 --- a/packages/perseus/src/engine/serve.rs +++ b/packages/perseus/src/engine/serve.rs @@ -6,6 +6,7 @@ use crate::PerseusAppBase; use futures::executor::block_on; use std::env; use std::fs; +use std::sync::Arc; use sycamore::web::SsrNode; // TODO Can we unify the two modes of server execution now? @@ -67,7 +68,7 @@ pub fn get_props( locales: app.get_locales(), root_id: app_root, snippets: "dist/pkg/snippets".to_string(), - error_pages: app.get_error_pages(), + error_pages: Arc::new(app.get_error_pages()), // This will be available directly at `/.perseus/static` static_dir: if fs::metadata(&static_dir_path).is_ok() { Some(static_dir_path) diff --git a/packages/perseus/src/init.rs b/packages/perseus/src/init.rs index ba596a78c5..ad5ebb1d77 100644 --- a/packages/perseus/src/init.rs +++ b/packages/perseus/src/init.rs @@ -16,6 +16,8 @@ use futures::Future; use std::marker::PhantomData; #[cfg(not(target_arch = "wasm32"))] use std::pin::Pin; +#[cfg(not(target_arch = "wasm32"))] +use std::sync::Arc; use std::{collections::HashMap, rc::Rc}; use sycamore::prelude::Scope; use sycamore::{ @@ -97,8 +99,9 @@ pub struct PerseusAppBase { /// The app's error pages. error_pages: ErrorPagesGetter, /// The global state creator for the app. + // This is wrapped in an `Arc` so we can pass it around on the engine-side (which is solely for Actix's benefit...) #[cfg(not(target_arch = "wasm32"))] - global_state_creator: GlobalStateCreator, + global_state_creator: Arc, /// The internationalization information for the app. locales: Locales, /// The static aliases the app serves. @@ -221,7 +224,7 @@ impl PerseusAppBase { // We do offer default error pages, but they'll panic if they're called for production building error_pages: ErrorPagesGetter(Box::new(ErrorPages::default)), #[cfg(not(target_arch = "wasm32"))] - global_state_creator: GlobalStateCreator::default(), + global_state_creator: Arc::new(GlobalStateCreator::default()), // By default, we'll disable i18n (as much as I may want more websites to support more languages...) locales: Locales { default: "xx-XX".to_string(), @@ -307,7 +310,7 @@ impl PerseusAppBase { pub fn global_state_creator(mut self, val: GlobalStateCreator) -> Self { #[cfg(not(target_arch = "wasm32"))] { - self.global_state_creator = val; + self.global_state_creator = Arc::new(val); } self } @@ -623,7 +626,7 @@ impl PerseusAppBase { } /// Gets the global state creator. This can't be directly modified by plugins because of reactive type complexities. #[cfg(not(target_arch = "wasm32"))] - pub fn get_global_state_creator(&self) -> GlobalStateCreator { + pub fn get_global_state_creator(&self) -> Arc { self.global_state_creator.clone() } /// Gets the locales information. diff --git a/packages/perseus/src/server/options.rs b/packages/perseus/src/server/options.rs index ddb6ddb22c..12d8653434 100644 --- a/packages/perseus/src/server/options.rs +++ b/packages/perseus/src/server/options.rs @@ -6,12 +6,13 @@ use crate::stores::{ImmutableStore, MutableStore}; use crate::template::ArcTemplateMap; use crate::SsrNode; use std::collections::HashMap; +use std::sync::Arc; use super::HtmlShell; /// The options for setting up all server integrations. This should be literally constructed, as nothing is optional. If integrations need further properties, -/// they should expose their own options in addition to these. These should be accessed through an `Arc`/`Rc` for integration developers. -#[derive(Debug)] +/// they should expose their own options in addition to these. +#[derive(Debug, Clone)] pub struct ServerOptions { /// The location on the filesystem of your JavaScript bundle. pub js_bundle: String, @@ -32,7 +33,7 @@ pub struct ServerOptions { /// The location of the JS interop snippets to be served as static files. pub snippets: String, /// The error pages for the app. These will be server-rendered if an initial load fails. - pub error_pages: ErrorPages, + pub error_pages: Arc>, /// The directory to serve static content from, which will be mapped to `/.perseus/static`in the browser. pub static_dir: Option, /// A map of URLs to act as aliases for certain static resources. These are particularly designed for things like a site manifest or @@ -41,7 +42,7 @@ pub struct ServerOptions { } /// The full set of properties that all server integrations take. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ServerProps { /// The options for setting up the server. pub opts: ServerOptions, @@ -52,5 +53,5 @@ pub struct ServerProps { /// A translations manager to use. pub translations_manager: T, /// The global state creator. This is used to avoid issues with `async` and cloning in Actix Web. - pub global_state_creator: GlobalStateCreator, + pub global_state_creator: Arc, } diff --git a/packages/perseus/src/state/global_state.rs b/packages/perseus/src/state/global_state.rs index 6c1ec9cb5f..4a5c4c570a 100644 --- a/packages/perseus/src/state/global_state.rs +++ b/packages/perseus/src/state/global_state.rs @@ -9,14 +9,14 @@ use std::rc::Rc; make_async_trait!(GlobalStateCreatorFnType, RenderFnResult); /// The type of functions that generate global state. These will generate a `String` for their custom global state type. -pub type GlobalStateCreatorFn = Rc; +pub type GlobalStateCreatorFn = Box; /// A creator for global state. This stores user-provided functions that will be invoked to generate global state on the client /// and the server. /// /// The primary purpose of this is to allow the generation of top-level app state on the server and the client. Notably, /// this can also be interacted with by plugins. -#[derive(Default, Clone)] +#[derive(Default)] pub struct GlobalStateCreator { /// The function that creates state at build-time. This is roughly equivalent to the *build state* strategy for templates. #[cfg(not(target_arch = "wasm32"))] @@ -38,7 +38,7 @@ impl GlobalStateCreator { mut self, val: impl GlobalStateCreatorFnType + Send + Sync + 'static, ) -> Self { - self.build = Some(Rc::new(val)); + self.build = Some(Box::new(val)); self } /// Adds a function to generate global state at build-time.