diff --git a/src/bootstrap.rs b/src/bootstrap.rs index 474a394..4ce2769 100644 --- a/src/bootstrap.rs +++ b/src/bootstrap.rs @@ -1,15 +1,9 @@ +//! Application start-up code handling CLI args, logging, config file load etc + use std::fs::File; use std::io::Read; use failure::Fallible; -use serde::Deserialize; -use serde::Serialize; -use crate::store::Store; use crate::Config; -use elefren::{Registration, Mastodon}; -use elefren::http_send::HttpSender; -use elefren::helpers::cli; -use elefren::scopes::Scopes; -use std::str::FromStr; const CONFIG_FILE: &str = "manabu.toml"; @@ -39,6 +33,7 @@ pub fn load_config(file: &str) -> Fallible { Ok(config) } +/// Boot up the application. Initializes common stuff and returns the loaded config. pub fn handle_cli_args_and_load_config() -> Fallible { let version = format!("{}, built from {}", env!("CARGO_PKG_VERSION"), env!("GIT_REV")); let argv = diff --git a/src/ele.rs b/src/ele.rs index eedb91a..58c9eac 100644 --- a/src/ele.rs +++ b/src/ele.rs @@ -1,6 +1,5 @@ -use std::fs::File; -use std::io::Read; -use failure::Fallible; +//! Mastodon API types and functions building on elefren + use serde::Deserialize; use serde::Serialize; use crate::store::Store; @@ -10,13 +9,14 @@ use elefren::http_send::HttpSender; use elefren::helpers::cli; use elefren::scopes::Scopes; use std::str::FromStr; +use failure::Fallible; const KEY_OAUTH_REGISTRATION: &str = "oauth.registration"; const KEY_OAUTH_SESSION: &str = "oauth.session"; pub type EleRegistratered = elefren::registration::Registered; pub type EleSession = elefren::Mastodon; -pub type EleWebsocket = websocket::sync::Client>; +pub type EleStreamSocket = websocket::sync::Client>; /// Wrapper for the long tuple with Registration state #[derive(Serialize,Deserialize,Debug)] @@ -49,6 +49,7 @@ pub fn register(store : &mut Store, config : &Config) -> EleRegistratered { } } +/// Open mastodon API session (get access token) pub fn open_session(store : &mut Store, registered: EleRegistratered) -> EleSession { match store.get::(KEY_OAUTH_SESSION) { Some(data) => { @@ -68,8 +69,9 @@ pub fn open_session(store : &mut Store, registered: EleRegistratered) -> EleSess } } -pub fn open_stream_websocket(session : &EleSession, stream_name : &str) -> EleWebsocket { - let connector = native_tls::TlsConnector::new().unwrap(); +/// Open streaming api websocket +pub fn open_stream_websocket(session : &EleSession, stream_name : &str) -> Fallible { + let connector = native_tls::TlsConnector::new()?; let hostname = &session.data.base[session.data.base.find("://").unwrap()+3..]; @@ -80,8 +82,7 @@ pub fn open_stream_websocket(session : &EleSession, stream_name : &str) -> EleWe debug!("WS url = {}", &url); - websocket::ClientBuilder::new(&url) + Ok(websocket::ClientBuilder::new(&url) .expect("Error create ClientBuilder") - .connect_secure(Some(connector)) - .expect("Error connect to wss") + .connect_secure(Some(connector))?) } diff --git a/src/main.rs b/src/main.rs index 71c5222..4c3d784 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,19 +6,9 @@ extern crate failure; extern crate smart_default; #[macro_use] extern crate serde; -#[macro_use] -use elefren::{ - helpers::cli, - prelude::*, - entities::prelude::*, - http_send::HttpSender, -}; - -use failure::Fallible; -use native_tls::TlsConnector; -use std::io::{Read, Write}; -use std::net::TcpStream; -use websocket::{OwnedMessage, ClientBuilder}; + +use websocket::{OwnedMessage}; +use std::thread; mod bootstrap; mod store; @@ -27,13 +17,18 @@ mod ele; use crate::bootstrap::handle_cli_args_and_load_config; use crate::store::Store; +use crate::ele::{EleSession, EleStreamSocket}; +use std::time::Duration; #[derive(SmartDefault,Serialize,Deserialize,Debug)] #[serde(default)] pub struct Config { + /// Logging level to use (can be increased using -v flags) #[default="info"] pub logging: String, + /// Instance domain name, e.g. example.com pub instance: String, + /// File to use for session storage #[default="manabu_store.json"] pub store: String, } @@ -45,6 +40,7 @@ fn main() { let mut store = Store::from_file(&config.store); store.set_autosave(true); + store.set_pretty_print(true); let registered = ele::register(&mut store, &config); @@ -52,25 +48,43 @@ fn main() { debug!("Listening to events"); - let mut socket = ele::open_stream_websocket(&session, "user"); + let m_session = session.clone(); + let handle = thread::spawn(move || handle_stream(m_session)); + + let _ = handle.join(); +} + +fn handle_stream(session : EleSession) -> ! { + loop { + debug!("Trying to open websocket"); + + match ele::open_stream_websocket(&session, "user") { + Ok(socket) => process_incoming_events(socket), + Err(e) => error!("Error opening socket: {}", e) + } + + debug!("Delay before reconnecting..."); + thread::sleep(Duration::from_secs(10)); + } +} - for m in socket.incoming_messages() { +fn process_incoming_events(mut socket : EleStreamSocket) { + 'listen: for m in socket.incoming_messages() { match m { Ok(OwnedMessage::Text(text)) => { debug!("Got msg: {}", text); }, - Ok(OwnedMessage::Close(text)) => { + Ok(OwnedMessage::Close(_text)) => { debug!("Close"); - break; + break 'listen; }, Ok(any) => { debug!("Unhandled msg: {:?}", any); } Err(e) => { - error!("{}", e); - break; + error!("Error reading from socket: {}", e); + break 'listen; }, } } - info!("Exit."); } diff --git a/src/store.rs b/src/store.rs index 068e4c7..440770b 100644 --- a/src/store.rs +++ b/src/store.rs @@ -1,3 +1,7 @@ +#![allow(unused)] + +//! JSON store + use std::borrow::Cow; use serde::{Serialize, Deserialize}; use serde::de::DeserializeOwned; @@ -18,6 +22,7 @@ use std::fmt::{self, Display, Formatter}; pub struct Store { path: Option, autosave: bool, + prettysave: bool, items: Map, } @@ -26,6 +31,7 @@ impl Default for Store { Self { path: None, autosave: false, + prettysave: false, items: Map::new() } } @@ -82,6 +88,7 @@ impl Store { let mut store = Store { path: Some(path.as_ref().into()), autosave: false, + prettysave: false, items: Map::new(), }; @@ -92,6 +99,11 @@ impl Store { store } + /// Set pretty print option to use when saved + pub fn set_pretty_print(&mut self, pretty : bool) { + self.prettysave = pretty; + } + /// Set auto-save option - save on each mutation. pub fn set_autosave(&mut self, autosave: bool) { self.autosave = autosave; @@ -142,7 +154,11 @@ impl Store { /// Save the map to a custom file path. pub fn save_to>(&self, path: P) -> Result<()> { - let as_str = serde_json::to_string(&self.items).unwrap(); + let as_str = (if self.prettysave { + serde_json::to_string_pretty(&self.items) + } else { + serde_json::to_string(&self.items) + }).unwrap(); let mut file = File::create(path)?; file.write(as_str.as_bytes())?;