wip
This commit is contained in:
@@ -27,7 +27,7 @@ leptos_axum = { version = "0.7", optional = true }
|
||||
leptos_meta = { version = "0.7" }
|
||||
leptos_router = { version = "0.7.0" }
|
||||
moonboard-parser = { workspace = true, optional = true }
|
||||
rand = { version = "0.8", optional = true }
|
||||
rand = { version = "0.9", default-features = false, features = ["std_rng"] }
|
||||
ron = { version = "0.8" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
server_fn = { version = "0.7.4", features = ["cbor"] }
|
||||
@@ -58,7 +58,6 @@ ssr = [
|
||||
"dep:redb",
|
||||
"dep:bincode",
|
||||
"dep:tokio",
|
||||
"dep:rand",
|
||||
"dep:tower",
|
||||
"dep:tower-http",
|
||||
"dep:leptos_axum",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::models;
|
||||
use crate::pages;
|
||||
@@ -5,7 +6,6 @@ use leptos::prelude::*;
|
||||
use leptos_router::components::*;
|
||||
use leptos_router::path;
|
||||
use std::sync::Arc;
|
||||
use type_toppings::ResultExt;
|
||||
|
||||
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
||||
use leptos_meta::MetaTags;
|
||||
@@ -45,9 +45,9 @@ pub fn App() -> impl leptos::IntoView {
|
||||
<Router>
|
||||
<Routes fallback=|| "Not found">
|
||||
<Route path=path!("/") view=Home />
|
||||
<Route path=path!("/wall/:id") view=pages::wall::Wall />
|
||||
<Route path=path!("/wall/:id/edit") view=pages::edit_wall::EditWall />
|
||||
<Route path=path!("/wall/:id/routes") view=pages::routes::Routes />
|
||||
<Route path=path!("/wall/:wall_uid") view=pages::wall::Wall />
|
||||
<Route path=path!("/wall/:wall_uid/edit") view=pages::edit_wall::EditWall />
|
||||
<Route path=path!("/wall/:wall_uid/routes") view=pages::routes::Routes />
|
||||
</Routes>
|
||||
</Router>
|
||||
}
|
||||
@@ -86,7 +86,11 @@ pub fn Home() -> impl leptos::IntoView {
|
||||
leptos::view! {}
|
||||
}
|
||||
|
||||
#[server]
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
||||
use redb::ReadableTable;
|
||||
@@ -105,8 +109,6 @@ async fn get_walls() -> Result<RonEncoded<Vec<models::Wall>>, ServerFnError> {
|
||||
tracing::debug!("opened table");
|
||||
let walls: Vec<models::Wall> = walls_table.iter()?.map(|r| r.map(|(_, v)| v.value())).collect::<Result<_, _>>()?;
|
||||
|
||||
tracing::debug!("got walls {walls:?}");
|
||||
|
||||
Ok(walls)
|
||||
})
|
||||
.await??;
|
||||
|
||||
@@ -69,35 +69,6 @@ pub mod ron {
|
||||
}
|
||||
}
|
||||
|
||||
// impl<T> serde::Serialize for RonEncoded<T>
|
||||
// where
|
||||
// T: serde::Serialize,
|
||||
// {
|
||||
// #[tracing::instrument(skip_all, err)]
|
||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: serde::Serializer,
|
||||
// {
|
||||
// let serialized = ron::to_string(&self.0).map_err(serde::ser::Error::custom)?;
|
||||
// serializer.serialize_str(&serialized)
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'de, T> serde::Deserialize<'de> for RonEncoded<T>
|
||||
// where
|
||||
// T: serde::de::DeserializeOwned + 'static,
|
||||
// {
|
||||
// #[tracing::instrument(skip_all, err)]
|
||||
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
// where
|
||||
// D: serde::Deserializer<'de>,
|
||||
// {
|
||||
// let s = String::deserialize(deserializer)?;
|
||||
// let t: T = ron::from_str(&s).map_err(serde::de::Error::custom)?;
|
||||
// Ok(Self(t))
|
||||
// }
|
||||
// }
|
||||
|
||||
// IntoReq
|
||||
impl<T, Request, Err> IntoReq<Ron, Request, Err> for RonEncoded<T>
|
||||
where
|
||||
@@ -151,7 +122,6 @@ pub mod ron {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ron;
|
||||
use super::RonEncoded;
|
||||
use codee::Decoder;
|
||||
use codee::Encoder;
|
||||
use serde::Deserialize;
|
||||
@@ -163,27 +133,6 @@ pub mod ron {
|
||||
value: i32,
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_ron_wrapper() {
|
||||
// let original = TestStruct {
|
||||
// name: "Test".to_string(),
|
||||
// value: 42,
|
||||
// };
|
||||
|
||||
// // Wrap in RonCodec
|
||||
// let wrapped = RonEncoded::new(original.clone());
|
||||
|
||||
// // Serialize
|
||||
// let serialized = serde_json::to_string(&wrapped).expect("Serialization failed");
|
||||
// println!("Serialized: {}", serialized);
|
||||
|
||||
// // Deserialize
|
||||
// let deserialized: RonEncoded<TestStruct> = serde_json::from_str(&serialized).expect("Deserialization failed");
|
||||
|
||||
// // Compare
|
||||
// assert_eq!(deserialized.into_inner(), original);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_ron_codec() {
|
||||
let original = TestStruct {
|
||||
|
||||
@@ -4,6 +4,12 @@ use web_sys::MouseEvent;
|
||||
#[component]
|
||||
pub fn Button(#[prop(into)] text: String, onclick: impl Fn(MouseEvent) -> () + 'static) -> impl IntoView {
|
||||
view! {
|
||||
<button on:click=onclick type="button" class="text-black bg-orange-300 hover:bg-orange-400 focus:ring-4 focus:ring-orange-500 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none">{ text }</button>
|
||||
<button
|
||||
on:click=onclick
|
||||
type="button"
|
||||
class="text-black bg-orange-300 hover:bg-orange-400 focus:ring-4 focus:ring-orange-500 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 focus:outline-none"
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ pub fn StyledHeader(items: HeaderItems) -> impl IntoView {
|
||||
view! {
|
||||
<div class="bg-orange-300 text-black border-b-2 border-b-orange-400">
|
||||
// <div class="container mx-auto" >
|
||||
<Header items />
|
||||
// </div>
|
||||
<Header items />
|
||||
// </div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ pub mod components {
|
||||
|
||||
pub mod codec;
|
||||
|
||||
pub mod server_functions;
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod server;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
@@ -35,17 +36,17 @@ pub fn EditWall() -> impl leptos::IntoView {
|
||||
right: vec![],
|
||||
};
|
||||
|
||||
leptos::view! {
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<StyledHeader items=header_items />
|
||||
// leptos::view! {
|
||||
// <div class="min-w-screen min-h-screen bg-slate-900">
|
||||
// <StyledHeader items=header_items />
|
||||
|
||||
<div class="container mx-auto mt-2">
|
||||
<Await future=load let:data>
|
||||
<Ready data=data.deref().to_owned() />
|
||||
</Await>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
// <div class="container mx-auto mt-2">
|
||||
// <Await future=load let:data>
|
||||
// <Ready data=data.deref().to_owned() />
|
||||
// </Await>
|
||||
// </div>
|
||||
// </div>
|
||||
// }
|
||||
}
|
||||
|
||||
#[component]
|
||||
@@ -149,7 +150,12 @@ pub struct Image {
|
||||
file_contents: Vec<u8>,
|
||||
}
|
||||
|
||||
#[server]
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||
todo!()
|
||||
// let wall = state.persistent.with(|s| s.wall.clone()).await;
|
||||
@@ -157,7 +163,7 @@ async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||
}
|
||||
|
||||
#[server(name = SetImage, input = Cbor)]
|
||||
#[tracing::instrument(skip(image))]
|
||||
#[tracing::instrument(skip(image), err)]
|
||||
async fn set_image(hold_position: HoldPosition, image: Image) -> Result<models::Hold, ServerFnError> {
|
||||
tracing::info!("Setting image, {}, {} bytes", image.file_name, image.file_contents.len());
|
||||
|
||||
|
||||
@@ -1,20 +1,42 @@
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
use crate::components::header::StyledHeader;
|
||||
use crate::models;
|
||||
use crate::models::WallUid;
|
||||
use leptos::Params;
|
||||
use leptos::prelude::*;
|
||||
use leptos_router::params::Params;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeSet;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Params, PartialEq, Clone)]
|
||||
struct RouteParams {
|
||||
wall_uid: Option<models::WallUid>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn Routes() -> impl leptos::IntoView {
|
||||
let load = async move {
|
||||
// TODO: What to do about this unwrap?
|
||||
load_initial_data().await.unwrap()
|
||||
};
|
||||
tracing::debug!("Enter");
|
||||
|
||||
let params = leptos_router::hooks::use_params::<RouteParams>();
|
||||
|
||||
let problems = Resource::<Option<models::Wall>, Ron>::new_with_options(
|
||||
move || params.get().map(|p| p.wall_uid),
|
||||
move |wall_uid: Result<Option<WallUid>, _>| async move {
|
||||
if let Ok(Some(wall_uid)) = wall_uid {
|
||||
let wall = crate::server_functions::get_wall(wall_uid).await.unwrap().into_inner();
|
||||
Some(wall)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
let header_items = HeaderItems {
|
||||
left: vec![HeaderItem {
|
||||
@@ -57,27 +79,6 @@ fn Ready(data: InitialData) -> impl leptos::IntoView {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct InitialData {
|
||||
problems: BTreeSet<models::Problem>,
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn load_initial_data() -> Result<RonEncoded<InitialData>, ServerFnError> {
|
||||
todo!()
|
||||
// let state = expect_context::<State>();
|
||||
|
||||
// let problems = state
|
||||
// .persistent
|
||||
// .with(|s| {
|
||||
// let problems = &s.problems.problems;
|
||||
// problems.clone()
|
||||
// })
|
||||
// .await;
|
||||
|
||||
// Ok(RonCodec::new(InitialData { problems }))
|
||||
}
|
||||
|
||||
#[server(name = ImportFromMiniMoonboard)]
|
||||
#[tracing::instrument]
|
||||
async fn import_from_mini_moonboard() -> Result<(), ServerFnError> {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::components::button::Button;
|
||||
use crate::components::header::HeaderItem;
|
||||
use crate::components::header::HeaderItems;
|
||||
@@ -11,45 +10,33 @@ use leptos::Params;
|
||||
use leptos::prelude::*;
|
||||
use leptos::reactive::graph::ReactiveNode;
|
||||
use leptos_router::params::Params;
|
||||
use std::sync::Arc;
|
||||
use rand::SeedableRng;
|
||||
|
||||
#[derive(Params, PartialEq, Clone)]
|
||||
struct WallParams {
|
||||
id: Option<models::WallUid>,
|
||||
struct RouteParams {
|
||||
wall_uid: Option<models::WallUid>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn Wall() -> impl leptos::IntoView {
|
||||
tracing::debug!("Enter");
|
||||
let params = leptos_router::hooks::use_params::<WallParams>();
|
||||
|
||||
// TODO
|
||||
let wall = Resource::<models::Wall, Ron>::new_with_options(
|
||||
move || params.get().unwrap().id,
|
||||
{
|
||||
move |wall_uid: Option<WallUid>| async move {
|
||||
let wall_uid = wall_uid.unwrap();
|
||||
let wall = get_wall(wall_uid).await.unwrap().into_inner();
|
||||
wall
|
||||
let params = leptos_router::hooks::use_params::<RouteParams>();
|
||||
|
||||
let wall = Resource::<Option<models::Wall>, Ron>::new_with_options(
|
||||
move || params.get().map(|p| p.wall_uid),
|
||||
move |wall_uid: Result<Option<WallUid>, _>| async move {
|
||||
if let Ok(Some(wall_uid)) = wall_uid {
|
||||
let wall = crate::server_functions::get_wall(wall_uid).await.unwrap().into_inner();
|
||||
Some(wall)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
// // TODO
|
||||
// let wall = Resource::new(
|
||||
// move || params.get().unwrap().id,
|
||||
// move |wall_id| async move {
|
||||
// if let Some(wall_id) = wall_id {
|
||||
// let wall = get_wall(wall_id).await.unwrap();
|
||||
// Some(wall)
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
|
||||
let header_items = HeaderItems {
|
||||
left: vec![],
|
||||
middle: vec![HeaderItem {
|
||||
@@ -69,23 +56,23 @@ pub fn Wall() -> impl leptos::IntoView {
|
||||
};
|
||||
|
||||
leptos::view! {
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<StyledHeader items=header_items />
|
||||
<div class="min-w-screen min-h-screen bg-slate-900">
|
||||
<StyledHeader items=header_items />
|
||||
|
||||
<div class="m-2">
|
||||
<Suspense fallback=move || view! {<p>"Loading..."</p>}>
|
||||
{move || Suspend::new(async move{
|
||||
let wall: Option<Option<RonEncoded<models::Wall>>> = wall.get();
|
||||
wall.map(|wall|{let wall = wall.unwrap();
|
||||
view! {
|
||||
<Ready wall=wall.into_inner() />
|
||||
}
|
||||
})
|
||||
})}
|
||||
</Suspense>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<Suspense fallback=move || {
|
||||
view! { <p>"Loading..."</p> }
|
||||
}>
|
||||
{move || Suspend::new(async move {
|
||||
let wall: Option<models::Wall> = wall.get().flatten();
|
||||
wall.map(|wall| {
|
||||
view! { <Ready wall/> }
|
||||
})
|
||||
})}
|
||||
</Suspense>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
@@ -93,14 +80,33 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
||||
tracing::debug!("ready");
|
||||
|
||||
let (current_problem, current_problem_writer) = signal(None::<models::Problem>);
|
||||
let problem_fetcher = LocalResource::new(move || async move {
|
||||
tracing::info!("Loading random problem");
|
||||
let problem = get_random_problem().await.expect("cannot get random problem");
|
||||
if problem.is_none() {
|
||||
tracing::info!("No problem returned by server in response to request for random problem");
|
||||
}
|
||||
current_problem_writer.set(problem.into_inner());
|
||||
});
|
||||
let problem_fetcher = {
|
||||
LocalResource::new(move || {
|
||||
let wall_uid = wall.uid;
|
||||
let problems = wall.problems.clone();
|
||||
|
||||
async move {
|
||||
tracing::info!("Loading random problem");
|
||||
|
||||
// TODO: seed properly
|
||||
use rand::seq::IteratorRandom;
|
||||
let rng = &mut rand::rngs::StdRng::seed_from_u64(0);
|
||||
let random_problem = problems.iter().choose(rng);
|
||||
|
||||
let problem = if let Some(random_problem) = random_problem {
|
||||
crate::server_functions::get_problem(wall_uid, *random_problem)
|
||||
.await
|
||||
.expect("cannot get random problem")
|
||||
.into_inner()
|
||||
} else {
|
||||
tracing::info!("Wall has no problems");
|
||||
None
|
||||
};
|
||||
|
||||
current_problem_writer.set(problem);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let mut cells = vec![];
|
||||
for (&hold_position, hold) in &wall.holds {
|
||||
@@ -117,17 +123,18 @@ fn Ready(wall: models::Wall) -> impl leptos::IntoView {
|
||||
view! {
|
||||
<div class="grid grid-cols-[auto,1fr] gap-8">
|
||||
// Render the wall
|
||||
<div style="max-height: 90vh; max-width: 90vh;" class=move || { grid_classes.clone() }>{cells}</div>
|
||||
<div style="max-height: 90vh; max-width: 90vh;" class=move || { grid_classes.clone() }>
|
||||
{cells}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
// TODO:
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
||||
<div>// TODO:
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.name.clone())}</p>
|
||||
// <p>{current_problem.read().as_ref().map(|p| p.set_by.clone())}</p>
|
||||
</div>
|
||||
|
||||
<Button onclick=move |_| problem_fetcher.mark_dirty() text="➤ Next problem" />
|
||||
</ div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -161,74 +168,3 @@ fn Hold(hold: models::Hold, #[prop(into)] role: Signal<Option<HoldRole>>) -> imp
|
||||
tracing::trace!("view");
|
||||
view! { <div class=class>{img}</div> }
|
||||
}
|
||||
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
||||
enum Error {
|
||||
#[display("Wall not found: {_0:?}")]
|
||||
NotFound(#[error(not(source))] models::WallUid),
|
||||
}
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let wall = tokio::task::spawn_blocking(move || -> Result<models::Wall, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let walls_table = read_txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
||||
let wall = walls_table.get(wall_uid)?.ok_or(Error::NotFound(wall_uid))?.value();
|
||||
|
||||
Ok(wall)
|
||||
})
|
||||
.await??;
|
||||
|
||||
tracing::debug!("ok");
|
||||
|
||||
Ok(RonEncoded::new(wall))
|
||||
}
|
||||
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
async fn get_problem(wall_uid: models::WallUid, problem_uid: models::ProblemUid) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let problem = tokio::task::spawn_blocking(move || -> Result<Option<models::Problem>, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let table = read_txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
||||
let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value());
|
||||
|
||||
Ok(problem)
|
||||
})
|
||||
.await??;
|
||||
|
||||
// use rand::seq::IteratorRandom;
|
||||
|
||||
// let state = expect_context::<State>();
|
||||
|
||||
// let problem = state
|
||||
// .persistent
|
||||
// .with(|s| {
|
||||
// let problems = &s.problems.problems;
|
||||
// let rng = &mut rand::thread_rng();
|
||||
// problems.iter().choose(rng).cloned()
|
||||
// })
|
||||
// .await;
|
||||
|
||||
// tracing::debug!("Returning randomized problem: {problem:?}");
|
||||
|
||||
Ok(RonEncoded::new(problem))
|
||||
}
|
||||
|
||||
66
crates/ascend/src/server_functions.rs
Normal file
66
crates/ascend/src/server_functions.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use crate::codec::ron::Ron;
|
||||
use crate::codec::ron::RonEncoded;
|
||||
use crate::models;
|
||||
use leptos::prelude::expect_context;
|
||||
use leptos::server;
|
||||
use server_fn::ServerFnError;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub(crate) async fn get_wall(wall_uid: models::WallUid) -> Result<RonEncoded<models::Wall>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
#[derive(Debug, derive_more::Error, derive_more::Display)]
|
||||
enum Error {
|
||||
#[display("Wall not found: {_0:?}")]
|
||||
NotFound(#[error(not(source))] models::WallUid),
|
||||
}
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let wall = tokio::task::spawn_blocking(move || -> Result<models::Wall, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let walls_table = read_txn.open_table(crate::server::db::current::TABLE_WALLS)?;
|
||||
let wall = walls_table.get(wall_uid)?.ok_or(Error::NotFound(wall_uid))?.value();
|
||||
|
||||
Ok(wall)
|
||||
})
|
||||
.await??;
|
||||
|
||||
tracing::debug!("ok");
|
||||
|
||||
Ok(RonEncoded::new(wall))
|
||||
}
|
||||
|
||||
#[server(
|
||||
input = Ron,
|
||||
output = Ron,
|
||||
custom = RonEncoded
|
||||
)]
|
||||
#[tracing::instrument(skip_all, err)]
|
||||
pub(crate) async fn get_problem(
|
||||
wall_uid: models::WallUid,
|
||||
problem_uid: models::ProblemUid,
|
||||
) -> Result<RonEncoded<Option<models::Problem>>, ServerFnError> {
|
||||
tracing::debug!("Enter");
|
||||
|
||||
let db = expect_context::<Arc<redb::Database>>();
|
||||
|
||||
let problem = tokio::task::spawn_blocking(move || -> Result<Option<models::Problem>, ServerFnError> {
|
||||
let read_txn = db.begin_read()?;
|
||||
|
||||
let table = read_txn.open_table(crate::server::db::current::TABLE_PROBLEMS)?;
|
||||
let problem = table.get((wall_uid, problem_uid))?.map(|guard| guard.value());
|
||||
|
||||
Ok(problem)
|
||||
})
|
||||
.await??;
|
||||
|
||||
Ok(RonEncoded::new(problem))
|
||||
}
|
||||
Reference in New Issue
Block a user