Use tracing in our webassembly as well.

This commit is contained in:
Jeremy Wall 2022-07-18 18:50:11 -04:00
parent cf38056104
commit bf97f1ed29
11 changed files with 89 additions and 139 deletions

13
Cargo.lock generated
View File

@ -789,6 +789,8 @@ dependencies = [
"reqwasm", "reqwasm",
"serde_json", "serde_json",
"sycamore", "sycamore",
"tracing",
"tracing-wasm",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
] ]
@ -1624,6 +1626,17 @@ dependencies = [
"tracing-log", "tracing-log",
] ]
[[package]]
name = "tracing-wasm"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07"
dependencies = [
"tracing",
"tracing-subscriber",
"wasm-bindgen",
]
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.3" version = "0.2.3"

View File

@ -18,6 +18,8 @@ reqwasm = "0.5.0"
# This makes debugging panics more tractable. # This makes debugging panics more tractable.
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"
serde_json = "1.0.79" serde_json = "1.0.79"
tracing = "0.1.35"
tracing-wasm = "0.2.1"
[dependencies.wasm-bindgen] [dependencies.wasm-bindgen]
# we need wasm-bindgen v0.2.78 exactly # we need wasm-bindgen v0.2.78 exactly

View File

@ -11,11 +11,12 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::console_log;
use crate::{components::Recipe, service::AppService}; use crate::{components::Recipe, service::AppService};
use sycamore::{context::use_context, prelude::*}; use sycamore::{context::use_context, prelude::*};
use tracing::{debug, instrument};
#[instrument]
#[component(RecipeList<G>)] #[component(RecipeList<G>)]
pub fn recipe_list() -> View<G> { pub fn recipe_list() -> View<G> {
let app_service = use_context::<AppService>(); let app_service = use_context::<AppService>();
@ -26,7 +27,7 @@ pub fn recipe_list() -> View<G> {
Indexed(IndexedProps{ Indexed(IndexedProps{
iterable: menu_list, iterable: menu_list,
template: |(idx, _count)| { template: |(idx, _count)| {
console_log!("Rendering recipe index: {}", idx); debug!(idx=%idx, "Rendering recipe");
let idx = Signal::new(idx); let idx = Signal::new(idx);
view ! { view ! {
Recipe(idx.handle()) Recipe(idx.handle())

View File

@ -11,17 +11,22 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::console_log;
use crate::service::AppService;
use std::rc::Rc; use std::rc::Rc;
use sycamore::{context::use_context, prelude::*}; use sycamore::{context::use_context, prelude::*};
use tracing::{debug, instrument};
use crate::service::AppService;
pub struct RecipeCheckBoxProps { pub struct RecipeCheckBoxProps {
pub i: usize, pub i: usize,
pub title: ReadSignal<String>, pub title: ReadSignal<String>,
} }
#[instrument(skip(props), fields(
idx=%props.i,
title=%props.title.get()
))]
#[component(RecipeSelection<G>)] #[component(RecipeSelection<G>)]
pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> { pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> {
let app_service = use_context::<AppService>(); let app_service = use_context::<AppService>();
@ -36,7 +41,7 @@ pub fn recipe_selection(props: RecipeCheckBoxProps) -> View<G> {
label(for=id_cloned_2) { (props.title.get()) } label(for=id_cloned_2) { (props.title.get()) }
input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| { input(type="number", class="item-count-sel", min="0", bind:value=count.clone(), name=format!("recipe_id:{}", i), value=id_as_str.clone(), on:change=move |_| {
let mut app_service = app_service.clone(); let mut app_service = app_service.clone();
console_log!("setting recipe id: {} to count: {}", i, *count.get()); debug!(idx=%i, count=*count.get(), "setting recipe count");
app_service.set_recipe_count_by_index(i, count.get().parse().unwrap()); app_service.set_recipe_count_by_index(i, count.get().parse().unwrap());
}) })
} }

View File

@ -11,11 +11,12 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::console_error;
use crate::{components::recipe_selection::*, service::AppService}; use crate::{components::recipe_selection::*, service::AppService};
use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*}; use sycamore::{context::use_context, futures::spawn_local_in_scope, prelude::*};
use tracing::{error, instrument};
#[instrument]
#[component(RecipeSelector<G>)] #[component(RecipeSelector<G>)]
pub fn recipe_selector() -> View<G> { pub fn recipe_selector() -> View<G> {
let app_service = use_context::<AppService>(); let app_service = use_context::<AppService>();
@ -32,8 +33,8 @@ pub fn recipe_selector() -> View<G> {
spawn_local_in_scope(cloned!((app_service) => { spawn_local_in_scope(cloned!((app_service) => {
let mut app_service = app_service.clone(); let mut app_service = app_service.clone();
async move { async move {
if let Err(e) = app_service.refresh().await { if let Err(err) = app_service.refresh().await {
console_error!("{}", e); error!(?err);
}; };
} }
})); }));

View File

@ -14,9 +14,10 @@
use crate::service::AppService; use crate::service::AppService;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use crate::console_debug;
use sycamore::{context::use_context, prelude::*}; use sycamore::{context::use_context, prelude::*};
use tracing::{debug, instrument};
#[instrument]
#[component(ShoppingList<G>)] #[component(ShoppingList<G>)]
pub fn shopping_list() -> View<G> { pub fn shopping_list() -> View<G> {
let app_service = use_context::<AppService>(); let app_service = use_context::<AppService>();
@ -27,7 +28,7 @@ pub fn shopping_list() -> View<G> {
create_effect(cloned!((app_service, ingredients_map) => move || { create_effect(cloned!((app_service, ingredients_map) => move || {
ingredients_map.set(app_service.get_shopping_list()); ingredients_map.set(app_service.get_shopping_list());
})); }));
console_debug!("Ingredients map: {:?}", ingredients_map.get_untracked()); debug!(ingredients_map=?ingredients_map.get_untracked());
let ingredients = create_memo(cloned!((ingredients_map, filtered_keys) => move || { let ingredients = create_memo(cloned!((ingredients_map, filtered_keys) => move || {
let mut ingredients = Vec::new(); let mut ingredients = Vec::new();
// This has the effect of sorting the ingredients by category // This has the effect of sorting the ingredients by category
@ -40,7 +41,7 @@ pub fn shopping_list() -> View<G> {
} }
ingredients ingredients
})); }));
console_debug!("Ingredients: {:?}", ingredients.get_untracked()); debug!(ingredients = ?ingredients.get_untracked());
let table_view = Signal::new(View::empty()); let table_view = Signal::new(View::empty());
create_effect( create_effect(
cloned!((table_view, ingredients, filtered_keys, modified_amts, extras) => move || { cloned!((table_view, ingredients, filtered_keys, modified_amts, extras) => move || {

View File

@ -16,19 +16,20 @@ mod components;
mod pages; mod pages;
mod router_integration; mod router_integration;
mod service; mod service;
mod typings;
mod web; mod web;
use sycamore::prelude::*; use sycamore::prelude::*;
#[cfg(feature = "web")]
use tracing_wasm;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
use web::UI; use web::UI;
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
pub fn main() { pub fn main() {
#[cfg(debug_assertions)] if cfg!(feature = "web") {
{
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
tracing_wasm::set_as_global_default();
} }
sycamore::render(|| view! { UI() }); sycamore::render(|| view! { UI() });
} }

View File

@ -16,16 +16,15 @@ use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use sycamore::prelude::*; use sycamore::prelude::*;
use tracing::{debug, error, instrument};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast; use wasm_bindgen::JsCast;
use web_sys::Event; use web_sys::Event;
use web_sys::{Element, HtmlAnchorElement}; use web_sys::{Element, HtmlAnchorElement};
use crate::app_state::AppRoutes; use crate::app_state::AppRoutes;
use crate::console_debug;
use crate::console_error;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct BrowserIntegration(Signal<(String, String, String)>); pub struct BrowserIntegration(Signal<(String, String, String)>);
impl BrowserIntegration { impl BrowserIntegration {
@ -38,6 +37,7 @@ impl BrowserIntegration {
))) )))
} }
#[instrument(skip(self))]
pub fn click_handler(&self) -> Box<dyn Fn(web_sys::Event)> { pub fn click_handler(&self) -> Box<dyn Fn(web_sys::Event)> {
let route_signal = self.0.clone(); let route_signal = self.0.clone();
Box::new(move |ev| { Box::new(move |ev| {
@ -49,12 +49,12 @@ impl BrowserIntegration {
.unwrap_throw() .unwrap_throw()
.map(|e| e.unchecked_into::<HtmlAnchorElement>()) .map(|e| e.unchecked_into::<HtmlAnchorElement>())
{ {
console_debug!("handling navigation event."); debug!("handling navigation event.");
let location = web_sys::window().unwrap_throw().location(); let location = web_sys::window().unwrap_throw().location();
if tgt.rel() == "external" { if tgt.rel() == "external" {
debug!("External Link so ignoring.");
return; return;
console_debug!("External Link so ignoring.");
} }
let origin = tgt.origin(); let origin = tgt.origin();
@ -67,12 +67,12 @@ impl BrowserIntegration {
} }
(true, _, false) // different hash (true, _, false) // different hash
| (true, false, _) /* different path */ => { | (true, false, _) /* different path */ => {
console_debug!("different path or hash"); debug!("different path or hash");
ev.prevent_default(); ev.prevent_default();
// Signal the pathname change // Signal the pathname change
let path = format!("{}{}{}", &origin, &tgt_pathname, &hash); let path = format!("{}{}{}", &origin, &tgt_pathname, &hash);
console_debug!("new route: ({}, {}, {})", origin, tgt_pathname, hash); debug!("new route: ({}, {}, {})", origin, tgt_pathname, hash);
console_debug!("new path: ({})", &path); debug!("new path: ({})", &path);
route_signal.set((origin, tgt_pathname, hash)); route_signal.set((origin, tgt_pathname, hash));
// Update History API. // Update History API.
let window = web_sys::window().unwrap_throw(); let window = web_sys::window().unwrap_throw();
@ -88,6 +88,7 @@ impl BrowserIntegration {
} }
} }
#[derive(Debug)]
pub struct RouterProps<R, F, G> pub struct RouterProps<R, F, G>
where where
G: GenericNode, G: GenericNode,
@ -99,13 +100,18 @@ where
pub browser_integration: BrowserIntegration, pub browser_integration: BrowserIntegration,
} }
#[instrument(fields(?props.route,
origin=props.browser_integration.0.get().0,
pathn=props.browser_integration.0.get().1,
hash=props.browser_integration.0.get().2),
skip(props))]
#[component(Router<G>)] #[component(Router<G>)]
pub fn router<R, F>(props: RouterProps<R, F, G>) -> View<G> pub fn router<R, F>(props: RouterProps<R, F, G>) -> View<G>
where where
R: DeriveRoute + NotFound + Clone + Default + Debug + 'static, R: DeriveRoute + NotFound + Clone + Default + Debug + 'static,
F: Fn(ReadSignal<R>) -> View<G> + 'static, F: Fn(ReadSignal<R>) -> View<G> + 'static,
{ {
console_debug!("Setting up router"); debug!("Setting up router");
let integration = Rc::new(props.browser_integration); let integration = Rc::new(props.browser_integration);
let route_select = Rc::new(props.route_select); let route_select = Rc::new(props.route_select);
@ -113,10 +119,10 @@ where
create_effect( create_effect(
cloned!((view_signal, integration, route_select) => move || { cloned!((view_signal, integration, route_select) => move || {
let path_signal = integration.0.clone(); let path_signal = integration.0.clone();
console_debug!("new path: {:?}", path_signal.get()); debug!("new path: {:?}", path_signal.get());
let path = path_signal.clone(); let path = path_signal.clone();
let route = R::from(path.get().as_ref()); let route = R::from(path.get().as_ref());
console_debug!("new route: {:?}", &route); debug!("new route: {:?}", &route);
// TODO(jwall): this is an unnecessary use of signal. // TODO(jwall): this is an unnecessary use of signal.
let view = route_select.as_ref()(Signal::new(route).handle()); let view = route_select.as_ref()(Signal::new(route).handle());
register_click_handler(&view, integration.clone()); register_click_handler(&view, integration.clone());
@ -131,22 +137,23 @@ where
} }
} }
#[instrument(skip_all)]
fn register_click_handler<G>(view: &View<G>, integration: Rc<BrowserIntegration>) fn register_click_handler<G>(view: &View<G>, integration: Rc<BrowserIntegration>)
where where
G: GenericNode<EventType = Event>, G: GenericNode<EventType = Event>,
{ {
console_debug!("Registring click handler on node(s)"); debug!("Registring click handler on node(s)");
if let Some(node) = view.as_node() { if let Some(node) = view.as_node() {
node.event("click", integration.click_handler()); node.event("click", integration.click_handler());
} else if let Some(frag) = view.as_fragment() { } else if let Some(frag) = view.as_fragment() {
console_debug!("Fragment? {:?}", frag); debug!(fragment=?frag);
for n in frag { for n in frag {
register_click_handler(n, integration.clone()); register_click_handler(n, integration.clone());
} }
} else if let Some(dyn_node) = view.as_dyn() { } else if let Some(dyn_node) = view.as_dyn() {
console_debug!("Dynamic node? {:?}", dyn_node); debug!(dynamic_node=?dyn_node);
} else { } else {
console_debug!("Unknown node? {:?}", view); debug!(node=?view, "Unknown node");
} }
} }
@ -165,8 +172,9 @@ pub trait DeriveRoute {
} }
impl DeriveRoute for AppRoutes { impl DeriveRoute for AppRoutes {
#[instrument]
fn from(input: &(String, String, String)) -> AppRoutes { fn from(input: &(String, String, String)) -> AppRoutes {
console_debug!("routing: {input:?}"); debug!("routing: {input:?}");
match input.2.as_str() { match input.2.as_str() {
"" => AppRoutes::default(), "" => AppRoutes::default(),
"#plan" => AppRoutes::Plan, "#plan" => AppRoutes::Plan,
@ -183,7 +191,7 @@ impl DeriveRoute for AppRoutes {
}; };
} }
} }
console_error!("Path not found: [{:?}]", input); error!("Path not found: [{:?}]", input);
AppRoutes::NotFound AppRoutes::NotFound
} }
} }

View File

@ -13,10 +13,9 @@
// limitations under the License. // limitations under the License.
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use crate::{console_debug, console_error, console_log};
use reqwasm::http; use reqwasm::http;
use sycamore::prelude::*; use sycamore::prelude::*;
use tracing::{debug, error, info, instrument};
use web_sys::{window, Storage}; use web_sys::{window, Storage};
use recipes::{parse, Ingredient, IngredientAccumulator, Recipe}; use recipes::{parse, Ingredient, IngredientAccumulator, Recipe};
@ -46,6 +45,7 @@ impl AppService {
.map_err(|e| format!("{:?}", e)) .map_err(|e| format!("{:?}", e))
} }
#[instrument]
async fn fetch_recipes_http() -> Result<String, String> { async fn fetch_recipes_http() -> Result<String, String> {
let resp = match http::Request::get("/api/v1/recipes").send().await { let resp = match http::Request::get("/api/v1/recipes").send().await {
Ok(resp) => resp, Ok(resp) => resp,
@ -54,52 +54,55 @@ impl AppService {
if resp.status() != 200 { if resp.status() != 200 {
return Err(format!("Status: {}", resp.status())); return Err(format!("Status: {}", resp.status()));
} else { } else {
console_debug!("We got a valid response back!"); debug!("We got a valid response back!");
return Ok(resp.text().await.map_err(|e| format!("{}", e))?); return Ok(resp.text().await.map_err(|e| format!("{}", e))?);
} }
} }
#[instrument]
async fn fetch_categories_http() -> Result<Option<String>, String> { async fn fetch_categories_http() -> Result<Option<String>, String> {
let resp = match http::Request::get("/api/v1/categories").send().await { let resp = match http::Request::get("/api/v1/categories").send().await {
Ok(resp) => resp, Ok(resp) => resp,
Err(e) => return Err(format!("Error: {}", e)), Err(e) => return Err(format!("Error: {}", e)),
}; };
if resp.status() == 404 { if resp.status() == 404 {
console_debug!("Categories returned 404"); debug!("Categories returned 404");
return Ok(None); return Ok(None);
} else if resp.status() != 200 { } else if resp.status() != 200 {
return Err(format!("Status: {}", resp.status())); return Err(format!("Status: {}", resp.status()));
} else { } else {
console_debug!("We got a valid response back!"); debug!("We got a valid response back!");
return Ok(Some(resp.text().await.map_err(|e| format!("{}", e))?)); return Ok(Some(resp.text().await.map_err(|e| format!("{}", e))?));
} }
} }
#[instrument]
async fn synchronize() -> Result<(), String> { async fn synchronize() -> Result<(), String> {
console_log!("Synchronizing Recipes"); info!("Synchronizing Recipes");
let storage = Self::get_storage()?.unwrap(); let storage = Self::get_storage()?.unwrap();
let recipes = Self::fetch_recipes_http().await?; let recipes = Self::fetch_recipes_http().await?;
storage storage
.set_item("recipes", &recipes) .set_item("recipes", &recipes)
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
console_log!("Synchronizing categories"); info!("Synchronizing categories");
match Self::fetch_categories_http().await { match Self::fetch_categories_http().await {
Ok(Some(categories_content)) => { Ok(Some(categories_content)) => {
console_debug!("categories: {}", categories_content); debug!(categories=?categories_content);
storage storage
.set_item("categories", &categories_content) .set_item("categories", &categories_content)
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
} }
Ok(None) => { Ok(None) => {
console_error!("There is no category file"); error!("There is no category file");
} }
Err(e) => { Err(e) => {
console_error!("{}", e); error!("{}", e);
} }
} }
Ok(()) Ok(())
} }
#[instrument]
pub fn fetch_categories_from_storage() -> Result<Option<BTreeMap<String, String>>, String> { pub fn fetch_categories_from_storage() -> Result<Option<BTreeMap<String, String>>, String> {
let storage = Self::get_storage()?.unwrap(); let storage = Self::get_storage()?.unwrap();
match storage match storage
@ -111,7 +114,7 @@ impl AppService {
match parse::as_categories(&parsed) { match parse::as_categories(&parsed) {
Ok(categories) => Ok(Some(categories)), Ok(categories) => Ok(Some(categories)),
Err(e) => { Err(e) => {
console_debug!("Error parsing categories {}", e); debug!("Error parsing categories {}", e);
Err(format!("Error parsing categories {}", e)) Err(format!("Error parsing categories {}", e))
} }
} }
@ -120,6 +123,7 @@ impl AppService {
} }
} }
#[instrument]
pub fn fetch_recipes_from_storage( pub fn fetch_recipes_from_storage(
) -> Result<(Option<Recipe>, Option<Vec<(usize, Recipe)>>), String> { ) -> Result<(Option<Recipe>, Option<Vec<(usize, Recipe)>>), String> {
let storage = Self::get_storage()?.unwrap(); let storage = Self::get_storage()?.unwrap();
@ -136,11 +140,10 @@ impl AppService {
let recipe = match parse::as_recipe(&r) { let recipe = match parse::as_recipe(&r) {
Ok(r) => r, Ok(r) => r,
Err(e) => { Err(e) => {
console_error!("Error parsing recipe {}", e); error!("Error parsing recipe {}", e);
continue; continue;
} }
}; };
//console_debug!("We parsed a recipe {}", recipe.title);
if recipe.title == "Staples" { if recipe.title == "Staples" {
staples = Some(recipe); staples = Some(recipe);
} else { } else {
@ -161,14 +164,15 @@ impl AppService {
Ok(Self::fetch_categories_from_storage()?) Ok(Self::fetch_categories_from_storage()?)
} }
#[instrument(skip(self))]
pub async fn refresh(&mut self) -> Result<(), String> { pub async fn refresh(&mut self) -> Result<(), String> {
Self::synchronize().await?; Self::synchronize().await?;
console_debug!("refreshing recipes"); debug!("refreshing recipes");
if let (staples, Some(r)) = Self::fetch_recipes().await? { if let (staples, Some(r)) = Self::fetch_recipes().await? {
self.set_recipes(r); self.set_recipes(r);
self.staples.set(staples); self.staples.set(staples);
} }
console_debug!("refreshing categories"); debug!("refreshing categories");
if let Some(categories) = Self::fetch_categories().await? { if let Some(categories) = Self::fetch_categories().await? {
self.set_categories(categories); self.set_categories(categories);
} }
@ -179,6 +183,7 @@ impl AppService {
self.recipes.get().get(idx).map(|(_, r)| r.clone()) self.recipes.get().get(idx).map(|(_, r)| r.clone())
} }
#[instrument(skip(self))]
pub fn get_shopping_list(&self) -> BTreeMap<String, Vec<(Ingredient, BTreeSet<String>)>> { pub fn get_shopping_list(&self) -> BTreeMap<String, Vec<(Ingredient, BTreeSet<String>)>> {
let mut acc = IngredientAccumulator::new(); let mut acc = IngredientAccumulator::new();
let recipe_counts = self.menu_list.get(); let recipe_counts = self.menu_list.get();
@ -205,7 +210,7 @@ impl AppService {
.or_insert(vec![]) .or_insert(vec![])
.push((i.clone(), recipes.clone())); .push((i.clone(), recipes.clone()));
} }
console_debug!("Category map {:?}", self.category_map); debug!(?self.category_map);
// FIXM(jwall): Sort by categories and names. // FIXM(jwall): Sort by categories and names.
groups groups
} }

View File

@ -1,88 +0,0 @@
// Copyright 2022 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn debug(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn warn(s: &str);
#[wasm_bindgen(js_namespace = console)]
pub fn error(s: &str);
}
#[macro_export]
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => {
if cfg!(feature="web") {
use crate::typings::log;
log(&format_args!($($t)*).to_string());
} else if cfg!(feature="ssr") {
println!($($t)*);
}
}
}
#[macro_export]
macro_rules! console_debug {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => {{
if cfg!(feature="web") {
use crate::typings::debug;
debug(&format_args!($($t)*).to_string());
} else if cfg!(feature="ssr") {
print!("DEBUG: ");
println!($($t)*);
}
}}
}
#[macro_export]
macro_rules! console_error {
// Note that this is using the `error` function imported above during
// `bare_bones`
($($t:tt)*) => {{
if cfg!(feature="web")
{
use crate::typings::error;
error(&format_args!($($t)*).to_string());
} else if cfg!(feature="ssr") {
print!("ERROR: ");
println!($($t)*);
};
}}
}
#[macro_export]
macro_rules! console_warn {
// Note that this is using the `warn` function imported above during
// `bare_bones`
($($t:tt)*) => {{
if cfg!("web") {
use crate::typings::warn;
(warn(&format_args!($($t)*).to_string()))
} else if cfg!(feature="ssr") {
print!("WARN: ");
(println!($($t)*))
}
}}
}

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
use crate::pages::*; use crate::pages::*;
use crate::{app_state::*, components::*, router_integration::*, service::AppService}; use crate::{app_state::*, components::*, router_integration::*, service::AppService};
use crate::{console_error, console_log}; use tracing::{error, info, instrument};
use sycamore::{ use sycamore::{
context::{ContextProvider, ContextProviderProps}, context::{ContextProvider, ContextProviderProps},
@ -51,10 +51,11 @@ fn route_switch<G: Html>(route: ReadSignal<AppRoutes>) -> View<G> {
}) })
} }
#[instrument]
#[component(UI<G>)] #[component(UI<G>)]
pub fn ui() -> View<G> { pub fn ui() -> View<G> {
let app_service = AppService::new(); let app_service = AppService::new();
console_log!("Starting UI"); info!("Starting UI");
view! { view! {
// NOTE(jwall): Set the app_service in our toplevel scope. Children will be able // NOTE(jwall): Set the app_service in our toplevel scope. Children will be able
// to find the service as long as they are a child of this scope. // to find the service as long as they are a child of this scope.
@ -70,9 +71,9 @@ pub fn ui() -> View<G> {
app_service.set_recipes(recipes); app_service.set_recipes(recipes);
} }
Ok((_, None)) => { Ok((_, None)) => {
console_error!("No recipes to find"); error!("No recipes to find");
} }
Err(msg) => console_error!("Failed to get recipes {}", msg), Err(msg) => error!("Failed to get recipes {}", msg),
} }
} }
}); });