From b49cff6694658f1e75f5f81298c3d122e1bf458b Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Fri, 15 Nov 2024 23:07:23 -0800 Subject: [PATCH] rustfmt, resultext --- .gitignore | 2 +- Cargo.lock | 5 + src/discord/src/channel/mod.rs | 2 +- src/discord/src/client.rs | 34 +++-- src/discord/src/lib.rs | 4 +- src/discord/src/message/author.rs | 2 +- src/discord/src/message/content.rs | 2 +- src/discord/src/message/mod.rs | 3 +- src/discord/src/snowflake.rs | 4 +- src/ui/Cargo.toml | 10 +- src/ui/src/_main.rs | 219 ++++++++++++----------------- src/ui/src/channel/message.rs | 12 +- src/ui/src/channel/message_list.rs | 17 ++- src/ui/src/channel/mod.rs | 72 +++++----- src/ui/src/main.rs | 88 +++++------- src/util/Cargo.toml | 6 + src/util/src/lib.rs | 19 +++ 17 files changed, 249 insertions(+), 252 deletions(-) create mode 100644 src/util/Cargo.toml create mode 100644 src/util/src/lib.rs diff --git a/.gitignore b/.gitignore index ed77ce5..5df70c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ /target .vscode - +.DS_Store .direnv .env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c463c9b..5f599bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4280,6 +4280,7 @@ dependencies = [ "reqwest_client", "scope-backend-discord", "scope-chat", + "scope-util", "tokio", ] @@ -4301,6 +4302,10 @@ dependencies = [ "tokio", ] +[[package]] +name = "scope-util" +version = "0.1.0" + [[package]] name = "scoped-tls" version = "1.0.1" diff --git a/src/discord/src/channel/mod.rs b/src/discord/src/channel/mod.rs index 71436cb..bdf0272 100644 --- a/src/discord/src/channel/mod.rs +++ b/src/discord/src/channel/mod.rs @@ -26,4 +26,4 @@ impl Channel for DiscordChannel { fn get_receiver(&self) -> broadcast::Receiver { self.receiver.resubscribe() } -} \ No newline at end of file +} diff --git a/src/discord/src/client.rs b/src/discord/src/client.rs index 9932548..b0d375a 100644 --- a/src/discord/src/client.rs +++ b/src/discord/src/client.rs @@ -1,13 +1,24 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; -use serenity::{all::{Context, EventHandler, GatewayIntents, Message}, async_trait, futures::SinkExt}; +use serenity::{ + all::{Context, EventHandler, GatewayIntents, Message}, + async_trait, + futures::SinkExt, +}; use tokio::sync::{broadcast, Mutex, RwLock}; -use crate::{message::{author::{DiscordMessageAuthor, DisplayName}, content::DiscordMessageContent, DiscordMessage}, snowflake::{self, Snowflake}}; +use crate::{ + message::{ + author::{DiscordMessageAuthor, DisplayName}, + content::DiscordMessageContent, + DiscordMessage, + }, + snowflake::{self, Snowflake}, +}; #[derive(Default)] pub struct DiscordClient { - channel_message_event_handlers: HashMap>> + channel_message_event_handlers: HashMap>>, } impl DiscordClient { @@ -16,10 +27,7 @@ impl DiscordClient { let remote = RemoteDiscordClient(client.clone()); tokio::spawn(async { - let mut client = serenity::Client::builder(token, GatewayIntents::all()) - .event_handler(remote) - .await - .expect("Error creating client"); + let mut client = serenity::Client::builder(token, GatewayIntents::all()).event_handler(remote).await.expect("Error creating client"); if let Err(why) = client.start().await { panic!("Client error: {why:?}"); @@ -45,7 +53,9 @@ impl EventHandler for RemoteDiscordClient { async fn message(&self, _: Context, msg: Message) { println!("Got message: {:?} {:?}", msg.channel_id, msg.content); - let snowflake = Snowflake { content: msg.channel_id.get() }; + let snowflake = Snowflake { + content: msg.channel_id.get(), + }; if let Some(vec) = self.0.read().await.channel_message_event_handlers.get(&snowflake) { for sender in vec { @@ -55,13 +65,13 @@ impl EventHandler for RemoteDiscordClient { id: snowflake, author: DiscordMessageAuthor { display_name: DisplayName(msg.author.name.clone()), - icon: msg.author.avatar_url().unwrap_or(msg.author.default_avatar_url()) + icon: msg.author.avatar_url().unwrap_or(msg.author.default_avatar_url()), }, content: DiscordMessageContent { - content: msg.content.clone() - } + content: msg.content.clone(), + }, }); } } } -} \ No newline at end of file +} diff --git a/src/discord/src/lib.rs b/src/discord/src/lib.rs index 5bdfffb..405c81d 100644 --- a/src/discord/src/lib.rs +++ b/src/discord/src/lib.rs @@ -1,4 +1,4 @@ -pub mod message; -pub mod snowflake; pub mod channel; pub mod client; +pub mod message; +pub mod snowflake; diff --git a/src/discord/src/message/author.rs b/src/discord/src/message/author.rs index 5476fba..6cd8ec1 100644 --- a/src/discord/src/message/author.rs +++ b/src/discord/src/message/author.rs @@ -24,4 +24,4 @@ impl RenderOnce for DisplayName { fn render(self, _: &mut WindowContext) -> impl IntoElement { div().text_sm().child(self.0) } -} \ No newline at end of file +} diff --git a/src/discord/src/message/content.rs b/src/discord/src/message/content.rs index a7f0a61..823bf42 100644 --- a/src/discord/src/message/content.rs +++ b/src/discord/src/message/content.rs @@ -9,4 +9,4 @@ impl RenderOnce for DiscordMessageContent { fn render(self, _: &mut WindowContext) -> impl IntoElement { self.content.clone() } -} \ No newline at end of file +} diff --git a/src/discord/src/message/mod.rs b/src/discord/src/message/mod.rs index c8935fe..9d33b7c 100644 --- a/src/discord/src/message/mod.rs +++ b/src/discord/src/message/mod.rs @@ -5,8 +5,8 @@ use scope_chat::message::Message; use crate::snowflake::Snowflake; -pub mod content; pub mod author; +pub mod content; #[derive(Clone)] pub struct DiscordMessage { @@ -28,4 +28,3 @@ impl Message for DiscordMessage { self.id.to_string() } } - diff --git a/src/discord/src/snowflake.rs b/src/discord/src/snowflake.rs index 8bd09ac..45b6447 100644 --- a/src/discord/src/snowflake.rs +++ b/src/discord/src/snowflake.rs @@ -1,10 +1,10 @@ #[derive(Clone, Hash, PartialEq, Eq, Copy)] pub struct Snowflake { - pub content: u64 + pub content: u64, } impl ToString for Snowflake { fn to_string(&self) -> String { self.content.to_string() } -} \ No newline at end of file +} diff --git a/src/ui/Cargo.toml b/src/ui/Cargo.toml index 7b9e678..920531c 100644 --- a/src/ui/Cargo.toml +++ b/src/ui/Cargo.toml @@ -3,7 +3,7 @@ name = "scope" version = "0.1.0" edition = "2021" authors = [ - "Rose Kodsi-Hall " + "Rose Kodsi-Hall ", # Add yourself here if you contribute! ] description = "discord client for power users" @@ -13,9 +13,13 @@ repository = "https://github.com/scopeclient/scope" keywords = ["discord", "scope", "reticle"] [dependencies] -gpui = { git = "https://github.com/zed-industries/zed.git", version = "0.1.0", default-features = false, features = ["http_client", "font-kit"]} -reqwest_client = { git = "https://github.com/zed-industries/zed.git", version = "0.1.0"} +gpui = { git = "https://github.com/zed-industries/zed.git", version = "0.1.0", default-features = false, features = [ + "http_client", + "font-kit", +] } +reqwest_client = { git = "https://github.com/zed-industries/zed.git", version = "0.1.0" } scope-chat = { version = "0.1.0", path = "../chat" } +scope-util = { version = "0.1.0", path = "../util" } scope-backend-discord = { version = "0.1.0", path = "../discord" } dotenv = "0.15.0" env_logger = "0.11.5" diff --git a/src/ui/src/_main.rs b/src/ui/src/_main.rs index abe85cd..8a03564 100644 --- a/src/ui/src/_main.rs +++ b/src/ui/src/_main.rs @@ -6,159 +6,120 @@ use gpui::*; use std::fs; struct Assets { - base: PathBuf, + base: PathBuf, } impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - fs::read(self.base.join(path)) - .map(|data| Some(std::borrow::Cow::Owned(data))) - .map_err(|e| e.into()) - } - - fn list(&self, path: &str) -> Result> { - fs::read_dir(self.base.join(path)) - .map(|entries| { - entries - .filter_map(|entry| { - entry - .ok() - .and_then(|entry| entry.file_name().into_string().ok()) - .map(SharedString::from) - }) - .collect() - }) - .map_err(|e| e.into()) - } + fn load(&self, path: &str) -> Result>> { + fs::read(self.base.join(path)).map(|data| Some(std::borrow::Cow::Owned(data))).map_err(|e| e.into()) + } + + fn list(&self, path: &str) -> Result> { + fs::read_dir(self.base.join(path)) + .map(|entries| entries.filter_map(|entry| entry.ok().and_then(|entry| entry.file_name().into_string().ok()).map(SharedString::from)).collect()) + .map_err(|e| e.into()) + } } #[derive(IntoElement)] struct ImageContainer { - text: SharedString, - src: ImageSource, + text: SharedString, + src: ImageSource, } impl ImageContainer { - pub fn new(text: impl Into, src: impl Into) -> Self { - Self { - text: text.into(), - src: src.into(), - } + pub fn new(text: impl Into, src: impl Into) -> Self { + Self { + text: text.into(), + src: src.into(), } + } } impl RenderOnce for ImageContainer { - fn render(self, _: &mut WindowContext) -> impl IntoElement { - div().child( - div() - .flex_row() - .size_full() - .gap_4() - .child(self.text) - .child(img(self.src).w(px(256.0)).h(px(256.0))), - ) - } + fn render(self, _: &mut WindowContext) -> impl IntoElement { + div().child(div().flex_row().size_full().gap_4().child(self.text).child(img(self.src).w(px(256.0)).h(px(256.0)))) + } } struct ImageShowcase { - local_resource: Arc, - remote_resource: SharedUri, - asset_resource: SharedString, + local_resource: Arc, + remote_resource: SharedUri, + asset_resource: SharedString, } impl Render for ImageShowcase { - fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + div() + .size_full() + .flex() + .flex_col() + .justify_center() + .items_center() + .gap_8() + .bg(rgb(0xFFFFFF)) + .child( div() - .size_full() - .flex() - .flex_col() - .justify_center() - .items_center() - .gap_8() - .bg(rgb(0xFFFFFF)) - .child( - div() - .flex() - .flex_row() - .justify_center() - .items_center() - .gap_8() - .child(ImageContainer::new( - "Image loaded from a local file", - self.local_resource.clone(), - )) - .child(ImageContainer::new( - "Image loaded from a remote resource", - self.remote_resource.clone(), - )) - .child(ImageContainer::new( - "Image loaded from an asset", - self.asset_resource.clone(), - )), - ) - .child( - div() - .flex() - .flex_row() - .gap_8() - .child( - div() - .flex_col() - .child("Auto Width") - .child(img("https://picsum.photos/800/400").h(px(180.))), - ) - .child( - div() - .flex_col() - .child("Auto Height") - .child(img("https://picsum.photos/480/640").w(px(180.))), - ), - ) - } + .flex() + .flex_row() + .justify_center() + .items_center() + .gap_8() + .child(ImageContainer::new("Image loaded from a local file", self.local_resource.clone())) + .child(ImageContainer::new("Image loaded from a remote resource", self.remote_resource.clone())) + .child(ImageContainer::new("Image loaded from an asset", self.asset_resource.clone())), + ) + .child( + div() + .flex() + .flex_row() + .gap_8() + .child(div().flex_col().child("Auto Width").child(img("https://picsum.photos/800/400").h(px(180.)))) + .child(div().flex_col().child("Auto Height").child(img("https://picsum.photos/480/640").w(px(180.)))), + ) + } } actions!(image, [Quit]); fn main() { - App::new() - .with_assets(Assets { - base: PathBuf::from("crates/gpui/examples"), + App::new() + .with_assets(Assets { + base: PathBuf::from("crates/gpui/examples"), + }) + .run(|cx: &mut AppContext| { + cx.activate(true); + cx.on_action(|_: &Quit, cx| cx.quit()); + cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); + cx.set_menus(vec![Menu { + name: "Image".into(), + items: vec![MenuItem::action("Quit", Quit)], + }]); + + let window_options = WindowOptions { + titlebar: Some(TitlebarOptions { + title: Some(SharedString::from("Image Example")), + appears_transparent: false, + ..Default::default() + }), + + window_bounds: Some(WindowBounds::Windowed(Bounds { + size: size(px(1100.), px(600.)), + origin: Point::new(px(200.), px(200.)), + })), + + ..Default::default() + }; + + cx.open_window(window_options, |cx| { + cx.new_view(|_cx| ImageShowcase { + // Relative path to your root project path + local_resource: Arc::new(PathBuf::from_str("crates/gpui/examples/image/app-icon.png").unwrap()), + remote_resource: "https://picsum.photos/512/512".into(), + + asset_resource: "image/color.svg".into(), }) - .run(|cx: &mut AppContext| { - cx.activate(true); - cx.on_action(|_: &Quit, cx| cx.quit()); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - cx.set_menus(vec![Menu { - name: "Image".into(), - items: vec![MenuItem::action("Quit", Quit)], - }]); - - let window_options = WindowOptions { - titlebar: Some(TitlebarOptions { - title: Some(SharedString::from("Image Example")), - appears_transparent: false, - ..Default::default() - }), - - window_bounds: Some(WindowBounds::Windowed(Bounds { - size: size(px(1100.), px(600.)), - origin: Point::new(px(200.), px(200.)), - })), - - ..Default::default() - }; - - cx.open_window(window_options, |cx| { - cx.new_view(|_cx| ImageShowcase { - // Relative path to your root project path - local_resource: Arc::new( - PathBuf::from_str("crates/gpui/examples/image/app-icon.png").unwrap(), - ), - remote_resource: "https://picsum.photos/512/512".into(), - - asset_resource: "image/color.svg".into(), - }) - }) - .unwrap(); - }); -} \ No newline at end of file + }) + .unwrap(); + }); +} diff --git a/src/ui/src/channel/message.rs b/src/ui/src/channel/message.rs index 289813e..5b6afda 100644 --- a/src/ui/src/channel/message.rs +++ b/src/ui/src/channel/message.rs @@ -2,12 +2,12 @@ use gpui::{div, img, rgb, IntoElement, ParentElement, Styled}; use scope_chat::message::{Message, MessageAuthor}; pub fn message(message: impl Message) -> impl IntoElement { - div().flex().flex_row().text_color(rgb(0xFFFFFF)) + div() + .flex() + .flex_row() + .text_color(rgb(0xFFFFFF)) .gap_2() .p_2() .child(img(message.get_author().get_icon()).object_fit(gpui::ObjectFit::Fill).bg(rgb(0xFFFFFF)).rounded_full().w_12().h_12()) - .child(div().flex().flex_col() - .child(message.get_author().get_display_name()) - .child(message.get_content()) - ) -} \ No newline at end of file + .child(div().flex().flex_col().child(message.get_author().get_display_name()).child(message.get_content())) +} diff --git a/src/ui/src/channel/message_list.rs b/src/ui/src/channel/message_list.rs index 4c75145..4ad3ba0 100644 --- a/src/ui/src/channel/message_list.rs +++ b/src/ui/src/channel/message_list.rs @@ -1,15 +1,20 @@ use gpui::{ListState, Pixels}; -use scope_backend_discord::{message::{author::{DiscordMessageAuthor, DisplayName}, content::DiscordMessageContent, DiscordMessage}, snowflake::Snowflake}; +use scope_backend_discord::{ + message::{ + author::{DiscordMessageAuthor, DisplayName}, + content::DiscordMessageContent, + DiscordMessage, + }, + snowflake::Snowflake, +}; use scope_chat::message::Message; pub struct MessageList { - pub (super) messages: Vec, + pub(super) messages: Vec, } impl MessageList { pub fn new() -> MessageList { - Self { - messages: Vec::default(), - } + Self { messages: Vec::default() } } -} \ No newline at end of file +} diff --git a/src/ui/src/channel/mod.rs b/src/ui/src/channel/mod.rs index 803414c..12b0538 100644 --- a/src/ui/src/channel/mod.rs +++ b/src/ui/src/channel/mod.rs @@ -1,7 +1,9 @@ -pub mod message_list; pub mod message; +pub mod message_list; -use gpui::{div, list, rgb, AppContext, Context, IntoElement, ListAlignment, ListState, Model, ParentElement, Pixels, Render, Styled, View, VisualContext}; +use gpui::{ + div, list, rgb, AppContext, Context, IntoElement, ListAlignment, ListState, Model, ParentElement, Pixels, Render, Styled, View, VisualContext, +}; use message::message; use message_list::MessageList; use scope_backend_discord::message::DiscordMessage; @@ -9,48 +11,50 @@ use scope_chat::{channel::Channel, message::Message}; pub struct ChannelView { list_state: ListState, - list_model: Model> + list_model: Model>, } impl ChannelView { pub fn create(ctx: &mut gpui::WindowContext, mut channel: impl Channel + 'static) -> View { let view = ctx.new_view(|ctx| { let state_model = ctx.new_model(|_cx| MessageList::::new()); - + let async_model = state_model.clone(); let mut async_ctx = ctx.to_async(); - ctx.foreground_executor().spawn(async move { - loop { - let message = channel.get_receiver().recv().await.unwrap(); + ctx + .foreground_executor() + .spawn(async move { + loop { + let message = channel.get_receiver().recv().await.unwrap(); + + println!("Got Message!"); - println!("Got Message!"); + async_model + .update(&mut async_ctx, |data, ctx| { + data.messages.push(message); + ctx.notify(); + }) + .unwrap(); + } + }) + .detach(); - async_model.update(&mut async_ctx, |data, ctx| { - data.messages.push(message); - ctx.notify(); - }).unwrap(); - } - }).detach(); + ctx + .observe(&state_model, |this: &mut ChannelView, model, cx| { + let items = model.read(cx).messages.clone(); - ctx.observe(&state_model, |this: &mut ChannelView, model, cx| { - let items = model.read(cx).messages.clone(); - - this.list_state = ListState::new( - items.len(), - ListAlignment::Bottom, - Pixels(20.), - move |idx, _cx| { + this.list_state = ListState::new(items.len(), ListAlignment::Bottom, Pixels(20.), move |idx, _cx| { let item = items.get(idx).unwrap().clone(); div().child(message(item)).into_any_element() - }, - ); - - cx.notify(); - }).detach(); - + }); + + cx.notify(); + }) + .detach(); + let items = state_model.read(ctx).messages.clone(); - + ChannelView:: { list_state: ListState::new(items.len(), ListAlignment::Bottom, Pixels(20.), move |idx, _cx| { let item = items.get(idx).unwrap().clone(); @@ -66,12 +70,6 @@ impl ChannelView { impl Render for ChannelView { fn render(&mut self, cx: &mut gpui::ViewContext) -> impl gpui::IntoElement { - div() - .flex() - .flex_col() - .w_full() - .h_full() - .child(list(self.list_state.clone()).w_full().h_full()) - .child(div().flex().h_24().bg(rgb(0xFF0000))) + div().flex().flex_col().w_full().h_full().child(list(self.list_state.clone()).w_full().h_full()).child(div().flex().h_24().bg(rgb(0xFF0000))) } -} \ No newline at end of file +} diff --git a/src/ui/src/main.rs b/src/ui/src/main.rs index 22713e4..0ee0554 100644 --- a/src/ui/src/main.rs +++ b/src/ui/src/main.rs @@ -6,72 +6,62 @@ use channel::ChannelView; use gpui::*; use scope_backend_discord::{channel::DiscordChannel, client::DiscordClient, message::DiscordMessage, snowflake::Snowflake}; use scope_chat::channel::Channel; +use scope_util::ResultExt; struct HelloWorld { - text: SharedString, + text: SharedString, } - + impl Render for HelloWorld { - fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { - div() - .flex() - .bg(rgb(0x2e7d32)) - .size_full() - .justify_center() - .items_center() - .text_xl() - .text_color(rgb(0xffffff)) - .child(format!("Hello, {}!", &self.text)) - } + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + div() + .flex() + .bg(rgb(0x2e7d32)) + .size_full() + .justify_center() + .items_center() + .text_xl() + .text_color(rgb(0xffffff)) + .child(format!("Hello, {}!", &self.text)) + } } struct Assets { - base: PathBuf, + base: PathBuf, } impl AssetSource for Assets { - fn load(&self, path: &str) -> Result>> { - fs::read(self.base.join(path)) - .map(|data| Some(std::borrow::Cow::Owned(data))) - .map_err(|e| e.into()) - } + fn load(&self, path: &str) -> Result>> { + fs::read(self.base.join(path)).map(|data| Some(std::borrow::Cow::Owned(data))).map_err(|e| e.into()) + } - fn list(&self, path: &str) -> Result> { - fs::read_dir(self.base.join(path)) - .map(|entries| { - entries - .filter_map(|entry| { - entry - .ok() - .and_then(|entry| entry.file_name().into_string().ok()) - .map(SharedString::from) - }) - .collect() - }) - .map_err(|e| e.into()) - } + fn list(&self, path: &str) -> Result> { + fs::read_dir(self.base.join(path)) + .map(|entries| entries.filter_map(|entry| entry.ok().and_then(|entry| entry.file_name().into_string().ok()).map(SharedString::from)).collect()) + .map_err(|e| e.into()) + } } #[tokio::main] async fn main() { - env_logger::init(); + env_logger::init(); - let token = dotenv::var("DISCORD_TOKEN").unwrap(); - let demo_channel_id = dotenv::var("DEMO_CHANNEL_ID").unwrap(); + let token = dotenv::var("DISCORD_TOKEN").unwrap_with_message("Must provide DISCORD_TOKEN in .env"); + let demo_channel_id = dotenv::var("DEMO_CHANNEL_ID").unwrap_with_message("Must provide DEMO_CHANNEL_ID in .env"); - let mut client = DiscordClient::new(token); + let mut client = DiscordClient::new(token); - let mut channel = DiscordChannel::new(&mut client, Snowflake { content: demo_channel_id.parse().unwrap() }).await; + let mut channel = DiscordChannel::new( + &mut client, + Snowflake { + content: demo_channel_id.parse().unwrap(), + }, + ) + .await; - App::new() - .with_assets(Assets { - base: PathBuf::from("img"), - }) - .with_http_client(Arc::new(reqwest_client::ReqwestClient::new())) - .run(|cx: &mut AppContext| { - let window = cx.open_window(WindowOptions::default(), |cx| { - ChannelView::::create(cx, channel) - }) - .unwrap(); - }); + App::new().with_assets(Assets { base: PathBuf::from("img") }).with_http_client(Arc::new(reqwest_client::ReqwestClient::new())).run( + |cx: &mut AppContext| { + let window = cx.open_window(WindowOptions::default(), |cx| ChannelView::::create(cx, channel)).unwrap(); + }, + ); } diff --git a/src/util/Cargo.toml b/src/util/Cargo.toml new file mode 100644 index 0000000..bee1cca --- /dev/null +++ b/src/util/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "scope-util" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/util/src/lib.rs b/src/util/src/lib.rs new file mode 100644 index 0000000..dde05f8 --- /dev/null +++ b/src/util/src/lib.rs @@ -0,0 +1,19 @@ +pub trait ResultExt { + fn unwrap_with_message(self, message: &str) -> T; +} + +impl ResultExt for Result +where + E: std::fmt::Debug, +{ + fn unwrap_with_message(self, message: &str) -> T { + match self { + Ok(value) => value, + Err(error) => { + eprintln!("Error: {}", message); + eprintln!("{:?}", error); + panic!(); + } + } + } +}