From e403be809024b417719ed80403de02833811f8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asger=20Juul=20Brunsh=C3=B8j?= Date: Wed, 26 Mar 2025 16:26:14 +0100 Subject: [PATCH] feat: refactor to controller component and redesign --- crates/ascend/src/app.rs | 2 +- crates/ascend/src/components.rs | 27 + crates/ascend/src/components/header_v2.rs | 13 + crates/ascend/src/components/outlined_box.rs | 1 + crates/ascend/src/gradient.rs | 6 +- crates/ascend/src/lib.rs | 51 +- crates/ascend/src/models/semantics.rs | 1 + crates/ascend/src/pages.rs | 4 + crates/ascend/src/pages/wall.rs | 519 ++++++++++--------- crates/ascend/src/server/migrations.rs | 26 + crates/ascend/src/tracing.rs | 22 + 11 files changed, 390 insertions(+), 282 deletions(-) create mode 100644 crates/ascend/src/components.rs create mode 100644 crates/ascend/src/components/header_v2.rs create mode 100644 crates/ascend/src/pages.rs create mode 100644 crates/ascend/src/tracing.rs diff --git a/crates/ascend/src/app.rs b/crates/ascend/src/app.rs index bce9710..a5a97e7 100644 --- a/crates/ascend/src/app.rs +++ b/crates/ascend/src/app.rs @@ -42,7 +42,7 @@ pub fn App() -> impl IntoView { - + diff --git a/crates/ascend/src/components.rs b/crates/ascend/src/components.rs new file mode 100644 index 0000000..677de3f --- /dev/null +++ b/crates/ascend/src/components.rs @@ -0,0 +1,27 @@ +pub use attempt::Attempt; +pub use button::Button; +pub use header::StyledHeader; +pub use problem::Problem; +pub use problem_info::ProblemInfo; + +pub mod attempt; +pub mod button; +pub mod checkbox; +pub mod header; +pub mod header_v2; +pub mod icons; +pub mod outlined_box; +pub mod problem; +pub mod problem_info; + +use leptos::prelude::*; + +#[component] +pub fn OnHoverRed(children: Children) -> impl IntoView { + view! { +
+
{children()}
+
+
+ } +} diff --git a/crates/ascend/src/components/header_v2.rs b/crates/ascend/src/components/header_v2.rs new file mode 100644 index 0000000..b6d88f9 --- /dev/null +++ b/crates/ascend/src/components/header_v2.rs @@ -0,0 +1,13 @@ +use leptos::prelude::*; + +#[component] +#[tracing::instrument(skip_all)] +pub fn Header(children: Children) -> impl IntoView { + crate::tracing::on_enter!(); + + view! { +
+ {children()} +
+ } +} diff --git a/crates/ascend/src/components/outlined_box.rs b/crates/ascend/src/components/outlined_box.rs index 286ed6e..6b94c42 100644 --- a/crates/ascend/src/components/outlined_box.rs +++ b/crates/ascend/src/components/outlined_box.rs @@ -27,6 +27,7 @@ pub fn OutlinedBox(children: Children, color: Gradient, #[prop(optional)] highli Gradient::TealLime => "bg-teal-700", Gradient::PurplePink => "bg-purple-900", Gradient::PurpleBlue => "bg-purple-900", + Gradient::Orange => "bg-orange-900", }; c.push(' '); diff --git a/crates/ascend/src/gradient.rs b/crates/ascend/src/gradient.rs index 55095a3..50a3078 100644 --- a/crates/ascend/src/gradient.rs +++ b/crates/ascend/src/gradient.rs @@ -1,11 +1,12 @@ #[derive(Debug, Copy, Clone, Default)] pub enum Gradient { - #[default] PurpleBlue, PinkOrange, CyanBlue, TealLime, PurplePink, + #[default] + Orange, } impl Gradient { pub fn class_from(&self) -> &str { @@ -15,6 +16,7 @@ impl Gradient { Gradient::TealLime => "from-teal-300", Gradient::PurplePink => "from-purple-500", Gradient::PurpleBlue => "from-purple-600", + Gradient::Orange => "from-orange-400", } } @@ -25,6 +27,7 @@ impl Gradient { Gradient::TealLime => "to-lime-300", Gradient::PurplePink => "to-pink-500", Gradient::PurpleBlue => "to-blue-500", + Gradient::Orange => "to-orange-500", } } @@ -35,6 +38,7 @@ impl Gradient { Gradient::TealLime => "text-teal-300", Gradient::PurplePink => "text-purple-500", Gradient::PurpleBlue => "text-purple-600", + Gradient::Orange => "text-orange-400", } } } diff --git a/crates/ascend/src/lib.rs b/crates/ascend/src/lib.rs index d48b10a..45d0c9e 100644 --- a/crates/ascend/src/lib.rs +++ b/crates/ascend/src/lib.rs @@ -1,48 +1,13 @@ pub mod app; -pub mod pages { - pub mod edit_wall; - pub mod routes; - pub mod settings; - pub mod wall; -} -pub mod components { - pub use attempt::Attempt; - pub use button::Button; - pub use header::StyledHeader; - pub use problem::Problem; - pub use problem_info::ProblemInfo; - - pub mod attempt; - pub mod button; - pub mod checkbox; - pub mod header; - pub mod icons; - pub mod outlined_box; - pub mod problem; - pub mod problem_info; - - use leptos::prelude::*; - - #[component] - pub fn OnHoverRed(children: Children) -> impl IntoView { - view! { -
-
{children()}
-
-
- } - } -} - -pub mod gradient; - -pub mod resources; - -pub mod css; - pub mod codec; +pub mod components; +pub mod css; +pub mod gradient; pub mod models; +pub mod pages; +pub mod resources; pub mod server_functions; +pub mod tracing; #[cfg(feature = "ssr")] pub mod server; @@ -57,12 +22,12 @@ pub fn hydrate() { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::builder() - .with_default_directive(tracing::level_filters::LevelFilter::DEBUG.into()) + .with_default_directive(::tracing::level_filters::LevelFilter::DEBUG.into()) .from_env_lossy(), ) .with_writer( // To avoide trace events in the browser from showing their JS backtrace - tracing_subscriber_wasm::MakeConsoleWriter::default().map_trace_level_to(tracing::Level::DEBUG), + tracing_subscriber_wasm::MakeConsoleWriter::default().map_trace_level_to(::tracing::Level::DEBUG), ) // For some reason, if we don't do this in the browser, we get a runtime error. .without_time() diff --git a/crates/ascend/src/models/semantics.rs b/crates/ascend/src/models/semantics.rs index 1244c3b..f9288fa 100644 --- a/crates/ascend/src/models/semantics.rs +++ b/crates/ascend/src/models/semantics.rs @@ -59,6 +59,7 @@ impl From<(&DateTime, &Attempt)> for DatedAttempt { } impl WallUid { + #[expect(dead_code)] pub(crate) fn create() -> Self { Self(uuid::Uuid::new_v4()) } diff --git a/crates/ascend/src/pages.rs b/crates/ascend/src/pages.rs new file mode 100644 index 0000000..40fe8dd --- /dev/null +++ b/crates/ascend/src/pages.rs @@ -0,0 +1,4 @@ +pub mod edit_wall; +pub mod routes; +pub mod settings; +pub mod wall; diff --git a/crates/ascend/src/pages/wall.rs b/crates/ascend/src/pages/wall.rs index 06889f8..6a67925 100644 --- a/crates/ascend/src/pages/wall.rs +++ b/crates/ascend/src/pages/wall.rs @@ -3,9 +3,6 @@ use crate::components::OnHoverRed; use crate::components::ProblemInfo; use crate::components::attempt::Attempt; use crate::components::button::Button; -use crate::components::header::HeaderItem; -use crate::components::header::HeaderItems; -use crate::components::header::StyledHeader; use crate::components::icons::Icon; use crate::gradient::Gradient; use crate::models; @@ -24,8 +21,8 @@ struct RouteParams { #[component] #[tracing::instrument(skip_all)] -pub fn Wall() -> impl IntoView { - tracing::debug!("Enter"); +pub fn Page() -> impl IntoView { + crate::tracing::on_enter!(); let route_params = leptos_router::hooks::use_params::(); @@ -41,40 +38,171 @@ pub fn Wall() -> impl IntoView { let problems = crate::resources::problems_for_wall(wall_uid); let user_interactions = crate::resources::user_interactions(wall_uid); - let header_items = move || HeaderItems { - left: vec![], - middle: vec![HeaderItem { - text: "ASCEND".to_string(), - link: None, - }], - right: vec![ - HeaderItem { - text: "Routes".to_string(), - link: Some(format!("/wall/{}/routes", wall_uid.get())), - }, - HeaderItem { - text: "Holds".to_string(), - link: Some(format!("/wall/{}/edit", wall_uid.get())), - }, - ], - }; - leptos::view! {
- + + {move || Suspend::new(async move { + tracing::debug!("executing main suspend"); + let wall = wall.await?; + let problems = problems.await?; + let user_interactions = user_interactions.await?; + let user_interactions = RwSignal::new(user_interactions); + Ok::<_, ServerFnError>(view! { }) + })} + +
+ } +} -
- - {move || Suspend::new(async move { - tracing::info!("executing main suspend"); - let wall = wall.await?; - let problems = problems.await?; - let user_interactions = user_interactions.await?; - let user_interactions = RwSignal::new(user_interactions); - let v = view! { }; - Ok::<_, ServerFnError>(v) - })} - +#[derive(Debug, Clone, Copy)] +struct Context { + wall: Signal, + user_interactions: Signal>, + problem: Signal>, + filtered_problems: Signal>, + user_interaction: Signal>, + todays_attempt: Signal>, + latest_attempt: Signal>, + filter_holds: Signal>, + cb_click_hold: Callback, + cb_remove_hold_from_filter: Callback, + cb_next_problem: Callback<()>, + cb_upsert_todays_attempt: Callback, +} + +#[component] +#[tracing::instrument(skip_all)] +fn Controller( + #[prop(into)] wall: Signal, + #[prop(into)] problems: Signal>, + #[prop(into)] user_interactions: RwSignal>, +) -> impl IntoView { + crate::tracing::on_enter!(); + + // Extract data from URL + let (problem_uid, set_problem_uid) = leptos_router::hooks::query_signal::("problem"); + + // Filter + let (filter_holds, set_filter_holds) = signal(BTreeSet::new()); + let cb_remove_hold_from_filter: Callback = Callback::new(move |hold_pos: models::HoldPosition| { + set_filter_holds.update(move |set| { + set.remove(&hold_pos); + }); + }); + + // Derive signals + let wall_uid = signals::wall_uid(wall); + let problem = signals::problem(problems, problem_uid.into()); + let user_interaction = signals::user_interaction(user_interactions.into(), problem_uid.into()); + let filtered_problems = signals::filtered_problems(problems, filter_holds.into()); + let todays_attempt = signals::todays_attempt(user_interaction); + let latest_attempt = signals::latest_attempt(user_interaction); + + // Submit attempt action + let upsert_todays_attempt = ServerAction::>::new(); + let cb_upsert_todays_attempt = Callback::new(move |attempt| { + upsert_todays_attempt.dispatch(RonEncoded(attempt)); + }); + + // Callback: Set next problem to a random problem + let cb_set_random_problem: Callback<()> = Callback::new(move |_| { + // TODO: remove current problem from population + let population = filtered_problems.read(); + let population = population.keys().copied(); + + use rand::seq::IteratorRandom; + let mut rng = rand::rng(); + let problem_uid = population.choose(&mut rng); + + set_problem_uid.set(problem_uid); + }); + + // Callback: On click hold, Add/Remove hold position to problem filter + let cb_click_hold: Callback = Callback::new(move |hold_position| { + set_filter_holds.update(|set| { + if !set.remove(&hold_position) { + set.insert(hold_position); + } + }); + }); + + // Set a problem when wall is set (loaded) + Effect::new(move |_prev_value| { + if problem_uid.get().is_none() { + tracing::debug!("Setting initial problem"); + cb_set_random_problem.run(()); + } + }); + + // Update user interactions after submitting an attempt + Effect::new(move || { + if let Some(Ok(v)) = upsert_todays_attempt.value().get() { + let v = v.into_inner(); + user_interactions.update(|map| { + map.insert(v.problem_uid, v); + }); + } + }); + + provide_context(Context { + wall, + problem, + cb_click_hold, + user_interaction, + latest_attempt, + cb_upsert_todays_attempt, + cb_remove_hold_from_filter, + cb_next_problem: cb_set_random_problem, + todays_attempt, + filter_holds: filter_holds.into(), + filtered_problems: filtered_problems.into(), + user_interactions: user_interactions.into(), + }); + + view! { } +} + +#[component] +#[tracing::instrument(skip_all)] +fn View() -> impl IntoView { + crate::tracing::on_enter!(); + + let ctx = use_context::().unwrap(); + + view! { +
+
+ +
+ +
+
+ +
+ + + + + + + +
+ {move || ctx.problem.get().map(|problem| view! { })} +
+ + + + + + + + +
+ +
+
} @@ -82,104 +210,56 @@ pub fn Wall() -> impl IntoView { #[component] #[tracing::instrument(skip_all)] -fn WithProblem(#[prop(into)] problem: Signal) -> impl IntoView { - tracing::trace!("Enter"); +fn HoldsButton() -> impl IntoView { + crate::tracing::on_enter!(); - view! { } + let ctx = use_context::().unwrap(); + + let link = move || format!("/wall/{}/edit", ctx.wall.read().uid); + + view! { + +
+ + } +} - let submit_attempt = ServerAction::>::new(); - Effect::new(move || { - if let Some(Ok(v)) = submit_attempt.value().get() { - let v = v.into_inner(); - user_interactions.update(|map| { - map.insert(v.problem_uid, v); - }); - } - }); - let submit_attempt_cb = StoredValue::new(move |attempt: server_functions::UpsertTodaysAttempt| { - submit_attempt.dispatch(RonEncoded(attempt)); - }); +#[component] +#[tracing::instrument(skip_all)] +fn Filter() -> impl IntoView { + crate::tracing::on_enter!(); - let problem = signals::problem(problems, problem_uid.into()); - let user_interaction = signals::user_interaction(user_interactions.into(), problem_uid.into()); + let ctx = use_context::().unwrap(); - // Filter - let (filter_holds, set_filter_holds) = signal(BTreeSet::new()); - let _filter_add_hold = move |hold_pos: models::HoldPosition| { - set_filter_holds.update(move |set| { - set.insert(hold_pos); - }); - }; - let filter_remove_hold = move |hold_pos: models::HoldPosition| { - set_filter_holds.update(move |set| { - set.remove(&hold_pos); - }); - }; - - let filtered_problems = Memo::new(move |_prev_val| { - let filter_holds = filter_holds.get(); - problems.with(|problems| { - problems - .iter() - .filter(|(_, problem)| filter_holds.iter().all(|hold_pos| problem.holds.contains_key(hold_pos))) - .map(|(problem_uid, problem)| (*problem_uid, problem.clone())) - .collect::>() - }) - }); - - let fn_next_problem = move || { - let problems = filtered_problems.read(); - - use rand::seq::IteratorRandom; - let mut rng = rand::rng(); - let problem_uid = problems.keys().copied().choose(&mut rng); - - set_problem_uid.set(problem_uid); - }; - - // Set a problem when wall is set (loaded) - Effect::new(move |_prev_value| { - if problem_uid.get().is_none() { - tracing::debug!("Setting next problem"); - fn_next_problem(); - } - }); - - let on_click_hold = move |hold_position: models::HoldPosition| { - // Add/Remove hold position to problem filter - set_filter_holds.update(|set| { - if !set.remove(&hold_position) { - set.insert(hold_position); - } - }); - }; - - let grid = move || { - let wall = wall.get(); - view! { } - }; - - let filter = move || { + move || { let mut cells = vec![]; - for hold_pos in filter_holds.get() { - let w = &*wall.read(); + for hold_pos in ctx.filter_holds.get() { + let w = &*ctx.wall.read(); if let Some(hold) = w.holds.get(&hold_pos).cloned() { let onclick = move |_| { - filter_remove_hold(hold_pos); + ctx.cb_remove_hold_from_filter.run(hold_pos); }; let v = view! { @@ -195,7 +275,7 @@ fn WithWall( let problems_counter = { let name = view! {

{"Problems:"}

}; - let value = view! {

{filtered_problems.read().len()}

}; + let value = view! {

{ctx.filtered_problems.read().len()}

}; view! {
{name} {value} @@ -213,8 +293,8 @@ fn WithWall( } let mut interaction_counters = InteractionCounters::default(); let interaction_counters_view = { - let user_ints = user_interactions.read(); - for problem_uid in filtered_problems.read().keys() { + let user_ints = ctx.user_interactions.read(); + for problem_uid in ctx.filtered_problems.read().keys() { if let Some(user_int) = user_ints.get(problem_uid) { match user_int.best_attempt().map(|da| da.attempt) { Some(models::Attempt::Flash) => interaction_counters.flash += 1, @@ -269,101 +349,43 @@ fn WithWall( {problems_counter} {interaction_counters_view} } - }; - - view! { -
-
{grid}
- -
-
{filter}
- - - -
-
-
-
- - - -
- {move || problem.get().map(|p| view! { })} -
- - - - {move || { - let Some(problem_uid) = problem_uid.get() else { - return view! {}.into_any(); - }; - view! { - - } - .into_any() - }} -
-
} } #[component] #[tracing::instrument(skip_all)] -fn WithUserInteraction( - #[prop(into)] wall_uid: Signal, - #[prop(into)] problem_uid: Signal, - #[prop(into)] user_interaction: Signal>, - submit_attempt: StoredValue, -) -> impl IntoView { - tracing::debug!("Enter WithUserInteraction"); +fn AttemptRadioGroup() -> impl IntoView { + crate::tracing::on_enter!(); - let todays_attempt = signals::todays_attempt(user_interaction); + let ctx = use_context::().unwrap(); + + let problem_uid = Signal::derive(move || ctx.problem.read().as_ref().map(|p| p.uid)); let mut attempt_radio_buttons = vec![]; for variant in [models::Attempt::Flash, models::Attempt::Send, models::Attempt::Attempt] { - let ui_toggle = Signal::derive(move || todays_attempt.get() == Some(variant)); + let ui_toggle = Signal::derive(move || ctx.todays_attempt.get() == Some(variant)); + let onclick = move |_| { let attempt = if ui_toggle.get() { None } else { Some(variant) }; - submit_attempt.read_value()(server_functions::UpsertTodaysAttempt { - wall_uid: wall_uid.get(), - problem_uid: problem_uid.get(), - attempt, - }); + + if let Some(problem_uid) = problem_uid.get() { + ctx.cb_upsert_todays_attempt.run(server_functions::UpsertTodaysAttempt { + wall_uid: ctx.wall.read().uid, + problem_uid, + attempt, + }); + } }; attempt_radio_buttons.push(view! { }); } - view! { - {attempt_radio_buttons} - - - } -} - -#[component] -#[tracing::instrument(skip_all)] -fn AttemptRadio(children: Children) -> impl IntoView { - tracing::debug!("Enter"); - - view! { -
{children()}
- } + view! {
{attempt_radio_buttons}
} } #[component] #[tracing::instrument(skip_all)] fn AttemptRadioButton(variant: models::Attempt, #[prop(into)] selected: Signal) -> impl IntoView { - tracing::debug!("Enter"); + crate::tracing::on_enter!(); let text = variant.to_string(); let icon = variant.icon(); @@ -377,13 +399,13 @@ fn AttemptRadioButton(variant: models::Attempt, #[prop(into)] selected: Signal>) -> impl IntoView { - tracing::debug!("Enter"); +fn History() -> impl IntoView { + crate::tracing::on_enter!(); - let latest_attempt = signals::latest_attempt(user_interaction); + let ctx = use_context::().unwrap(); let attempts = move || { - user_interaction + ctx.user_interaction .read() .as_ref() .iter() @@ -397,7 +419,7 @@ fn History(#[prop(into)] user_interaction: Signal } }) @@ -408,44 +430,42 @@ fn History(#[prop(into)] user_interaction: Signal>, - on_click_hold: impl Fn(models::HoldPosition) + 'static, -) -> impl IntoView { - tracing::debug!("Enter"); +fn Wall() -> impl IntoView { + crate::tracing::on_enter!(); - let on_click_hold = std::rc::Rc::new(on_click_hold); + let ctx = use_context::().unwrap(); - let mut cells = vec![]; - for (&hold_position, hold) in &wall.holds { - let role = Signal::derive(move || problem.get().and_then(|p| p.holds.get(&hold_position).copied())); + move || { + let wall = ctx.wall.read(); - let on_click = { - let on_click_hold = std::rc::Rc::clone(&on_click_hold); - move |_| { - on_click_hold(hold_position); - } + let mut cells = vec![]; + for (&hold_position, hold) in &wall.holds { + let hold_role = signals::hold_role(ctx.problem, hold_position); + + let on_click = move |_| { + ctx.cb_click_hold.run(hold_position); + }; + + let cell = view! { +
+ +
+ }; + cells.push(cell); + } + + let style = { + let grid_rows = crate::css::grid_rows_n(wall.rows); + let grid_cols = crate::css::grid_cols_n(wall.cols); + let max_width = format!("{}vh", wall.cols as f64 / wall.rows as f64 * 100.); + format!("max-height: 100vh; max-width: {max_width}; {}", [grid_rows, grid_cols].join(" ")) }; - let cell = view! { -
- -
- }; - cells.push(cell); - } - let style = { - let grid_rows = crate::css::grid_rows_n(wall.rows); - let grid_cols = crate::css::grid_cols_n(wall.cols); - format!("max-height: 90vh; max-width: 90vh; {}", [grid_rows, grid_cols].join(" ")) - }; - view! { -
-
+ view! { +
{cells}
-
+ } } } @@ -459,10 +479,10 @@ fn Hold( #[prop(into)] role: Option>>, ) -> impl IntoView { - tracing::trace!("Enter"); + crate::tracing::on_enter!(); move || { - let mut class = "bg-sky-100 aspect-square rounded-sm hover:brightness-125".to_string(); + let mut class = "bg-sky-100 max-w-full max-h-full aspect-square rounded-sm hover:brightness-125".to_string(); if let Some(role) = role { let role = role.get(); @@ -509,6 +529,7 @@ mod signals { use crate::models; use leptos::prelude::*; use std::collections::BTreeMap; + use std::collections::BTreeSet; pub fn latest_attempt(user_interaction: Signal>) -> Signal> { Signal::derive(move || user_interaction.read().as_ref().and_then(models::UserInteraction::latest_attempt)) @@ -518,6 +539,10 @@ mod signals { Signal::derive(move || latest_attempt.read().as_ref().and_then(models::UserInteraction::todays_attempt)) } + pub fn wall_uid(wall: Signal) -> Signal { + Signal::derive(move || wall.read().uid) + } + pub fn user_interaction( user_interactions: Signal>, problem_uid: Signal>, @@ -539,4 +564,24 @@ mod signals { problems.get(&problem_uid).cloned() }) } + + pub(crate) fn filtered_problems( + problems: Signal>, + filter_holds: Signal>, + ) -> Memo> { + Memo::new(move |_prev_val| { + let filter_holds = filter_holds.read(); + problems.with(|problems| { + problems + .iter() + .filter(|(_, problem)| filter_holds.iter().all(|hold_pos| problem.holds.contains_key(hold_pos))) + .map(|(problem_uid, problem)| (*problem_uid, problem.clone())) + .collect::>() + }) + }) + } + + pub(crate) fn hold_role(problem: Signal>, hold_position: models::HoldPosition) -> Signal> { + Signal::derive(move || problem.get().and_then(|p| p.holds.get(&hold_position).copied())) + } } diff --git a/crates/ascend/src/server/migrations.rs b/crates/ascend/src/server/migrations.rs index b51a5ce..34db626 100644 --- a/crates/ascend/src/server/migrations.rs +++ b/crates/ascend/src/server/migrations.rs @@ -1,12 +1,38 @@ use super::db::Database; use super::db::DatabaseOperationError; use super::db::{self}; +use crate::models; +use redb::ReadableTable; #[tracing::instrument(skip_all, err)] pub async fn run_migrations(db: &Database) -> Result<(), Box> { if is_at_version(db, 2).await? { migrate_to_v3(db).await?; } + + migrate_wall(db).await?; + + Ok(()) +} + +#[tracing::instrument(skip_all, err)] +pub async fn migrate_wall(db: &Database) -> Result<(), Box> { + tracing::warn!("MIGRATING WALL"); + + db.write(|txn| { + let mut table = txn.open_table(db::current::TABLE_WALLS)?; + let wall = table + .get(models::WallUid(uuid::Uuid::parse_str("8a00ab39-89f5-4fc5-b9c6-f86b4c040f68").unwrap()))? + .map(|x| x.value()); + if let Some(mut wall) = wall { + wall.rows = 11; + wall.holds.retain(|k, _| k.row < 11); + table.insert(wall.uid, wall)?; + } + Ok(()) + }) + .await?; + Ok(()) } diff --git a/crates/ascend/src/tracing.rs b/crates/ascend/src/tracing.rs new file mode 100644 index 0000000..c3925fb --- /dev/null +++ b/crates/ascend/src/tracing.rs @@ -0,0 +1,22 @@ +macro_rules! where_am_i { + () => {{ + fn f() {} + + fn type_name_of(_: T) -> &'static str { + std::any::type_name::() + } + + let name = type_name_of(f); + + // `3` is the length of the `::f`. + &name[..name.len() - 3] + }}; +} +pub(crate) use where_am_i; + +macro_rules! on_enter { + () => { + tracing::trace!("Entering {}", crate::tracing::where_am_i!()); + }; +} +pub(crate) use on_enter;