wip: overhaul event system

This commit is contained in:
Jonathan Kelley committed 2021-10-04 01:28:04 -04:00
1 parent 82953f2ac3
commit 7a03c1d2b4
34 files changed
+2488 -1468

No files matched your search

+10 -4
View File
@@ -4,30 +4,36 @@ use dioxus_core::DomEdit;
use wry::{
application::{
error::OsError,
event_loop::EventLoopWindowTarget,
event_loop::{EventLoop, EventLoopWindowTarget},
menu::MenuBar,
window::{Fullscreen, Icon, Window, WindowBuilder},
},
webview::{RpcRequest, RpcResponse},
webview::{RpcRequest, RpcResponse, WebView},
};
pub struct DesktopConfig<'a> {
pub window: WindowBuilder,
pub(crate) manual_edits: Option<DomEdit<'a>>,
pub(crate) manual_edits: Option<Vec<DomEdit<'a>>>,
pub(crate) pre_rendered: Option<String>,
pub(crate) event_handler: Option<Box<dyn Fn(&mut EventLoop<()>, &mut WebView)>>,
}
impl DesktopConfig<'_> {
impl<'a> DesktopConfig<'a> {
/// Initializes a new `WindowBuilder` with default values.
#[inline]
pub fn new() -> Self {
Self {
event_handler: None,
window: Default::default(),
pre_rendered: None,
manual_edits: None,
}
}
pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) {
self.manual_edits = Some(edits);
}
pub fn with_prerendered(&mut self, content: String) -> &mut Self {
self.pre_rendered = Some(content);
self
+86 -55
View File
@@ -22,69 +22,100 @@ pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
let mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
let data = data.drain(..).next().unwrap();
let event = SyntheticEvent::MouseEvent(MouseEvent(Arc::new(WebviewMouseEvent)));
let scope = ScopeId(data.scope as usize);
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
UserEvent {
name: todo!(),
name: "click",
event,
scope,
mounted_dom_id,
}
}
#[derive(Debug)]
struct WebviewMouseEvent;
impl MouseEventInner for WebviewMouseEvent {
fn alt_key(&self) -> bool {
todo!()
}
fn button(&self) -> i16 {
todo!()
}
fn buttons(&self) -> u16 {
todo!()
}
fn client_x(&self) -> i32 {
todo!()
}
fn client_y(&self) -> i32 {
todo!()
}
fn ctrl_key(&self) -> bool {
todo!()
}
fn meta_key(&self) -> bool {
todo!()
}
fn page_x(&self) -> i32 {
todo!()
}
fn page_y(&self) -> i32 {
todo!()
}
fn screen_x(&self) -> i32 {
todo!()
}
fn screen_y(&self) -> i32 {
todo!()
}
fn shift_key(&self) -> bool {
todo!()
}
fn get_modifier_state(&self, key_code: &str) -> bool {
todo!()
fn event_name_from_typ(typ: &str) -> &'static str {
match typ {
"copy" => "copy",
"cut" => "cut",
"paste" => "paste",
"compositionend" => "compositionend",
"compositionstart" => "compositionstart",
"compositionupdate" => "compositionupdate",
"keydown" => "keydown",
"keypress" => "keypress",
"keyup" => "keyup",
"focus" => "focus",
"blur" => "blur",
"change" => "change",
"input" => "input",
"invalid" => "invalid",
"reset" => "reset",
"submit" => "submit",
"click" => "click",
"contextmenu" => "contextmenu",
"doubleclick" => "doubleclick",
"drag" => "drag",
"dragend" => "dragend",
"dragenter" => "dragenter",
"dragexit" => "dragexit",
"dragleave" => "dragleave",
"dragover" => "dragover",
"dragstart" => "dragstart",
"drop" => "drop",
"mousedown" => "mousedown",
"mouseenter" => "mouseenter",
"mouseleave" => "mouseleave",
"mousemove" => "mousemove",
"mouseout" => "mouseout",
"mouseover" => "mouseover",
"mouseup" => "mouseup",
"pointerdown" => "pointerdown",
"pointermove" => "pointermove",
"pointerup" => "pointerup",
"pointercancel" => "pointercancel",
"gotpointercapture" => "gotpointercapture",
"lostpointercapture" => "lostpointercapture",
"pointerenter" => "pointerenter",
"pointerleave" => "pointerleave",
"pointerover" => "pointerover",
"pointerout" => "pointerout",
"select" => "select",
"touchcancel" => "touchcancel",
"touchend" => "touchend",
"touchmove" => "touchmove",
"touchstart" => "touchstart",
"scroll" => "scroll",
"wheel" => "wheel",
"animationstart" => "animationstart",
"animationend" => "animationend",
"animationiteration" => "animationiteration",
"transitionend" => "transitionend",
"abort" => "abort",
"canplay" => "canplay",
"canplaythrough" => "canplaythrough",
"durationchange" => "durationchange",
"emptied" => "emptied",
"encrypted" => "encrypted",
"ended" => "ended",
"error" => "error",
"loadeddata" => "loadeddata",
"loadedmetadata" => "loadedmetadata",
"loadstart" => "loadstart",
"pause" => "pause",
"play" => "play",
"playing" => "playing",
"progress" => "progress",
"ratechange" => "ratechange",
"seeked" => "seeked",
"seeking" => "seeking",
"stalled" => "stalled",
"suspend" => "suspend",
"timeupdate" => "timeupdate",
"volumechange" => "volumechange",
"waiting" => "waiting",
"toggle" => "toggle",
_ => {
panic!("unsupported event type")
}
}
}
+1 -206
View File
@@ -9,212 +9,7 @@
<div id="_dioxusroot">
</div>
</body>
<script>
class Interpreter {
constructor(root) {
this.root = root;
this.stack = [root];
this.listeners = {
"onclick": {}
};
this.lastNodeWasText = false;
this.nodes = [root, root, root, root];
}
top() {
return this.stack[this.stack.length - 1];
}
pop() {
return this.stack.pop();
}
PushRoot(edit) {
const id = edit.id;
const node = this.nodes[id];
console.log("pushing root ", node, "with id", id);
this.stack.push(node);
}
PopRoot(edit) {
this.stack.pop();
}
AppendChildren(edit) {
let root = this.stack[this.stack.length - (edit.many + 1)];
for (let i = 0; i < edit.many; i++) {
console.log("popping ", i, edit.many);
let node = this.pop();
root.appendChild(node);
}
}
ReplaceWith(edit) {
let root = this.stack[this.stack.length - (edit.many + 1)];
let els = [];
for (let i = 0; i < edit.many; i++) {
els.push(this.pop());
}
root.replaceWith(...els);
}
Remove(edit) {
const node = this.stack.pop();
node.remove();
}
RemoveAllChildren(edit) {}
CreateTextNode(edit) {
const node = document.createTextNode(edit.text);
this.nodes[edit.id] = node;
this.stack.push(node);
}
CreateElement(edit) {
const tagName = edit.tag;
const el = document.createElement(tagName);
this.nodes[edit.id] = el;
console.log(`creating element: `, edit);
this.stack.push(el);
}
CreateElementNs(edit) {
const tagName = edit.tag;
console.log(`creating namespaced element: `, edit);
this.stack.push(document.createElementNS(edit.ns, edit.tag));
}
CreatePlaceholder(edit) {
const a = `this.stack.push(document.createElement(" pre"))`;
this.stack.push(document.createComment("vroot"));
}
NewEventListener(edit) {
const element_id = edit.element_id;
const event_name = edit.event_name;
const mounted_node_id = edit.mounted_node_id;
const scope = edit.scope;
const element = this.top();
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
console.log("listener map is", this.listeners);
if (this.listeners[event_name] === undefined) {
console.log("adding listener!");
this.listeners[event_name] = "bla";
this.root.addEventListener(event_name, (event) => {
const target = event.target;
const type = event.type;
const val = target.getAttribute(`dioxus-event-${event_name}`);
const fields = val.split(".");
const scope_id = parseInt(fields[0]);
const real_id = parseInt(fields[1]);
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
rpc.call('user_event', {
event: event_name,
scope: scope_id,
mounted_dom_id: real_id,
}).then((reply) => {
console.log(reply);
this.stack.push(this.root);
let edits = reply.edits;
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = this[edit.type];
f.call(this, edit);
}
console.log("initiated");
}).catch((err) => {
console.log("failed to initiate", err);
});
});
}
}
RemoveEventListener(edit) {}
SetText(edit) {
this.top().textContent = edit.text;
}
SetAttribute(edit) {
const name = edit.field;
const value = edit.value;
const ns = edit.ns;
const node = this.top(this.stack);
if (ns == "style") {
node.style[name] = value;
} else if (ns !== undefined) {
node.setAttributeNS(ns, name, value);
} else {
node.setAttribute(name, value);
}
if (name === "value") {
node.value = value;
}
if (name === "checked") {
node.checked = true;
}
if (name === "selected") {
node.selected = true;
}
}
RemoveAttribute(edit) {
const name = edit.field;
const node = this.top(this.stack);
node.removeAttribute(name);
if (name === "value") {
node.value = null;
}
if (name === "checked") {
node.checked = false;
}
if (name === "selected") {
node.selected = false;
}
}
}
async function initialize() {
const reply = await rpc.call('initiate');
let root = window.document.getElementById("_dioxusroot");
const interpreter = new Interpreter(root);
console.log(reply);
let pre_rendered = reply.pre_rendered;
if (pre_rendered !== undefined) {
root.innerHTML = pre_rendered;
}
const edits = reply.edits;
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = interpreter[edit.type];
f.call(interpreter, edit);
}
console.log("stack completed: ", interpreter.stack);
}
console.log("initializing...");
initialize();
<script type="text/javascript" src="/index.js">
</script>
</html>
+216
View File
@@ -0,0 +1,216 @@
class Interpreter {
constructor(root) {
this.root = root;
this.stack = [root];
this.listeners = {
"onclick": {}
};
this.lastNodeWasText = false;
this.nodes = [root, root, root, root];
}
top() {
return this.stack[this.stack.length - 1];
}
pop() {
return this.stack.pop();
}
PushRoot(edit) {
const id = edit.id;
const node = this.nodes[id];
console.log("pushing root ", node, "with id", id);
this.stack.push(node);
}
PopRoot(_edit) {
this.stack.pop();
}
AppendChildren(edit) {
let root = this.stack[this.stack.length - (1 + edit.many)];
let to_add = this.stack.splice(this.stack.length - edit.many);
for (let i = 0; i < edit.many; i++) {
root.appendChild(to_add[i]);
}
}
ReplaceWith(edit) {
console.log(edit);
let root = this.nodes[edit.root];
let els = this.stack.splice(this.stack.length - edit.m);
console.log(root);
console.log(els);
root.replaceWith(...els);
}
Remove(edit) {
let node = this.nodes[edit.element_id];
node.remove();
}
CreateTextNode(edit) {
const node = document.createTextNode(edit.text);
this.nodes[edit.id] = node;
this.stack.push(node);
}
CreateElement(edit) {
const tagName = edit.tag;
const el = document.createElement(tagName);
this.nodes[edit.id] = el;
this.stack.push(el);
}
CreateElementNs(edit) {
let el = document.createElementNS(edit.ns, edit.tag);
this.stack.push(el);
this.nodes[edit.id] = el;
}
CreatePlaceholder(edit) {
let el = document.createElement("pre");
// let el = document.createComment("vroot");
this.stack.push(el);
this.nodes[edit.id] = el;
}
RemoveEventListener(edit) { }
SetText(edit) {
this.top().textContent = edit.text;
}
SetAttribute(edit) {
const name = edit.field;
const value = edit.value;
const ns = edit.ns;
const node = this.top(this.stack);
if (ns == "style") {
node.style[name] = value;
} else if (ns !== undefined) {
node.setAttributeNS(ns, name, value);
} else {
node.setAttribute(name, value);
}
if (name === "value") {
node.value = value;
}
if (name === "checked") {
node.checked = true;
}
if (name === "selected") {
node.selected = true;
}
}
RemoveAttribute(edit) {
const name = edit.field;
const node = this.top(this.stack);
node.removeAttribute(name);
if (name === "value") {
node.value = null;
}
if (name === "checked") {
node.checked = false;
}
if (name === "selected") {
node.selected = false;
}
}
InsertAfter(edit) {
let old = this.nodes[edit.element_id];
let new_nodes = this.stack.splice(edit.many);
old.after(...new_nodes);
}
InsertBefore(edit) {
let old = this.nodes[edit.element_id];
let new_nodes = this.stack.splice(edit.many);
old.before(...new_nodes);
}
NewEventListener(edit) {
const event_name = edit.event_name;
const mounted_node_id = edit.mounted_node_id;
const scope = edit.scope;
const element = this.top();
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
console.log("listener map is", this.listeners);
if (this.listeners[event_name] === undefined) {
console.log("adding listener!");
this.listeners[event_name] = "bla";
this.root.addEventListener(event_name, (event) => {
const target = event.target;
const val = target.getAttribute(`dioxus-event-${event_name}`);
const fields = val.split(".");
const scope_id = parseInt(fields[0]);
const real_id = parseInt(fields[1]);
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
rpc.call('user_event', {
event: event_name,
scope: scope_id,
mounted_dom_id: real_id,
}).then((reply) => {
console.log(reply);
this.stack.push(this.root);
let edits = reply.edits;
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = this[edit.type];
f.call(this, edit);
}
console.log("initiated");
}).catch((err) => {
console.log("failed to initiate", err);
});
});
}
}
}
async function initialize() {
const reply = await rpc.call('initiate');
let root = window.document.getElementById("_dioxusroot");
const interpreter = new Interpreter(root);
console.log(reply);
let pre_rendered = reply.pre_rendered;
if (pre_rendered !== undefined) {
root.innerHTML = pre_rendered;
}
const edits = reply.edits;
apply_edits(edits, interpreter);
}
function apply_edits(edits, interpreter) {
for (let x = 0; x < edits.length; x++) {
let edit = edits[x];
console.log(edit);
let f = interpreter[edit.type];
f.call(interpreter, edit);
}
console.log("stack completed: ", interpreter.stack);
}
initialize();
+159 -111
View File
@@ -4,20 +4,28 @@
//!
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use cfg::DesktopConfig;
use dioxus_core::scheduler::SchedulerMsg;
use dioxus_core::*;
// use futures_channel::mpsc::UnboundedSender;
use serde::{Deserialize, Serialize};
mod logging;
pub use logging::set_up_logging;
pub use wry;
use wry::application::event::{Event, WindowEvent};
use wry::application::event_loop::{ControlFlow, EventLoop};
use wry::application::event_loop::{self, ControlFlow, EventLoop};
use wry::application::window::Fullscreen;
use wry::webview::WebViewBuilder;
use wry::webview::{WebView, WebViewBuilder};
use wry::{
application::window::{Window, WindowBuilder},
webview::{RpcRequest, RpcResponse},
@@ -45,10 +53,16 @@ pub fn launch_with_props<P: Properties + 'static + Send + Sync>(
run(root, props, builder)
}
#[derive(Serialize)]
enum RpcEvent<'a> {
Initialize { edits: Vec<DomEdit<'a>> },
}
enum BridgeEvent {
Initialize(serde_json::Value),
Update(serde_json::Value),
}
#[derive(Serialize)]
struct Response<'a> {
pre_rendered: Option<String>,
@@ -60,72 +74,175 @@ pub fn run<T: Properties + 'static + Send + Sync>(
props: T,
user_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
) -> anyhow::Result<()> {
run_with_edits(root, props, user_builder, None)
}
pub fn run_with_edits<
F: for<'a, 'b> FnOnce(&'a mut DesktopConfig<'b>) -> &'a mut DesktopConfig<'b>,
T: Properties + 'static + Send + Sync,
>(
root: FC<T>,
props: T,
user_builder: F,
redits: Option<Vec<DomEdit<'static>>>,
) -> anyhow::Result<()> {
/*
*/
let mut cfg = DesktopConfig::new();
user_builder(&mut cfg);
let DesktopConfig {
window,
manual_edits,
pre_rendered,
..
} = cfg;
let event_loop = EventLoop::new();
let window = window.build(&event_loop)?;
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
let sender = launch_vdom_with_tokio(root, props, event_tx.clone());
let locked_receiver = Rc::new(RefCell::new(event_rx));
let webview = WebViewBuilder::new(window)?
.with_url("wry://src/index.html")?
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
match req.method.as_str() {
"initiate" => {
//
let mut rx = (*locked_receiver).borrow_mut();
match rx.try_recv() {
Ok(BridgeEvent::Initialize(edits)) => {
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
}
_ => None,
}
}
"user_event" => {
//
let data = req.params.unwrap();
log::debug!("Data: {:#?}", data);
let event = events::trigger_from_serialized(data);
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
let mut rx = (*locked_receiver).borrow_mut();
match rx.blocking_recv() {
Some(BridgeEvent::Update(edits)) => {
log::info!("Passing response back");
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
}
None => {
log::error!("Sender half is gone");
None
}
_ => {
log::error!("No update event received");
None
}
}
}
_ => todo!("this message failed"),
}
})
// this isn't quite portable unfortunately :(
// todo: figure out a way to allow us to create the index.html with the index.js file separately
// it's a bit easier to hack with
.with_custom_protocol("wry".into(), move |request| {
use std::fs::{canonicalize, read};
use wry::http::ResponseBuilder;
// Remove url scheme
let path = request.uri().replace("wry://", "");
// Read the file content from file path
let content = read(canonicalize(&path)?)?;
// Return asset contents and mime types based on file extentions
// If you don't want to do this manually, there are some crates for you.
// Such as `infer` and `mime_guess`.
let (data, meta) = if path.ends_with(".html") {
(content, "text/html")
} else if path.ends_with(".js") {
(content, "text/javascript")
} else if path.ends_with(".png") {
(content, "image/png")
} else {
unimplemented!();
};
ResponseBuilder::new().mimetype(meta).body(data)
})
.build()?;
run_event_loop(event_loop, webview, event_tx);
Ok(())
}
pub fn start<P: 'static + Send>(
root: FC<P>,
config_builder: impl for<'a, 'b> FnOnce(&'b mut DesktopConfig<'a>) -> &'b mut DesktopConfig<'a>,
) -> ((), ()) {
//
((), ())
}
// Create a new tokio runtime on a dedicated thread and then launch the apps VirtualDom.
fn launch_vdom_with_tokio<C: Send + 'static>(
root: FC<C>,
props: C,
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
// Spawn the virtualdom onto its own thread
// if it wants to spawn multithreaded tasks, it can use the executor directly
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
let sender_2 = sender.clone();
std::thread::spawn(move || {
//
let runtime = tokio::runtime::Builder::new_current_thread()
// We create the runtim as multithreaded, so you can still "spawn" onto multiple threads
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
runtime.block_on(async move {
let mut vir = VirtualDom::new_with_props(root, props);
let channel = vir.get_event_sender();
let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
let _ = vir.get_event_sender();
let edits = vir.rebuild();
#[derive(Serialize)]
struct Evt<'a> {
edits: Vec<DomEdit<'a>>,
}
// let msg = RpcEvent::Initialize { edits: edits.edits };
let edit_string = serde_json::to_value(Evt { edits: edits.edits }).unwrap();
match event_tx.send(BridgeEvent::Initialize(edit_string)) {
Ok(_) => {}
Err(_) => {}
}
loop {
vir.wait_for_work().await;
let edits = vir.run_with_deadline(|| false);
let edit_string = serde_json::to_string(&edits[0].edits).unwrap();
event_tx.send(edit_string).unwrap();
log::info!("{}", vir);
let mut muts = vir.run_with_deadline(|| false);
log::info!("muts {:#?}", muts);
while let Some(edit) = muts.pop() {
let edit_string = serde_json::to_value(Evt { edits: edit.edits }).unwrap();
match event_tx.send(BridgeEvent::Update(edit_string)) {
Ok(_) => {}
Err(er) => {
log::error!("Sending should not fail {}", er);
}
}
}
log::info!("mutations sent on channel");
}
})
});
let dioxus_requsted = Arc::new(AtomicBool::new(false));
sender_2
}
let webview = WebViewBuilder::new(window)?
.with_url(&format!("data:text/html,{}", HTML_CONTENT))?
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
match req.method.as_str() {
"initiate" => {}
"user_event" => {}
_ => todo!("this message failed"),
}
todo!()
})
.build()?;
event_loop.run(move |event, _, control_flow| {
fn run_event_loop(
event_loop: EventLoop<()>,
webview: WebView,
event_tx: tokio::sync::mpsc::UnboundedSender<BridgeEvent>,
) {
let _ = event_tx.clone();
event_loop.run(move |event, target, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
@@ -146,74 +263,5 @@ pub fn run_with_edits<
_ => {}
}
});
Ok(())
})
}
// let edits = if let Some(edits) = &redits {
// serde_json::to_value(edits).unwrap()
// } else {
// let mut lock = vdom.write().unwrap();
// // let mut reg_lock = registry.write().unwrap();
// // Create the thin wrapper around the registry to collect the edits into
// let mut real = dom::WebviewDom::new();
// let pre = pre_rendered.clone();
// let response = match pre {
// Some(content) => {
// lock.rebuild_in_place().unwrap();
// Response {
// edits: Vec::new(),
// pre_rendered: Some(content),
// }
// }
// None => {
// //
// let edits = {
// // let mut edits = Vec::new();
// todo!()
// // lock.rebuild(&mut real, &mut edits).unwrap();
// // edits
// };
// Response {
// edits,
// pre_rendered: None,
// }
// }
// };
// serde_json::to_value(&response).unwrap()
// };
// // Return the edits into the webview runtime
// Some(RpcResponse::new_result(req.id.take(), Some(edits)))
// log::debug!("User event received");
// // let registry = registry.clone();
// let vdom = vdom.clone();
// let response = async_std::task::block_on(async move {
// let mut lock = vdom.write().unwrap();
// // let mut reg_lock = registry.write().unwrap();
// // a deserialized event
// let data = req.params.unwrap();
// log::debug!("Data: {:#?}", data);
// let event = trigger_from_serialized(data);
// // lock.queue_event(event);
// // Create the thin wrapper around the registry to collect the edits into
// let mut real = dom::WebviewDom::new();
// // Serialize the edit stream
// //
// let mut edits = Vec::new();
// // lock.run(&mut real, &mut edits)
// // .await
// // .expect("failed to progress");
// let response = Response {
// edits,
// pre_rendered: None,
// };
// let response = serde_json::to_value(&response).unwrap();
// // Give back the registry into its slot
// // *reg_lock = Some(real.consume());
// // Return the edits into the webview runtime
// Some(RpcResponse::new_result(req.id.take(), Some(response)))
// });
// response
// // spawn a task to clean up the garbage
+51
View File
@@ -0,0 +1,51 @@
pub fn set_up_logging(enabled: bool) {
use fern::colors::{Color, ColoredLevelConfig};
if !enabled {
return;
}
// configure colors for the whole line
let colors_line = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
// we actually don't need to specify the color for debug and info, they are white by default
.info(Color::White)
.debug(Color::White)
// depending on the terminals color scheme, this is the same as the background color
.trace(Color::BrightBlack);
// configure colors for the name of the level.
// since almost all of them are the same as the color for the whole line, we
// just clone `colors_line` and overwrite our changes
let colors_level = colors_line.clone().info(Color::Green);
// here we set up our fern Dispatch
// when running tests in batch, the logger is re-used, so ignore the logger error
let _ = fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{color_line}[{level}{color_line}] {message}\x1B[0m",
color_line = format_args!(
"\x1B[{}m",
colors_line.get_color(&record.level()).to_fg_str()
),
level = colors_level.color(record.level()),
message = message,
));
})
// set the default log level. to filter out verbose log messages from dependencies, set
// this to Warn and overwrite the log level for your crate.
.level(log::LevelFilter::Debug)
// .level(log::LevelFilter::Warn)
// change log levels for individual modules. Note: This looks for the record's target
// field which defaults to the module path but can be overwritten with the `target`
// parameter:
// `info!(target="special_target", "This log message is about special_target");`
// .level_for("dioxus", log::LevelFilter::Debug)
// .level_for("dioxus", log::LevelFilter::Info)
// .level_for("pretty_colored", log::LevelFilter::Trace)
// output to stdout
.chain(std::io::stdout())
.apply();
}