Skip to content

Commit 42f0d99

Browse files
committed
feat: made plugin actions return Result
Errors on the engine-side are automatically handled and returned through the CLI, while those on the browser-side are handled through a single `panic!` that will go to the user's defined browser-side panic handler Closes #234. BREAKING CHANGE: all plugin actions must now return `Result<T, Box<dyn std::error::Error>>` (return some arbitrary error and then use `.into()`) BREAKING CHANGE: all plugin actions that previously took `Rc<perseus::errors::EngineError>` now take `Rc<perseus::errors::Error>`
1 parent 9c3444a commit 42f0d99

File tree

13 files changed

+268
-139
lines changed

13 files changed

+268
-139
lines changed

examples/core/plugins/src/plugin.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ pub fn get_test_plugin<G: perseus::Html>() -> Plugin<G, TestPluginData> {
1616
.register_plugin("test-plugin", |_, _| {
1717
let mut map = std::collections::HashMap::new();
1818
map.insert("/Cargo.toml".to_string(), "Cargo.toml".to_string());
19-
map
19+
Ok(map)
2020
});
2121
actions.settings_actions.add_templates.register_plugin(
2222
"test-plugin",
2323
|_, plugin_data| {
2424
if let Some(plugin_data) = plugin_data.downcast_ref::<TestPluginData>() {
2525
let about_page_greeting = plugin_data.about_page_greeting.to_string();
26-
vec![Template::new("about").template(move |cx| {
26+
Ok(vec![Template::new("about").template(move |cx| {
2727
sycamore::view! { cx, p { (about_page_greeting) } }
28-
})]
28+
})])
2929
} else {
3030
unreachable!()
3131
}
@@ -38,6 +38,7 @@ pub fn get_test_plugin<G: perseus::Html>() -> Plugin<G, TestPluginData> {
3838
let test = "[package]\name = \"test\"";
3939
let parsed: toml::Value = toml::from_str(test).unwrap();
4040
println!("{}", toml::to_string(&parsed).unwrap());
41+
Ok(())
4142
});
4243
actions
4344
},

packages/perseus/src/client.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use crate::errors::PluginError;
12
use crate::{
23
checkpoint,
34
plugins::PluginAction,
45
router::{perseus_router, PerseusRouterProps},
56
template::TemplateNodeType,
67
};
78
use crate::{i18n::TranslationsManager, stores::MutableStore, PerseusAppBase};
9+
use fmterr::fmt_err;
810
use std::collections::HashMap;
911
use wasm_bindgen::JsValue;
1012

@@ -22,7 +24,6 @@ pub fn run_client<M: MutableStore, T: TranslationsManager>(
2224
app: impl Fn() -> PerseusAppBase<TemplateNodeType, M, T>,
2325
) -> Result<(), JsValue> {
2426
let mut app = app();
25-
let plugins = app.get_plugins();
2627
let panic_handler = app.take_panic_handler();
2728

2829
checkpoint("begin");
@@ -38,27 +39,44 @@ pub fn run_client<M: MutableStore, T: TranslationsManager>(
3839
}
3940
}));
4041

42+
let res = client_core(app);
43+
if let Err(err) = res {
44+
// This will go to the panic handler we defined above
45+
// Unfortunately, at this stage, we really can't do anything else
46+
panic!("plugin error: {}", fmt_err(&err));
47+
}
48+
49+
Ok(())
50+
}
51+
52+
/// This executes the actual underlying browser-side logic, including
53+
/// instantiating the user's app. This is broken out due to plugin fallibility.
54+
fn client_core<M: MutableStore, T: TranslationsManager>(
55+
app: PerseusAppBase<TemplateNodeType, M, T>,
56+
) -> Result<(), PluginError> {
57+
let plugins = app.get_plugins();
58+
4159
plugins
4260
.functional_actions
4361
.client_actions
4462
.start
45-
.run((), plugins.get_plugin_data());
63+
.run((), plugins.get_plugin_data())?;
4664
checkpoint("initial_plugins_complete");
4765

4866
// Get the root we'll be injecting the router into
4967
let root = web_sys::window()
5068
.unwrap()
5169
.document()
5270
.unwrap()
53-
.query_selector(&format!("#{}", app.get_root()))
71+
.query_selector(&format!("#{}", app.get_root()?))
5472
.unwrap()
5573
.unwrap();
5674

5775
// Set up the properties we'll pass to the router
5876
let router_props = PerseusRouterProps {
59-
locales: app.get_locales(),
77+
locales: app.get_locales()?,
6078
error_pages: app.get_error_pages(),
61-
templates: app.get_templates_map(),
79+
templates: app.get_templates_map()?,
6280
render_cfg: get_render_cfg().expect("render configuration invalid or not injected"),
6381
pss_max_size: app.get_pss_max_size(),
6482
};

packages/perseus/src/engine/build.rs

+20-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use crate::build::{build_app, BuildProps};
2+
use crate::errors::Error;
23
use crate::{
3-
errors::{EngineError, ServerError},
4-
i18n::TranslationsManager,
5-
plugins::PluginAction,
6-
stores::MutableStore,
4+
errors::ServerError, i18n::TranslationsManager, plugins::PluginAction, stores::MutableStore,
75
PerseusAppBase, SsrNode,
86
};
97
use std::rc::Rc;
@@ -15,37 +13,43 @@ use std::rc::Rc;
1513
/// Note that this expects to be run in the root of the project.
1614
pub async fn build<M: MutableStore, T: TranslationsManager>(
1715
app: PerseusAppBase<SsrNode, M, T>,
18-
) -> Result<(), Rc<EngineError>> {
16+
) -> Result<(), Rc<Error>> {
1917
let plugins = app.get_plugins();
2018

2119
plugins
2220
.functional_actions
2321
.build_actions
2422
.before_build
25-
.run((), plugins.get_plugin_data());
23+
.run((), plugins.get_plugin_data())
24+
.map_err(|err| Rc::new(err.into()))?;
2625

27-
let immutable_store = app.get_immutable_store();
26+
let immutable_store = app
27+
.get_immutable_store()
28+
.map_err(|err| Rc::new(err.into()))?;
2829
let mutable_store = app.get_mutable_store();
29-
let locales = app.get_locales();
30+
let locales = app.get_locales().map_err(|err| Rc::new(err.into()))?;
3031
// Generate the global state
3132
let gsc = app.get_global_state_creator();
3233
let global_state = match gsc.get_build_state().await {
3334
Ok(global_state) => global_state,
3435
Err(err) => {
35-
let err: Rc<EngineError> = Rc::new(ServerError::GlobalStateError(err).into());
36+
let err: Rc<Error> = Rc::new(ServerError::GlobalStateError(err).into());
3637
plugins
3738
.functional_actions
3839
.build_actions
3940
.after_failed_global_state_creation
40-
.run(err.clone(), plugins.get_plugin_data());
41+
.run(err.clone(), plugins.get_plugin_data())
42+
.map_err(|err| Rc::new(err.into()))?;
4143
return Err(err);
4244
}
4345
};
4446

4547
// Build the site for all the common locales (done in parallel)
4648
// All these parameters can be modified by `PerseusApp` and plugins, so there's
4749
// no point in having a plugin opportunity here
48-
let templates_map = app.get_atomic_templates_map();
50+
let templates_map = app
51+
.get_atomic_templates_map()
52+
.map_err(|err| Rc::new(err.into()))?;
4953

5054
// We have to get the translations manager last, because it consumes everything
5155
let translations_manager = app.get_translations_manager().await;
@@ -61,20 +65,22 @@ pub async fn build<M: MutableStore, T: TranslationsManager>(
6165
})
6266
.await;
6367
if let Err(err) = res {
64-
let err: Rc<EngineError> = Rc::new(err.into());
68+
let err: Rc<Error> = Rc::new(err.into());
6569
plugins
6670
.functional_actions
6771
.build_actions
6872
.after_failed_build
69-
.run(err.clone(), plugins.get_plugin_data());
73+
.run(err.clone(), plugins.get_plugin_data())
74+
.map_err(|err| Rc::new(err.into()))?;
7075

7176
Err(err)
7277
} else {
7378
plugins
7479
.functional_actions
7580
.build_actions
7681
.after_successful_build
77-
.run((), plugins.get_plugin_data());
82+
.run((), plugins.get_plugin_data())
83+
.map_err(|err| Rc::new(err.into()))?;
7884

7985
Ok(())
8086
}

packages/perseus/src/engine/dflt_engine.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ where
109109
EngineOperation::Serve => {
110110
// To reduce friction for default servers and user-made servers, we
111111
// automatically do the boilerplate that all servers would have to do
112-
let props = get_props(app());
112+
let props = match get_props(app()) {
113+
Ok(props) => props,
114+
Err(err) => {
115+
eprintln!("{}", fmt_err(&err));
116+
return 1;
117+
}
118+
};
113119
// This returns a `(String, u16)` of the host and port for maximum compatibility
114120
let addr = get_host_and_port();
115121
// In production, give the user a heads up that something's actually happening
@@ -123,10 +129,12 @@ where
123129
serve_fn(props, addr).await;
124130
0
125131
}
126-
EngineOperation::Tinker => {
127-
// This is infallible (though plugins could panic)
128-
super::engine_tinker(app());
129-
0
130-
}
132+
EngineOperation::Tinker => match super::engine_tinker(app()) {
133+
Ok(_) => 0,
134+
Err(err) => {
135+
eprintln!("{}", fmt_err(&err));
136+
1
137+
}
138+
},
131139
}
132140
}

0 commit comments

Comments
 (0)