Commit accd8aba2a684cdb34e55f996ba0262525f75a4f

Authored by Georg Hopp
1 parent 55351af6

Client code cleanup and refactoring

  1 +use std::fmt::Display;
  2 +
  3 +use artshop_common::types::MarkdownJson;
  4 +
  5 +use super::super::error::*;
  6 +use super::super::client::Client;
  7 +
  8 +#[derive(Debug, Clone)]
  9 +pub struct Markdown {
  10 + client: Client,
  11 + pub json: MarkdownJson,
  12 +}
  13 +
  14 +impl Markdown {
  15 + pub(crate) async fn new(name :&str) -> Result<Markdown> {
  16 + let client = Client::new()?;
  17 + let api_uri = format!("/api/v0/markdowns/{}", name);
  18 + let (response, data) = client.get(&api_uri).await?;
  19 +
  20 + match response.status() {
  21 + 200 => Ok(Self { client
  22 + , json: serde_json::from_str(data.as_str())? }),
  23 + status => Err(Self::status_error(status)),
  24 + }
  25 + }
  26 +
  27 + pub(crate) async fn read( &mut self
  28 + , patch :Option<i32>
  29 + ) -> Result<&Markdown>
  30 + {
  31 + let api_uri = match patch {
  32 + Some(i) => format!( "/api/v0/markdowns/{}?patch={}"
  33 + , self.json.name, i ),
  34 + None => format!("/api/v0/markdowns/{}", self.json.name),
  35 + };
  36 + let (response, data) = self.client.get(&api_uri).await?;
  37 +
  38 + match response.status() {
  39 + 200 => {
  40 + self.json = serde_json::from_str(data.as_str())?;
  41 + Ok(self)
  42 + },
  43 + status => Err(Self::status_error(status)),
  44 + }
  45 + }
  46 +
  47 + pub(crate) async fn save(&self) -> Result<&Markdown> {
  48 + let url = format!("/api/v0/markdowns/{}", self.json.name);
  49 + let data = serde_json::to_string(&self.json)?;
  50 +
  51 + let response = self.client.put(url.as_str(), data.as_str()).await?;
  52 +
  53 + match response.status() {
  54 + 200 => Ok(self),
  55 + status => Err(Self::status_error(status)),
  56 + }
  57 + }
  58 +
  59 + pub(crate) fn _to_html_string(&self) -> String {
  60 + use pulldown_cmark::{Parser, Options, html};
  61 +
  62 + let mut html_out = String::new();
  63 + let parser = Parser::new_ext(&self.json.content, Options::all());
  64 +
  65 + html::push_html(&mut html_out, parser);
  66 + html_out
  67 + }
  68 +
  69 +
  70 + fn status_error<I: Display>(status :I) -> Error {
  71 + let err_str = format!("Invalid response status: {}", status);
  72 + Error::from(err_str.as_str())
  73 + }
  74 +}
... ...
  1 +pub(crate) mod markdown;
... ...
  1 +use js_sys::JsString;
  2 +use mogwai::prelude::*;
  3 +use wasm_bindgen::prelude::*;
  4 +use web_sys::{Window, window, Response, Request, RequestInit, RequestMode};
  5 +use super::error::*;
  6 +
  7 +use std::result::Result as StdResult;
  8 +
  9 +#[derive(Debug, Clone)]
  10 +pub(crate) struct Client {
  11 + window :Window,
  12 +}
  13 +
  14 +type ReqGetter = fn(&str, &RequestInit) -> StdResult<Request, JsValue>;
  15 +
  16 +const REQUEST :ReqGetter = Request::new_with_str_and_init;
  17 +
  18 +impl Client {
  19 + pub fn new() -> Result<Self> {
  20 + const WINDOW_ERROR :&str = "Unable to get window instance";
  21 +
  22 + Ok(Self { window: window()
  23 + . ok_or(Error::from(WINDOW_ERROR))? })
  24 + }
  25 +
  26 + pub async fn get(&self, url :&str) -> Result<(Response, String)> {
  27 + let mut init = RequestInit::new();
  28 + let request = REQUEST( &url
  29 + , init . method("GET")
  30 + . mode(RequestMode::Cors) )?;
  31 +
  32 + request . headers()
  33 + . set("Accept", "application/json")?;
  34 +
  35 + let response = JsFuture::from( self.window
  36 + . fetch_with_request(&request) )
  37 + . await?
  38 + . dyn_into::<Response>()?;
  39 +
  40 + let data = JsFuture::from(response.text()?)
  41 + . await?
  42 + . dyn_into::<JsString>()?;
  43 +
  44 + Ok((response, String::from(data)))
  45 + }
  46 +
  47 + pub async fn put(&self, url :&str, data :&str) -> Result<Response> {
  48 + let mut init = RequestInit::new();
  49 + let request = REQUEST( &url
  50 + , init . method("PUT")
  51 + . mode(RequestMode::Cors)
  52 + . body(Some(&data.into())) )?;
  53 +
  54 + request . headers()
  55 + . set("Content-Type", "application/json")?;
  56 +
  57 + let response = JsFuture::from( self.window
  58 + . fetch_with_request(&request))
  59 + . await?
  60 + . dyn_into::<Response>()?;
  61 +
  62 + Ok(response)
  63 + }
  64 +}
... ...
  1 +use std::fmt::Display;
  2 +use wasm_bindgen::prelude::*;
  3 +
  4 +type ParentError = Option<Box<dyn std::error::Error>>;
  5 +
  6 +#[derive(Debug)]
  7 +pub(crate) struct Error {
  8 + source: ParentError,
  9 + message: String,
  10 +}
  11 +
  12 +pub(crate) type Result<T> = std::result::Result<T, Error>;
  13 +
  14 +impl std::error::Error for Error {
  15 + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
  16 + self.source.as_deref()
  17 + }
  18 +}
  19 +
  20 +impl Display for Error {
  21 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  22 + match self {
  23 + Error { source: Some(source), message } =>
  24 + write!(f, "{}: {}", message, source),
  25 + Error { source: None, message } => write!(f, "{}", message),
  26 + }
  27 + }
  28 +}
  29 +
  30 +impl From<&str> for Error {
  31 + fn from(message: &str) -> Self {
  32 + Self { source: None, message: String::from(message) }
  33 + }
  34 +}
  35 +
  36 +impl From<JsValue> for Error {
  37 + fn from(source: JsValue) -> Self {
  38 + let source = js_sys::Error::from(source);
  39 +
  40 + let message = format!("[{}] {}", source.name(), source.message());
  41 + let source = Error::from(message.as_str());
  42 +
  43 + Self { source: Some(Box::new(source))
  44 + , message: String::from("WebSys Error")
  45 + }
  46 + }
  47 +}
  48 +
  49 +impl From<serde_json::Error> for Error {
  50 + fn from(source: serde_json::Error) -> Self {
  51 + Self { source: Some(Box::new(source))
  52 + , message: String::from("Serde Error")
  53 + }
  54 + }
  55 +}
... ...
  1 +mod api;
1 2 mod data;
  3 +mod error;
  4 +mod client;
2 5
3   -use artshop_common::types::MarkdownJson;
  6 +use api::markdown::Markdown;
4 7 use data::icons::*;
5   -use js_sys::JsString;
6 8 use log::Level;
7 9 use mogwai::prelude::*;
8   -use web_sys::{RequestInit, RequestMode, Request, Response};
9 10 use std::panic;
10 11 use wasm_bindgen::prelude::*;
11 12
... ... @@ -18,69 +19,16 @@ enum AppLogic {
18 19 Discard,
19 20 }
20 21
21   -fn md_to_html(source: &str) -> String {
22   - use pulldown_cmark::{Parser, Options, html};
23   -
24   - let parser = Parser::new_ext(source, Options::all());
25   - let mut html_output = String::new();
26   - html::push_html(&mut html_output, parser);
27   -
28   - html_output
29   -}
30   -
31   -async fn md_from_db(patch :Option<i32>) -> MarkdownJson {
32   - let window = web_sys::window().unwrap();
33   - let mut opts = RequestInit::new();
34   - opts.method("GET").mode(RequestMode::Cors);
35   -
36   - let api_uri = match patch {
37   - Some(i) => format!("/api/v0/markdowns/md-example?patch={}", i),
38   - None => String::from("/api/v0/markdowns/md-example"),
39   - };
40   -
41   - let request = Request::new_with_str_and_init(&api_uri, &opts).unwrap();
42   - request.headers().set("Accept", "application/json").unwrap();
43   -
44   - let response = JsFuture::from(window.fetch_with_request(&request))
45   - . await.unwrap()
46   - . dyn_into::<Response>().unwrap();
47   - let data = String::from( JsFuture::from(response.text().unwrap())
48   - . await.unwrap()
49   - . dyn_into::<JsString>().unwrap()
50   - );
51   - let data :MarkdownJson = serde_json::from_str(data.as_str()).unwrap();
52   -
53   - data
54   -}
55   -
56   -async fn md_to_db(md :&MarkdownJson) {
57   - let encoded = serde_json::to_string(&md).unwrap();
58   -
59   - let window = web_sys::window().unwrap();
60   - let mut opts = RequestInit::new();
61   - opts . method("PUT")
62   - . mode(RequestMode::Cors)
63   - . body(Some(&encoded.into()));
64   -
65   - let url_str = format!("/api/v0/markdowns/{}", md.name);
66   - let request = Request::new_with_str_and_init(url_str.as_str(), &opts)
67   - . unwrap();
68   - request.headers().set("Content-Type", "application/json").unwrap();
69   -
70   - let _response = JsFuture::from(window.fetch_with_request(&request))
71   - . await.unwrap()
72   - . dyn_into::<Response>().unwrap();
73   -
74   - /* do something with the response here.... */
75   -}
76   -
77 22 async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
78 23 , tx_view: broadcast::Sender<String>
79 24 , mut rx_dom: broadcast::Receiver<Dom> )
80 25 {
81 26 let dom = rx_dom.next().await.unwrap();
82 27 let mut show_edit = false;
83   - let mut md = md_from_db(Some(2)).await;
  28 + let mut md = Markdown::new("md-example").await.unwrap();
  29 +
  30 + // TODO Only for experiments... remove when ready
  31 + md.read(Some(2)).await.unwrap();
84 32
85 33 let container = match dom.inner_read() {
86 34 Either::Left(dom_js) =>
... ... @@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
117 65
118 66 let update = move || {
119 67 match cont_ref {
120   - Some((md_cont, view_cont)) =>
121   - view_cont.set_inner_html(
122   - md_to_html(md_cont.inner_text().as_str()).as_str() ),
  68 + Some((_, view_cont)) => {
  69 + use pulldown_cmark::{Parser, Options, html};
  70 +
  71 + let mut html_out = String::new();
  72 + let md = get_md();
  73 + let parser = Parser::new_ext(&md, Options::all());
  74 +
  75 + html::push_html(&mut html_out, parser);
  76 + view_cont.set_inner_html(&html_out)
  77 + },
123 78 None => (),
124 79 }
125 80 };
126 81
127   - set_md(md.content.as_str());
  82 + set_md(md.json.content.as_str());
128 83 update();
129 84
130 85 /* play with katex ==== */
... ... @@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
136 91
137 92 if let Either::Left(dom_js) = dom.inner_read() {
138 93 dom_js . to_owned()
139   - . dyn_into::<Node>().unwrap()
140   - . child_nodes().get(1).unwrap()
141   - . child_nodes().get(2).unwrap()
142   - . dyn_into::<HtmlElement>().unwrap()
143   - . set_inner_html(formula1.as_str())
  94 + . dyn_into::<Node>().unwrap()
  95 + . child_nodes().get(1).unwrap()
  96 + . child_nodes().get(2).unwrap()
  97 + . dyn_into::<HtmlElement>().unwrap()
  98 + . set_inner_html(formula1.as_str())
144 99 };
145 100
146 101 if let Either::Left(dom_js) = dom.inner_read() {
147 102 dom_js . to_owned()
148   - . dyn_into::<Node>().unwrap()
149   - . child_nodes().get(1).unwrap()
150   - . child_nodes().get(3).unwrap()
151   - . dyn_into::<HtmlElement>().unwrap()
152   - . set_inner_html(formula2.as_str())
  103 + . dyn_into::<Node>().unwrap()
  104 + . child_nodes().get(1).unwrap()
  105 + . child_nodes().get(3).unwrap()
  106 + . dyn_into::<HtmlElement>().unwrap()
  107 + . set_inner_html(formula2.as_str())
153 108 };
154 109 /* =========== */
155 110
156 111 while let Some(msg) = rx_logic.next().await {
157 112 match msg {
158 113 AppLogic::Store => {
159   - md.content = get_md();
160   - md_to_db(&md).await;
  114 + md.json.content = get_md();
  115 + md.save().await.unwrap();
161 116 },
162 117 AppLogic::Update => update(),
163 118 AppLogic::Toggle => {
... ... @@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
170 125 };
171 126 },
172 127 AppLogic::Discard => {
173   - set_md(md.content.as_str());
  128 + set_md(md.json.content.as_str());
174 129 update();
175 130 },
176   - AppLogic::Select(id) => {
177   - md = md_from_db(id).await;
178   - set_md(md.content.as_str());
  131 + AppLogic::Select(_id) => {
  132 + md.read(None).await.unwrap();
  133 + set_md(md.json.content.as_str());
179 134 update();
180 135 },
181 136 }
... ...
Please register or login to post a comment