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 mod data; 2 mod data;
  3 +mod error;
  4 +mod client;
2 5
3 -use artshop_common::types::MarkdownJson; 6 +use api::markdown::Markdown;
4 use data::icons::*; 7 use data::icons::*;
5 -use js_sys::JsString;  
6 use log::Level; 8 use log::Level;
7 use mogwai::prelude::*; 9 use mogwai::prelude::*;
8 -use web_sys::{RequestInit, RequestMode, Request, Response};  
9 use std::panic; 10 use std::panic;
10 use wasm_bindgen::prelude::*; 11 use wasm_bindgen::prelude::*;
11 12
@@ -18,69 +19,16 @@ enum AppLogic { @@ -18,69 +19,16 @@ enum AppLogic {
18 Discard, 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 async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> 22 async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
78 , tx_view: broadcast::Sender<String> 23 , tx_view: broadcast::Sender<String>
79 , mut rx_dom: broadcast::Receiver<Dom> ) 24 , mut rx_dom: broadcast::Receiver<Dom> )
80 { 25 {
81 let dom = rx_dom.next().await.unwrap(); 26 let dom = rx_dom.next().await.unwrap();
82 let mut show_edit = false; 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 let container = match dom.inner_read() { 33 let container = match dom.inner_read() {
86 Either::Left(dom_js) => 34 Either::Left(dom_js) =>
@@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> @@ -117,14 +65,21 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
117 65
118 let update = move || { 66 let update = move || {
119 match cont_ref { 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 None => (), 78 None => (),
124 } 79 }
125 }; 80 };
126 81
127 - set_md(md.content.as_str()); 82 + set_md(md.json.content.as_str());
128 update(); 83 update();
129 84
130 /* play with katex ==== */ 85 /* play with katex ==== */
@@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> @@ -136,28 +91,28 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
136 91
137 if let Either::Left(dom_js) = dom.inner_read() { 92 if let Either::Left(dom_js) = dom.inner_read() {
138 dom_js . to_owned() 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 if let Either::Left(dom_js) = dom.inner_read() { 101 if let Either::Left(dom_js) = dom.inner_read() {
147 dom_js . to_owned() 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 while let Some(msg) = rx_logic.next().await { 111 while let Some(msg) = rx_logic.next().await {
157 match msg { 112 match msg {
158 AppLogic::Store => { 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 AppLogic::Update => update(), 117 AppLogic::Update => update(),
163 AppLogic::Toggle => { 118 AppLogic::Toggle => {
@@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic> @@ -170,12 +125,12 @@ async fn editor_logic( mut rx_logic: broadcast::Receiver<AppLogic>
170 }; 125 };
171 }, 126 },
172 AppLogic::Discard => { 127 AppLogic::Discard => {
173 - set_md(md.content.as_str()); 128 + set_md(md.json.content.as_str());
174 update(); 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 update(); 134 update();
180 }, 135 },
181 } 136 }
Please register or login to post a comment