improvements in streaming, add debug format structs

master
Ondřej Hruška 3 years ago
parent 568b1ff07a
commit 638594b11b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      src/data.rs
  2. 59
      src/debug.rs
  3. 4
      src/helpers/cli.rs
  4. 38
      src/helpers/json.rs
  5. 4
      src/helpers/mod.rs
  6. 38
      src/helpers/toml.rs
  7. 33
      src/lib.rs
  8. 8
      src/page.rs
  9. 8
      src/registration.rs
  10. 12
      src/requests/push.rs
  11. 4
      src/requests/update_credentials.rs
  12. 36
      src/streaming.rs
  13. 34
      src/unauth.rs

@ -4,7 +4,7 @@ use std::borrow::Cow;
/// Raw data about mastodon app. Save `Data` using `serde` to prevent needing /// Raw data about mastodon app. Save `Data` using `serde` to prevent needing
/// to authenticate on every run. /// to authenticate on every run.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct ClientData { pub struct AppData {
/// Base url of instance eg. `https://mastodon.social`. /// Base url of instance eg. `https://mastodon.social`.
pub base: Cow<'static, str>, pub base: Cow<'static, str>,
/// The client's id given by the instance. /// The client's id given by the instance.

@ -0,0 +1,59 @@
use crate::entities::notification::{Notification, NotificationType};
use std::fmt::{Display, Formatter};
use crate::entities::event::Event;
pub struct NotificationDisplay<'a>(pub &'a Notification);
impl<'a> Display for NotificationDisplay<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let n = self.0;
match n.notification_type {
NotificationType::Follow => {
write!(f, "Follow {{ #{}, @{} }}", n.id, n.account.acct )
}
NotificationType::Favourite => {
if let Some(ref s) = n.status {
write!(f, "Favourite {{ #{}, acct: @{}, status: «{}» }}", n.id, n.account.acct, s.content )
} else {
write!(f, "Favourite {{ #{}, acct: @{}, status: -- }}", n.id, n.account.acct )
}
}
NotificationType::Mention => {
if let Some(ref s) = n.status {
write!(f, "Mention {{ #{}, acct: @{}, status: «{}» }}", n.id, n.account.acct, s.content )
} else {
write!(f, "Mention {{ #{}, acct: @{}, status: -- }}", n.id, n.account.acct )
}
}
NotificationType::Reblog => {
if let Some(ref s) = n.status {
write!(f, "Reblog {{ #{}, acct: @{}, status: «{}» }}", n.id, n.account.acct, s.content )
} else {
write!(f, "Reblog {{ #{}, acct: @{}, status: -- }}", n.id, n.account.acct )
}
}
}
}
}
pub struct EventDisplay<'a>(pub &'a Event);
impl<'a> Display for EventDisplay<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let n = self.0;
match n {
Event::Notification(n) => {
NotificationDisplay(n).fmt(f)
}
Event::Delete(id) => {
write!(f, "Delete {{ #{} }}", id)
}
Event::FiltersChanged => {
write!(f, "FiltersChanged")
}
Event::Update(s) => {
write!(f, "Status {{ #{}, acct: @{}, status: «{}», vis: {:?} }}", s.id, s.account.acct, s.content, s.visibility )
}
}
}
}

@ -1,10 +1,10 @@
use std::io::{self, BufRead, Write}; use std::io::{self, BufRead, Write};
use crate::{errors::Result, registration::Registered, Client}; use crate::{errors::Result, registration::Registered, FediClient};
/// Finishes the authentication process for the given `Registered` object, /// Finishes the authentication process for the given `Registered` object,
/// using the command-line /// using the command-line
pub async fn authenticate(registration: Registered) -> Result<Client> { pub async fn authenticate(registration: Registered) -> Result<FediClient> {
let url = registration.authorize_url()?; let url = registration.authorize_url()?;
let stdout = io::stdout(); let stdout = io::stdout();

@ -4,46 +4,46 @@ use std::{
path::Path, path::Path,
}; };
use crate::{data::ClientData, Result}; use crate::{data::AppData, Result};
/// Attempts to deserialize a Data struct from a string /// Attempts to deserialize a Data struct from a string
pub fn from_str(s: &str) -> Result<ClientData> { pub fn from_str(s: &str) -> Result<AppData> {
Ok(serde_json::from_str(s)?) Ok(serde_json::from_str(s)?)
} }
/// Attempts to deserialize a Data struct from a slice of bytes /// Attempts to deserialize a Data struct from a slice of bytes
pub fn from_slice(s: &[u8]) -> Result<ClientData> { pub fn from_slice(s: &[u8]) -> Result<AppData> {
Ok(serde_json::from_slice(s)?) Ok(serde_json::from_slice(s)?)
} }
/// Attempts to deserialize a Data struct from something that implements /// Attempts to deserialize a Data struct from something that implements
/// the std::io::Read trait /// the std::io::Read trait
pub fn from_reader<R: Read>(mut r: R) -> Result<ClientData> { pub fn from_reader<R: Read>(mut r: R) -> Result<AppData> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
r.read_to_end(&mut buffer)?; r.read_to_end(&mut buffer)?;
from_slice(&buffer) from_slice(&buffer)
} }
/// Attempts to deserialize a Data struct from a file /// Attempts to deserialize a Data struct from a file
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ClientData> { pub fn from_file<P: AsRef<Path>>(path: P) -> Result<AppData> {
let path = path.as_ref(); let path = path.as_ref();
let file = File::open(path)?; let file = File::open(path)?;
Ok(from_reader(file)?) Ok(from_reader(file)?)
} }
/// Attempts to serialize a Data struct to a String /// Attempts to serialize a Data struct to a String
pub fn to_string(data: &ClientData) -> Result<String> { pub fn to_string(data: &AppData) -> Result<String> {
Ok(serde_json::to_string_pretty(data)?) Ok(serde_json::to_string_pretty(data)?)
} }
/// Attempts to serialize a Data struct to a Vec of bytes /// Attempts to serialize a Data struct to a Vec of bytes
pub fn to_vec(data: &ClientData) -> Result<Vec<u8>> { pub fn to_vec(data: &AppData) -> Result<Vec<u8>> {
Ok(serde_json::to_vec(data)?) Ok(serde_json::to_vec(data)?)
} }
/// Attempts to serialize a Data struct to something that implements the /// Attempts to serialize a Data struct to something that implements the
/// std::io::Write trait /// std::io::Write trait
pub fn to_writer<W: Write>(data: &ClientData, writer: W) -> Result<()> { pub fn to_writer<W: Write>(data: &AppData, writer: W) -> Result<()> {
let mut buf_writer = BufWriter::new(writer); let mut buf_writer = BufWriter::new(writer);
let vec = to_vec(data)?; let vec = to_vec(data)?;
buf_writer.write_all(&vec)?; buf_writer.write_all(&vec)?;
@ -55,7 +55,7 @@ pub fn to_writer<W: Write>(data: &ClientData, writer: W) -> Result<()> {
/// When opening the file, this will set the `.write(true)` and /// When opening the file, this will set the `.write(true)` and
/// `.truncate(true)` options, use the next method for more /// `.truncate(true)` options, use the next method for more
/// fine-grained control /// fine-grained control
pub fn to_file<P: AsRef<Path>>(data: &ClientData, path: P) -> Result<()> { pub fn to_file<P: AsRef<Path>>(data: &AppData, path: P) -> Result<()> {
let mut options = OpenOptions::new(); let mut options = OpenOptions::new();
options.create(true).write(true).truncate(true); options.create(true).write(true).truncate(true);
to_file_with_options(data, path, options)?; to_file_with_options(data, path, options)?;
@ -63,7 +63,7 @@ pub fn to_file<P: AsRef<Path>>(data: &ClientData, path: P) -> Result<()> {
} }
/// Attempts to serialize a Data struct to a file /// Attempts to serialize a Data struct to a file
pub fn to_file_with_options<P: AsRef<Path>>(data: &ClientData, path: P, options: OpenOptions) -> Result<()> { pub fn to_file_with_options<P: AsRef<Path>>(data: &AppData, path: P, options: OpenOptions) -> Result<()> {
let path = path.as_ref(); let path = path.as_ref();
let file = options.open(path)?; let file = options.open(path)?;
to_writer(data, file)?; to_writer(data, file)?;
@ -93,7 +93,7 @@ mod tests {
let desered = from_str(DOC).expect("Couldn't deserialize Data"); let desered = from_str(DOC).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -108,7 +108,7 @@ mod tests {
let desered = from_slice(&doc).expect("Couldn't deserialize Data"); let desered = from_slice(&doc).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -124,7 +124,7 @@ mod tests {
let desered = from_reader(doc).expect("Couldn't deserialize Data"); let desered = from_reader(doc).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -140,7 +140,7 @@ mod tests {
let desered = from_file(datafile.path()).expect("Couldn't deserialize Data"); let desered = from_file(datafile.path()).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -151,7 +151,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_string() { fn test_to_string() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -164,7 +164,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_vec() { fn test_to_vec() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -177,7 +177,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_writer() { fn test_to_writer() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -192,7 +192,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_file() { fn test_to_file() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -207,7 +207,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_file_with_options() { fn test_to_file_with_options() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),

@ -31,13 +31,13 @@ pub async fn deserialise_response<T: for<'de> serde::Deserialize<'de>>(response:
let bytes = response.bytes().await?; let bytes = response.bytes().await?;
match serde_json::from_slice(&bytes) { match serde_json::from_slice(&bytes) {
Ok(t) => { Ok(t) => {
debug!("{}", String::from_utf8_lossy(&bytes)); trace!("Resp: {}", String::from_utf8_lossy(&bytes));
Ok(t) Ok(t)
} }
// If deserializing into the desired type fails try again to // If deserializing into the desired type fails try again to
// see if this is an error response. // see if this is an error response.
Err(e) => { Err(e) => {
error!("{}", String::from_utf8_lossy(&bytes)); error!("Error resp: {}", String::from_utf8_lossy(&bytes));
if let Ok(error) = serde_json::from_slice(&bytes) { if let Ok(error) = serde_json::from_slice(&bytes) {
return Err(crate::Error::Api(error)); return Err(crate::Error::Api(error));
} }

@ -1,47 +1,47 @@
use crate::data::ClientData; use crate::data::AppData;
use crate::Result; use crate::Result;
use std::io::{Read, Write, BufWriter}; use std::io::{Read, Write, BufWriter};
use std::path::Path; use std::path::Path;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
/// Attempts to deserialize a Data struct from a string /// Attempts to deserialize a Data struct from a string
pub fn from_str(s: &str) -> Result<ClientData> { pub fn from_str(s: &str) -> Result<AppData> {
Ok(toml::from_str(s)?) Ok(toml::from_str(s)?)
} }
/// Attempts to deserialize a Data struct from a slice of bytes /// Attempts to deserialize a Data struct from a slice of bytes
pub fn from_slice(s: &[u8]) -> Result<ClientData> { pub fn from_slice(s: &[u8]) -> Result<AppData> {
Ok(toml::from_slice(s)?) Ok(toml::from_slice(s)?)
} }
/// Attempts to deserialize a Data struct from something that implements /// Attempts to deserialize a Data struct from something that implements
/// the std::io::Read trait /// the std::io::Read trait
pub fn from_reader<R: Read>(mut r: R) -> Result<ClientData> { pub fn from_reader<R: Read>(mut r: R) -> Result<AppData> {
let mut buffer = Vec::new(); let mut buffer = Vec::new();
r.read_to_end(&mut buffer)?; r.read_to_end(&mut buffer)?;
from_slice(&buffer) from_slice(&buffer)
} }
/// Attempts to deserialize a Data struct from a file /// Attempts to deserialize a Data struct from a file
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ClientData> { pub fn from_file<P: AsRef<Path>>(path: P) -> Result<AppData> {
let path = path.as_ref(); let path = path.as_ref();
let file = File::open(path)?; let file = File::open(path)?;
Ok(from_reader(file)?) Ok(from_reader(file)?)
} }
/// Attempts to serialize a Data struct to a String /// Attempts to serialize a Data struct to a String
pub fn to_string(data: &ClientData) -> Result<String> { pub fn to_string(data: &AppData) -> Result<String> {
Ok(toml::to_string_pretty(data)?) Ok(toml::to_string_pretty(data)?)
} }
/// Attempts to serialize a Data struct to a Vec of bytes /// Attempts to serialize a Data struct to a Vec of bytes
pub fn to_vec(data: &ClientData) -> Result<Vec<u8>> { pub fn to_vec(data: &AppData) -> Result<Vec<u8>> {
Ok(toml::to_vec(data)?) Ok(toml::to_vec(data)?)
} }
/// Attempts to serialize a Data struct to something that implements the /// Attempts to serialize a Data struct to something that implements the
/// std::io::Write trait /// std::io::Write trait
pub fn to_writer<W: Write>(data: &ClientData, writer: W) -> Result<()> { pub fn to_writer<W: Write>(data: &AppData, writer: W) -> Result<()> {
let mut buf_writer = BufWriter::new(writer); let mut buf_writer = BufWriter::new(writer);
let vec = to_vec(data)?; let vec = to_vec(data)?;
buf_writer.write_all(&vec)?; buf_writer.write_all(&vec)?;
@ -53,7 +53,7 @@ pub fn to_writer<W: Write>(data: &ClientData, writer: W) -> Result<()> {
/// When opening the file, this will set the `.write(true)` and /// When opening the file, this will set the `.write(true)` and
/// `.truncate(true)` options, use the next method for more /// `.truncate(true)` options, use the next method for more
/// fine-grained control /// fine-grained control
pub fn to_file<P: AsRef<Path>>(data: &ClientData, path: P) -> Result<()> { pub fn to_file<P: AsRef<Path>>(data: &AppData, path: P) -> Result<()> {
let mut options = OpenOptions::new(); let mut options = OpenOptions::new();
options.create(true).write(true).truncate(true); options.create(true).write(true).truncate(true);
to_file_with_options(data, path, options)?; to_file_with_options(data, path, options)?;
@ -61,7 +61,7 @@ pub fn to_file<P: AsRef<Path>>(data: &ClientData, path: P) -> Result<()> {
} }
/// Attempts to serialize a Data struct to a file /// Attempts to serialize a Data struct to a file
pub fn to_file_with_options<P: AsRef<Path>>(data: &ClientData, path: P, options: OpenOptions) -> Result<()> { pub fn to_file_with_options<P: AsRef<Path>>(data: &AppData, path: P, options: OpenOptions) -> Result<()> {
let path = path.as_ref(); let path = path.as_ref();
let file = options.open(path)?; let file = options.open(path)?;
to_writer(data, file)?; to_writer(data, file)?;
@ -89,7 +89,7 @@ mod tests {
let desered = from_str(DOC).expect("Couldn't deserialize Data"); let desered = from_str(DOC).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -104,7 +104,7 @@ mod tests {
let desered = from_slice(&doc).expect("Couldn't deserialize Data"); let desered = from_slice(&doc).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -120,7 +120,7 @@ mod tests {
let desered = from_reader(doc).expect("Couldn't deserialize Data"); let desered = from_reader(doc).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -136,7 +136,7 @@ mod tests {
let desered = from_file(datafile.path()).expect("Couldn't deserialize Data"); let desered = from_file(datafile.path()).expect("Couldn't deserialize Data");
assert_eq!( assert_eq!(
desered, desered,
ClientData { AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -147,7 +147,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_string() { fn test_to_string() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -160,7 +160,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_vec() { fn test_to_vec() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -173,7 +173,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_writer() { fn test_to_writer() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -188,7 +188,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_file() { fn test_to_file() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),
@ -203,7 +203,7 @@ mod tests {
} }
#[test] #[test]
fn test_to_file_with_options() { fn test_to_file_with_options() {
let data = ClientData { let data = AppData {
base: "https://example.com".into(), base: "https://example.com".into(),
client_id: "adbc01234".into(), client_id: "adbc01234".into(),
client_secret: "0987dcba".into(), client_secret: "0987dcba".into(),

@ -3,6 +3,8 @@
//! Most of the api is documented on [Mastodon's website](https://docs.joinmastodon.org/client/intro/) //! Most of the api is documented on [Mastodon's website](https://docs.joinmastodon.org/client/intro/)
//! //!
#![deny(unused_must_use)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use] #[macro_use]
@ -28,7 +30,7 @@ use streaming::EventReader;
pub use streaming::StreamKind; pub use streaming::StreamKind;
pub use crate::{ pub use crate::{
data::ClientData, data::AppData,
media_builder::MediaBuilder, media_builder::MediaBuilder,
page::Page, page::Page,
registration::Registration, registration::Registration,
@ -66,24 +68,26 @@ pub mod unauth;
/// Streaming API /// Streaming API
pub mod streaming; pub mod streaming;
pub mod debug;
/// Your mastodon application client, handles all requests to and from Mastodon. /// Your mastodon application client, handles all requests to and from Mastodon.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Client { pub struct FediClient {
http_client: reqwest::Client, http_client: reqwest::Client,
/// Raw data about your mastodon instance. /// Raw data about your mastodon instance.
pub data: ClientData, pub data: AppData,
} }
impl From<ClientData> for Client { impl From<AppData> for FediClient {
/// Creates a mastodon instance from the data struct. /// Creates a mastodon instance from the data struct.
fn from(data: ClientData) -> Client { fn from(data: AppData) -> FediClient {
let mut builder = ClientBuilder::new(); let mut builder = ClientBuilder::new();
builder.data(data); builder.data(data);
builder.build().expect("We know `data` is present, so this should be fine") builder.build().expect("We know `data` is present, so this should be fine")
} }
} }
impl Client { impl FediClient {
methods![get, put, post, delete,]; methods![get, put, post, delete,];
pub fn route(&self, url: &str) -> String { pub fn route(&self, url: &str) -> String {
@ -99,12 +103,13 @@ impl Client {
/// Open streaming API of the given kind /// Open streaming API of the given kind
pub async fn open_streaming_api<'k>(&self, kind: StreamKind<'k>) -> Result<EventReader> { pub async fn open_streaming_api<'k>(&self, kind: StreamKind<'k>) -> Result<EventReader> {
let mut url: url::Url = self.route(&format!("/api/v1/streaming/{}", kind.get_url_fragment())).parse()?; let mut url: url::Url = self.route(&"/api/v1/streaming").parse()?;
// let mut url: url::Url = self.route(&format!("/api/v1/streaming/{}", kind.get_url_fragment())).parse()?;
{ {
let mut qpm = url.query_pairs_mut(); let mut qpm = url.query_pairs_mut();
qpm.append_pair("access_token", &self.token); qpm.append_pair("access_token", &self.token);
//qpm.append_pair("stream", "user"); qpm.append_pair("stream", kind.get_stream_name());
for (k, v) in kind.get_query_params() { for (k, v) in kind.get_query_params() {
qpm.append_pair(k, v); qpm.append_pair(k, v);
@ -390,8 +395,8 @@ impl Client {
} }
} }
impl ops::Deref for Client { impl ops::Deref for FediClient {
type Target = ClientData; type Target = AppData;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.data &self.data
@ -400,7 +405,7 @@ impl ops::Deref for Client {
struct ClientBuilder { struct ClientBuilder {
http_client: Option<reqwest::Client>, http_client: Option<reqwest::Client>,
data: Option<ClientData>, data: Option<AppData>,
} }
impl ClientBuilder { impl ClientBuilder {
@ -416,14 +421,14 @@ impl ClientBuilder {
self self
} }
pub fn data(&mut self, data: ClientData) -> &mut Self { pub fn data(&mut self, data: AppData) -> &mut Self {
self.data = Some(data); self.data = Some(data);
self self
} }
pub fn build(self) -> Result<Client> { pub fn build(self) -> Result<FediClient> {
Ok(if let Some(data) = self.data { Ok(if let Some(data) = self.data {
Client { FediClient {
http_client: self.http_client.unwrap_or_else(reqwest::Client::new), http_client: self.http_client.unwrap_or_else(reqwest::Client::new),
data, data,
} }

@ -1,4 +1,4 @@
use super::{deserialise_response, Client, Result}; use super::{deserialise_response, FediClient, Result};
use crate::entities::itemsiter::ItemsIter; use crate::entities::itemsiter::ItemsIter;
use hyper_old_types::header::{parsing, Link, RelationType}; use hyper_old_types::header::{parsing, Link, RelationType};
use reqwest::{Response, header::LINK}; use reqwest::{Response, header::LINK};
@ -35,7 +35,7 @@ macro_rules! pages {
/// easily stored for later use /// easily stored for later use
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OwnedPage<T: for<'de> Deserialize<'de>> { pub struct OwnedPage<T: for<'de> Deserialize<'de>> {
api_client: Client, api_client: FediClient,
next: Option<Url>, next: Option<Url>,
prev: Option<Url>, prev: Option<Url>,
/// Initial set of items /// Initial set of items
@ -63,7 +63,7 @@ impl<'a, T: for<'de> Deserialize<'de>> From<Page<'a, T>> for OwnedPage<T> {
/// Represents a single page of API results /// Represents a single page of API results
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Page<'a, T: for<'de> Deserialize<'de>> { pub struct Page<'a, T: for<'de> Deserialize<'de>> {
api_client: &'a Client, api_client: &'a FediClient,
next: Option<Url>, next: Option<Url>,
prev: Option<Url>, prev: Option<Url>,
/// Initial set of items /// Initial set of items
@ -76,7 +76,7 @@ impl<'a, T: for<'de> Deserialize<'de>> Page<'a, T> {
prev: prev_page prev: prev_page
} }
pub(crate) async fn new<'m>(api_client: &'m Client, response: Response) -> Result<Page<'m, T>> { pub(crate) async fn new<'m>(api_client: &'m FediClient, response: Response) -> Result<Page<'m, T>> {
let (prev, next) = get_links(&response)?; let (prev, next) = get_links(&response)?;
Ok(Page { Ok(Page {
initial_items: deserialise_response(response).await?, initial_items: deserialise_response(response).await?,

@ -4,8 +4,8 @@ use serde::Deserialize;
use std::convert::TryInto; use std::convert::TryInto;
use crate::apps::{AppBuilder, App}; use crate::apps::{AppBuilder, App};
use crate::scopes::Scopes; use crate::scopes::Scopes;
use crate::{Result, Error, Client, ClientBuilder}; use crate::{Result, Error, FediClient, ClientBuilder};
use crate::data::ClientData; use crate::data::AppData;
const DEFAULT_REDIRECT_URI: &str = "urn:ietf:wg:oauth:2.0:oob"; const DEFAULT_REDIRECT_URI: &str = "urn:ietf:wg:oauth:2.0:oob";
@ -191,7 +191,7 @@ impl Registered {
/// Create an access token from the client id, client secret, and code /// Create an access token from the client id, client secret, and code
/// provided by the authorisation url. /// provided by the authorisation url.
pub async fn complete(&self, code: &str) -> Result<Client> { pub async fn complete(&self, code: &str) -> Result<FediClient> {
let url = format!( let url = format!(
"{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&\ "{}/oauth/token?client_id={}&client_secret={}&code={}&grant_type=authorization_code&\
redirect_uri={}", redirect_uri={}",
@ -200,7 +200,7 @@ impl Registered {
let token: AccessToken = self.send(self.http_client.post(&url)).await?.json().await?; let token: AccessToken = self.send(self.http_client.post(&url)).await?.json().await?;
let data = ClientData { let data = AppData {
base: self.base.clone().into(), base: self.base.clone().into(),
client_id: self.client_id.clone().into(), client_id: self.client_id.clone().into(),
client_secret: self.client_secret.clone().into(), client_secret: self.client_secret.clone().into(),

@ -45,9 +45,9 @@ impl Keys {
/// ///
/// ```no_run /// ```no_run
/// # extern crate elefren; /// # extern crate elefren;
/// # use elefren::{MastodonClient, Client, ClientData}; /// # use elefren::{MastodonClient, FediClient, AppData};
/// # fn main() -> Result<(), elefren::Error> { /// # fn main() -> Result<(), elefren::Error> {
/// # let data = ClientData { /// # let data = AppData {
/// # base: "".into(), /// # base: "".into(),
/// # client_id: "".into(), /// # client_id: "".into(),
/// # client_secret: "".into(), /// # client_secret: "".into(),
@ -56,7 +56,7 @@ impl Keys {
/// # }; /// # };
/// use elefren::requests::{AddPushRequest, Keys}; /// use elefren::requests::{AddPushRequest, Keys};
/// ///
/// let client = Client::from(data); /// let client = FediClient::from(data);
/// ///
/// let keys = Keys::new("stahesuahoei293ise===", "tasecoa,nmeozka=="); /// let keys = Keys::new("stahesuahoei293ise===", "tasecoa,nmeozka==");
/// let mut request = AddPushRequest::new("http://example.com/push/endpoint", &keys); /// let mut request = AddPushRequest::new("http://example.com/push/endpoint", &keys);
@ -214,9 +214,9 @@ impl AddPushRequest {
/// ///
/// ```no_run /// ```no_run
/// # extern crate elefren; /// # extern crate elefren;
/// # use elefren::{Client, ClientData}; /// # use elefren::{FediClient, AppData};
/// # fn main() -> Result<(), elefren::Error> { /// # fn main() -> Result<(), elefren::Error> {
/// # let data = ClientData { /// # let data = AppData {
/// # base: "".into(), /// # base: "".into(),
/// # client_id: "".into(), /// # client_id: "".into(),
/// # client_secret: "".into(), /// # client_secret: "".into(),
@ -225,7 +225,7 @@ impl AddPushRequest {
/// # }; /// # };
/// use elefren::requests::UpdatePushRequest; /// use elefren::requests::UpdatePushRequest;
/// ///
/// let client = Client::from(data); /// let client = FediClient::from(data);
/// ///
/// let mut request = UpdatePushRequest::new("foobar"); /// let mut request = UpdatePushRequest::new("foobar");
/// request.follow(true).reblog(true); /// request.follow(true).reblog(true);

@ -15,9 +15,9 @@ use crate::{
/// ///
/// ```no_run /// ```no_run
/// # extern crate elefren; /// # extern crate elefren;
/// # use elefren::ClientData; /// # use elefren::AppData;
/// # fn main() -> Result<(), elefren::Error> { /// # fn main() -> Result<(), elefren::Error> {
/// # let data = ClientData { /// # let data = AppData {
/// # base: "".into(), /// # base: "".into(),
/// # client_id: "".into(), /// # client_id: "".into(),
/// # client_secret: "".into(), /// # client_secret: "".into(),

@ -21,6 +21,19 @@ pub enum StreamKind<'a> {
} }
impl<'a> StreamKind<'a> { impl<'a> StreamKind<'a> {
pub(crate) fn get_stream_name(&self) -> &'static str {
match self {
StreamKind::User => "user",
StreamKind::Public => "public",
StreamKind::PublicLocal => "public:local",
StreamKind::Direct => "direct",
StreamKind::Hashtag(_) => "hashtag",
StreamKind::HashtagLocal(_) => "hashtag:local",
StreamKind::List(_) => "list",
}
}
#[allow(unused)]
pub(crate) fn get_url_fragment(&self) -> &'static str { pub(crate) fn get_url_fragment(&self) -> &'static str {
match self { match self {
StreamKind::User => "user", StreamKind::User => "user",
@ -83,26 +96,33 @@ impl Stream for EventReader {
fn poll_next(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Option<Self::Item>> {
match Pin::new(&mut self.stream).poll_next(cx) { match Pin::new(&mut self.stream).poll_next(cx) {
Poll::Ready(Some(Ok(Message::Text(line)))) => { Poll::Ready(Some(Ok(Message::Text(line)))) => {
debug!("WS rx: {}", line); trace!("WS rx: {}", line);
let line = line.trim().to_string(); let line = line.trim().to_string();
if line.starts_with(':') || line.is_empty() { if line.starts_with(':') || line.is_empty() {
debug!("discard as comment"); trace!("discard as comment");
return Poll::Pending; return Poll::Pending;
} }
self.lines.push(line); self.lines.push(line);
if let Ok(event) = self.make_event(&self.lines) { if let Ok(event) = self.make_event(&self.lines) {
debug!("Parsed event"); trace!("Parsed event");
self.lines.clear(); self.lines.clear();
return Poll::Ready(Some(event)); return Poll::Ready(Some(event));
} else { } else {
debug!("Failed to parse"); trace!("Failed to parse");
return Poll::Pending; return Poll::Pending;
} }
} }
Poll::Ready(Some(Ok(other))) => { Poll::Ready(Some(Ok(Message::Ping(_)))) | Poll::Ready(Some(Ok(Message::Pong(_)))) => {
warn!("Unexpected msg: {:?}", other); // Discard
Poll::Pending
}
Poll::Ready(Some(Ok(Message::Binary(_)))) => {
warn!("Unexpected binary msg");
Poll::Pending Poll::Pending
} }
Poll::Ready(Some(Ok(Message::Close(_)))) => {
Poll::Ready(None)
}
Poll::Ready(Some(Err(error))) => { Poll::Ready(Some(Err(error))) => {
error!("Websocket error: {:?}", error); error!("Websocket error: {:?}", error);
// Close // Close
@ -124,14 +144,14 @@ impl EventReader {
let event; let event;
let data; let data;
if let Some(event_line) = lines.iter().find(|line| line.starts_with("event:")) { if let Some(event_line) = lines.iter().find(|line| line.starts_with("event:")) {
debug!("plaintext formatted event"); trace!("plaintext formatted event");
event = event_line[6..].trim().to_string(); event = event_line[6..].trim().to_string();
data = lines data = lines
.iter() .iter()
.find(|line| line.starts_with("data:")) .find(|line| line.starts_with("data:"))
.map(|x| x[5..].trim().to_string()); .map(|x| x[5..].trim().to_string());
} else { } else {
debug!("JSON formatted event"); trace!("JSON formatted event");
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Message { struct Message {

@ -7,27 +7,27 @@ use crate::streaming::EventReader;
/// Client that can make unauthenticated calls to a mastodon instance /// Client that can make unauthenticated calls to a mastodon instance
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Client { pub struct FediClient {
client: reqwest::Client, http_client: reqwest::Client,
base: url::Url, base: url::Url,
} }
impl Client { impl FediClient {
/// Create a new unauthenticated client /// Create a new unauthenticated client
pub fn new(base: &str) -> Result<Client> { pub fn new(base: &str) -> Result<FediClient> {
let base = if base.starts_with("https://") { let base = if base.starts_with("https://") {
base.to_string() base.to_string()
} else { } else {
format!("https://{}", base) format!("https://{}", base)
}; };
Ok(Client { Ok(FediClient {
client: reqwest::Client::new(), http_client: reqwest::Client::new(),
base: url::Url::parse(&base)?, base: url::Url::parse(&base)?,
}) })
} }
} }
impl Client { impl FediClient {
/// # Low-level API for extending /// # Low-level API for extending
/// Create a route with the given path fragment /// Create a route with the given path fragment
pub fn route(&self, url: &str) -> Result<url::Url> { pub fn route(&self, url: &str) -> Result<url::Url> {
@ -38,20 +38,28 @@ impl Client {
/// Send a request /// Send a request
pub async fn send(&self, req: reqwest::RequestBuilder) -> Result<reqwest::Response> { pub async fn send(&self, req: reqwest::RequestBuilder) -> Result<reqwest::Response> {
let req = req.build()?; let req = req.build()?;
Ok(self.client.execute(req).await?) Ok(self.http_client.execute(req).await?)
} }
// TODO verify if this really works without auth // TODO verify if this really works without auth
/// Get a stream of the public timeline /// Get a stream of the public timeline
pub async fn streaming_public(&self) -> Result<EventReader> { pub async fn streaming_public(&self) -> Result<EventReader> {
let url: url::Url = self.route("/api/v1/streaming/public")?; let mut url: url::Url = self.route("/api/v1/streaming")?;
{
let mut qpm = url.query_pairs_mut();
qpm.append_pair("stream", "public");
}
streaming::do_open_streaming(url.as_str()).await streaming::do_open_streaming(url.as_str()).await
} }
/// Get a stream of the local timeline /// Get a stream of the local timeline
pub async fn streaming_local(&self) -> Result<EventReader> { pub async fn streaming_local(&self) -> Result<EventReader> {
let url: url::Url = self.route("/api/v1/streaming/public/local")?; let mut url: url::Url = self.route("/api/v1/streaming/public")?;
{
let mut qpm = url.query_pairs_mut();
qpm.append_pair("stream", "public:local");
}
streaming::do_open_streaming(url.as_str()).await streaming::do_open_streaming(url.as_str()).await
} }
@ -59,7 +67,7 @@ impl Client {
pub async fn get_status(&self, id: &str) -> Result<Status> { pub async fn get_status(&self, id: &str) -> Result<Status> {
let route = self.route("/api/v1/statuses")?; let route = self.route("/api/v1/statuses")?;
let route = route.join(id)?; let route = route.join(id)?;
let response = self.send(self.client.get(route)).await?; let response = self.send(self.http_client.get(route)).await?;
deserialise_response(response).await deserialise_response(response).await
} }
@ -68,7 +76,7 @@ impl Client {
let route = self.route("/api/v1/statuses")?; let route = self.route("/api/v1/statuses")?;
let route = route.join(id)?; let route = route.join(id)?;
let route = route.join("context")?; let route = route.join("context")?;
let response = self.send(self.client.get(route)).await?; let response = self.send(self.http_client.get(route)).await?;
deserialise_response(response).await deserialise_response(response).await
} }
@ -77,7 +85,7 @@ impl Client {
let route = self.route("/api/v1/statuses")?; let route = self.route("/api/v1/statuses")?;
let route = route.join(id)?; let route = route.join(id)?;
let route = route.join("card")?; let route = route.join("card")?;
let response = self.send(self.client.get(route)).await?; let response = self.send(self.http_client.get(route)).await?;
deserialise_response(response).await deserialise_response(response).await
} }
} }

Loading…
Cancel
Save