Skip to content

Commit

Permalink
refactor: added Debug implementations to everything
Browse files Browse the repository at this point in the history
This is now linted as well. Also, unsafe code is forbidden (for now at least).
  • Loading branch information
arctic-hen7 committed Jan 23, 2022
1 parent 02608fb commit 2392daa
Show file tree
Hide file tree
Showing 22 changed files with 169 additions and 127 deletions.
16 changes: 3 additions & 13 deletions examples/basic/.perseus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use perseus::{
shell::{app_shell, get_initial_state, get_render_cfg, InitialState, ShellProps},
},
plugins::PluginAction,
state::{AnyFreeze, FrozenApp, PageStateStore},
state::{FrozenApp, GlobalState, PageStateStore},
templates::{RouterState, TemplateNodeType, ThawPrefs},
DomNode,
};
Expand Down Expand Up @@ -68,19 +68,9 @@ pub fn run() -> Result<(), JsValue> {
// Create a page state store to use
let pss = PageStateStore::default();
// Create a new global state set to `None`, which will be updated and handled entirely by the template macro from here on
let global_state: Rc<RefCell<Box<dyn AnyFreeze>>> =
Rc::new(RefCell::new(Box::new(Option::<()>::None)));
let global_state = GlobalState::default();

// TODO Try to fetch a previous frozen app
// let frozen_app: Option<Rc<FrozenApp>> = Some(Rc::new(FrozenApp {
// global_state: r#"{"test":"Hello from the frozen app!"}"#.to_string(),
// route: "".to_string(),
// page_state_store: {
// let mut map = std::collections::HashMap::new();
// map.insert("".to_string(), r#"{"username":"Sam"}"#.to_string());
// map
// },
// }));
// Instantiate an empty frozen app that can persist across templates (with interior mutability for possible thawing)
let frozen_app: Rc<RefCell<Option<(FrozenApp, ThawPrefs)>>> = Rc::new(RefCell::new(None));

// Create the router we'll use for this app, based on the user's app definition
Expand Down
76 changes: 1 addition & 75 deletions packages/perseus-macro/src/template_rx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,53 +175,13 @@ pub fn template_impl(input: TemplateFn, attr_args: AttributeArgs) -> TokenStream
render_ctx.register_global_state_str::<#global_state_rx>(&props.global_state.unwrap()).unwrap();
}

// // Deserialize the global state, make it reactive, and register it with the `RenderCtx`
// // If it's already there, we'll leave it
// // This means that we can pass an `Option<String>` around safely and then deal with it at the template site (here)
// let frozen_app = render_ctx.frozen_app;
// let global_state_refcell = render_ctx.global_state;
// let global_state = global_state_refcell.borrow();
// // This will work if the global state hasn't been initialized yet, because it's the default value that Perseus sets
// if global_state.as_any().downcast_ref::<::std::option::Option::<()>>().is_some() {
// // We can downcast it as the type set by the core render system, so we're the first page to be loaded
// // In that case, we'll set the global state properly
// drop(global_state);
// let mut global_state_mut = global_state_refcell.borrow_mut();
// // If there's a frozen app, we'll try to use that
// let new_global_state = match frozen_app {
// // If it hadn't been initialized yet when we froze, it would've been set to `None` here, and we'll use the one from the server
// ::std::option::Option::Some(frozen_app) if frozen_app.global_state != "None" => {
// let global_state_str = frozen_app.global_state.clone();
// let global_state = ::serde_json::from_str::<<#global_state_rx as ::perseus::state::MakeUnrx>::Unrx>(&global_state_str);
// // We don't control the source of the frozen app, so we have to assume that it could well be invalid, in whcih case we'll turn to the server
// match global_state {
// ::std::result::Result::Ok(global_state) => global_state,
// ::std::result::Result::Err(_) => {
// // This will be defined if we're the first page
// let global_state_str = props.global_state.unwrap();
// // That's from the server, so it's unrecoverable if it doesn't deserialize
// ::serde_json::from_str::<<#global_state_rx as ::perseus::state::MakeUnrx>::Unrx>(&global_state_str).unwrap()
// }
// }
// },
// _ => {
// // This will be defined if we're the first page
// let global_state_str = props.global_state.unwrap();
// // That's from the server, so it's unrecoverable if it doesn't deserialize
// ::serde_json::from_str::<<#global_state_rx as ::perseus::state::MakeUnrx>::Unrx>(&global_state_str).unwrap()
// }
// };
// let new_global_state_rx = new_global_state.make_rx();
// *global_state_mut = ::std::boxed::Box::new(new_global_state_rx);
// // The component function can now access this in `RenderCtx`
// }
// The user's function
// We know this won't be async because Sycamore doesn't allow that
#(#attrs)*
#[::sycamore::component(#component_name<#type_param>)]
fn #name#generics(#state_arg) -> #return_type {
let #global_state_arg_pat: #global_state_rx = {
let global_state = ::perseus::get_render_ctx!().global_state;
let global_state = ::perseus::get_render_ctx!().global_state.0;
let global_state = global_state.borrow();
// We can guarantee that it will downcast correctly now, because we'll only invoke the component from this function, which sets up the global state correctly
let global_state_ref = global_state.as_any().downcast_ref::<#global_state_rx>().unwrap();
Expand Down Expand Up @@ -286,40 +246,6 @@ pub fn template_impl(input: TemplateFn, attr_args: AttributeArgs) -> TokenStream
render_ctx.register_page_state_str::<#rx_props_ty>(&props.path, &props.state.unwrap()).unwrap()
}
}
// // Check if properties of the reactive type are already in the page state store
// // If they are, we'll use them (so state persists for templates across the whole app)
// let render_ctx = ::perseus::get_render_ctx!();
// let frozen_app = render_ctx.frozen_app;
// let mut pss = render_ctx.page_state_store;
// match pss.get(&props.path) {
// ::std::option::Option::Some(old_state) => old_state,
// // Check if there's anything in the previous frozen state
// ::std::option::Option::None => {
// let state = match frozen_app {
// ::std::option::Option::Some(frozen_app) if frozen_app.page_state_store.contains_key(&props.path) => {
// let old_state = frozen_app.page_state_store.get(&props.path).unwrap();
// // As with the global state, this may not deserialize correctly, in which case we'll fall back to the generated state
// let state = ::serde_json::from_str::<<#rx_props_ty as ::perseus::state::MakeUnrx>::Unrx>(&old_state);
// match state {
// ::std::result::Result::Ok(state) => state,
// ::std::result::Result::Err(_) => {
// // If there are props, they will always be provided, the compiler just doesn't know that
// // If the user is using this macro, they sure should be using `#[make_rx(...)]` or similar!
// ::serde_json::from_str::<<#rx_props_ty as ::perseus::state::MakeUnrx>::Unrx>(&props.state.unwrap()).unwrap()
// }
// }
// },
// _ => {
// // If there are props, they will always be provided, the compiler just doesn't know that
// // If the user is using this macro, they sure should be using `#[make_rx(...)]` or similar!
// ::serde_json::from_str::<<#rx_props_ty as ::perseus::state::MakeUnrx>::Unrx>(&props.state.unwrap()).unwrap()
// }
// };
// let rx_state: #rx_props_ty = state.make_rx();
// pss.add(&props.path, rx_state.clone());
// rx_state
// }
// }
}
)
}
Expand Down
1 change: 1 addition & 0 deletions packages/perseus/src/client_translations_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::translator::Translator;
/// Manages translations in the app shell. This handles fetching translations from the server as well as caching for performance.
/// This is distinct from `TranslationsManager` in that it operates on the client-side rather than on the server. This optimizes for
/// users viewing many pages in the same locale, which is by far the most common use of most websites in terms of i18n.
#[derive(Debug)]
pub struct ClientTranslationsManager {
/// The cached translator. If the same locale is requested again, this will simply be returned.
cached_translator: Option<Translator>,
Expand Down
7 changes: 6 additions & 1 deletion packages/perseus/src/error_pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ pub struct ErrorPages<G: Html> {
status_pages: HashMap<u16, ErrorPageTemplate<G>>,
fallback: ErrorPageTemplate<G>,
}
impl<G: Html> std::fmt::Debug for ErrorPages<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ErrorPages").finish()
}
}
impl<G: Html> ErrorPages<G> {
/// Creates a new definition of error pages with just a fallback.
pub fn new(
Expand Down Expand Up @@ -122,7 +127,7 @@ impl ErrorPages<SsrNode> {

/// A representation of an error page, particularly for storage in transit so that server-side rendered error pages can be hydrated on
/// the client-side.
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Debug)]
pub struct ErrorPageData {
/// The URL for the error.
pub url: String,
Expand Down
10 changes: 10 additions & 0 deletions packages/perseus/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ pub struct GlobalStateCreator {
/// The function that creates state at build-time. This is roughly equivalent to the *build state* strategy for templates.
build: Option<GlobalStateCreatorFn>,
}
impl std::fmt::Debug for GlobalStateCreator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GlobalStateCreator")
.field(
"build",
&self.build.as_ref().map(|_| "GlobalStateCreatorFn"),
)
.finish()
}
}
impl GlobalStateCreator {
/// Creates a new instance of this `struct`.
pub fn new() -> Self {
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/html_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn escape_page_data(data: &str) -> String {
}

/// Represents a shell of an HTML file. It may have content that gets interpolated into the file.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct HtmlShell<'a> {
/// The actual shell content, on whcih interpolations will be performed.
shell: String,
Expand Down
4 changes: 3 additions & 1 deletion packages/perseus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
*/

#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![forbid(unsafe_code)]
#![recursion_limit = "256"]

pub mod errors;
Expand Down Expand Up @@ -103,7 +105,7 @@ pub mod state {
pub use crate::global_state::GlobalStateCreator;
pub use crate::page_state_store::PageStateStore;
pub use crate::rx_state::*;
pub use crate::template::{FrozenApp, PageThawPrefs, ThawPrefs};
pub use crate::template::{FrozenApp, GlobalState, PageThawPrefs, ThawPrefs};
}
/// A series of exports that should be unnecessary for nearly all uses of Perseus. These are used principally in developing alternative
/// engines.
Expand Down
2 changes: 1 addition & 1 deletion packages/perseus/src/locales.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Defines app information about i18n, specifically about which locales are supported.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Locales {
/// The default locale, which will be used as a fallback if the user's locale can't be extracted. This will be built for at build-time.
pub default: String,
Expand Down
6 changes: 5 additions & 1 deletion packages/perseus/src/page_state_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use crate::state::AnyFreeze;
///
/// Note that the same pages in different locales will have different entries here. If you need to store state for a page across locales, you should use the global state system instead. For apps
/// not using i18n, the page URL will not include any locale.
// TODO Make this work with multiple pages for a single template
#[derive(Default, Clone)]
pub struct PageStateStore {
/// A map of type IDs to anything, allowing one storage of each type (each type is intended to a properties `struct` for a template). Entries must be `Clone`able because we assume them
Expand Down Expand Up @@ -50,6 +49,11 @@ impl PageStateStore {
str_map
}
}
impl std::fmt::Debug for PageStateStore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PageStateStore").finish()
}
}

// TODO Use `trybuild` properly with all this
//// These are tests for the `#[make_rx]` proc macro (here temporarily)
Expand Down
20 changes: 14 additions & 6 deletions packages/perseus/src/plugins/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,17 @@ impl<A, R> Default for ControlPluginAction<A, R> {
}
}
}
impl<A, R> std::fmt::Debug for ControlPluginAction<A, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ControlPluginAction")
.field("controller_name", &self.controller_name)
.field("runner", &self.runner.as_ref().map(|_| "Runner"))
.finish()
}
}

/// All the control actions that a plugin can take.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginActions {
/// Actions pertaining to the modification of settings created with the `define_app!` macro.
pub settings_actions: ControlPluginSettingsActions,
Expand All @@ -72,7 +80,7 @@ pub struct ControlPluginActions {
}

/// Control actions that pertain to altering settings from `define_app!`.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginSettingsActions {
/// Sets an immutable store to be used everywhere. This will provided the current immutable store for reference.
pub set_immutable_store:
Expand All @@ -83,14 +91,14 @@ pub struct ControlPluginSettingsActions {
pub set_app_root: ControlPluginAction<(), String>,
}
/// Control actions that pertain to the build process.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginBuildActions {}
/// Control actions that pertain to the export process.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginExportActions {}
/// Control actions that pertain to the server.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginServerActions {}
/// Control actions that pertain to the client-side code. As yet, there are none of these.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct ControlPluginClientActions {}
19 changes: 14 additions & 5 deletions packages/perseus/src/plugins/functional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,16 @@ impl<A, R> Default for FunctionalPluginAction<A, R> {
}
}
}
impl<A, R> std::fmt::Debug for FunctionalPluginAction<A, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FunctionalPluginAction")
.field("runners", &"HashMap<String, Runner>")
.finish()
}
}

/// Actions designed to be compatible with other plugins such that two plugins can execute the same action.
#[derive(Debug)]
pub struct FunctionalPluginActions<G: Html> {
/// The all-powerful action that can modify the Perseus engine itself. Because modifying the code you're running doesn't work with
/// compiled languages like Rust, this has its own command in the CLI, `perseus tinker`. This is best used for modifying
Expand Down Expand Up @@ -86,6 +94,7 @@ impl<G: Html> Default for FunctionalPluginActions<G> {
}

/// Functional actions that pertain to altering the settings exported from the `define_app!` macro.
#[derive(Debug)]
pub struct FunctionalPluginSettingsActions<G: Html> {
/// Adds additional static aliases. Note that a static alias is a mapping of a URL path to a filesystem path (relative to the
/// project root). These will be vetted to ensure they don't access anything outside the project root for security reasons. If they
Expand All @@ -111,7 +120,7 @@ impl<G: Html> Default for FunctionalPluginSettingsActions<G> {

/// Functional actions that pertain to the build process. Note that these actions are not available for the build
/// stage of the export process, and those should be registered separately.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct FunctionalPluginBuildActions {
/// Runs before the build process.
pub before_build: FunctionalPluginAction<(), ()>,
Expand All @@ -124,7 +133,7 @@ pub struct FunctionalPluginBuildActions {
FunctionalPluginAction<crate::errors::GlobalStateError, ()>,
}
/// Functional actions that pertain to the export process.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct FunctionalPluginExportActions {
/// Runs before the export process.
pub before_export: FunctionalPluginAction<(), ()>,
Expand All @@ -149,7 +158,7 @@ pub struct FunctionalPluginExportActions {
FunctionalPluginAction<crate::errors::GlobalStateError, ()>,
}
/// Functional actions that pertain to the process of exporting an error page.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct FunctionalPluginExportErrorPageActions {
/// Runs before the process of exporting an error page, providing the HTTP status code to be exported and the output filename (relative to the root of the project, not to `.perseus/`).
pub before_export_error_page: FunctionalPluginAction<(u16, String), ()>,
Expand All @@ -159,14 +168,14 @@ pub struct FunctionalPluginExportErrorPageActions {
pub after_failed_write: FunctionalPluginAction<(std::io::Error, String), ()>,
}
/// Functional actions that pertain to the server.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct FunctionalPluginServerActions {
/// Runs before the server activates. This runs AFTER the current directory has been appropriately set for a standalone binary vs
/// running in the development environment (inside `.perseus/`).
pub before_serve: FunctionalPluginAction<(), ()>,
}
/// Functional actions that pertain to the client-side code. These in particular should be as fast as possible.
#[derive(Default)]
#[derive(Default, Debug)]
pub struct FunctionalPluginClientActions {
/// Runs before anything else in the browser. Note that this runs after panics have been set to go to the console.
pub start: FunctionalPluginAction<(), ()>,
Expand Down
Loading

0 comments on commit 2392daa

Please sign in to comment.