forked from MightyPork/elefren-fork
improvements in streaming, add debug format structs
This commit is contained in:
+1
-1
@@ -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 )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -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();
|
||||||
|
|||||||
+19
-19
@@ -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(),
|
||||||
|
|||||||
+2
-2
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -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(),
|
||||||
|
|||||||
+19
-14
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -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
-4
@@ -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(),
|
||||||
|
|||||||
+28
-8
@@ -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::Pending
|
||||||
}
|
}
|
||||||
|
Poll::Ready(Some(Ok(Message::Binary(_)))) => {
|
||||||
|
warn!("Unexpected binary msg");
|
||||||
|
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 {
|
||||||
|
|||||||
+21
-13
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user