Skip to content

Commit

Permalink
Add async feature and rework Dispatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
yukibtc committed Jan 6, 2025
1 parent ccc7465 commit 2050a07
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 186 deletions.
65 changes: 0 additions & 65 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ readme = "README.md"
keywords = ["ntfy", "notifications", "sdk"]

[features]
default = ["dep:reqwest"]
default = ["async"]
async = ["dep:reqwest"]
blocking = ["dep:ureq"]

[dependencies]
Expand All @@ -23,14 +24,15 @@ ureq = { version = "2.12", features = ["json", "socks-proxy"], optional = true }
url = { version = "2", features = ["serde"] }

[dev-dependencies]
tokio = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

[[example]]
name = "blocking"
required-features = ["blocking"]

[[example]]
name = "client"
required-features = ["async"]

[profile.release]
lto = true
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use ntfy::prelude::*;
#[tokio::main]
async fn main() -> Result<(), NtfyError> {
let dispatcher = Dispatcher::builder("https://ntfy.sh")
let dispatcher = DispatcherBuilder::new("https://ntfy.sh")
.credentials(Auth::credentials("username", "password")) // Add optional credentials
.proxy("socks5h://127.0.0.1:9050") // Add optional proxy
.build()?; // Build dispatcher
.build_async()?; // Build dispatcher
let action = Action::new(
ActionType::Http,
Expand Down
4 changes: 2 additions & 2 deletions examples/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
use ntfy::prelude::*;

fn main() -> Result<(), NtfyError> {
let dispatcher = Dispatcher::builder("https://ntfy.sh")
let dispatcher = DispatcherBuilder::new("https://ntfy.sh")
.credentials(Auth::credentials("username", "password")) // Add optional credentials
.proxy("socks5://127.0.0.1:9050") // Add optional proxy
.build()?; // Build dispatcher
.build_blocking()?; // Build dispatcher

let action = Action::new(
ActionType::Http,
Expand Down
4 changes: 2 additions & 2 deletions examples/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use ntfy::prelude::*;

#[tokio::main]
async fn main() -> Result<(), NtfyError> {
let dispatcher = Dispatcher::builder("https://ntfy.sh")
let dispatcher = DispatcherBuilder::new("https://ntfy.sh")
.credentials(Auth::credentials("username", "password")) // Add optional credentials
.proxy("socks5://127.0.0.1:9050") // Add optional proxy
.build()?; // Build dispatcher
.build_async()?; // Build dispatcher

let action = Action::new(
ActionType::Http,
Expand Down
9 changes: 6 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

check:
cargo fmt --all -- --config format_code_in_doc_comments=true
cargo check
cargo test
cargo clippy -- -D warnings
cargo check --no-default-features --features async
cargo check --no-default-features --features blocking
cargo test --no-default-features --features async
cargo test --no-default-features --features blocking
cargo clippy --no-default-features --features async -- -D warnings
cargo clippy --no-default-features --features blocking -- -D warnings
49 changes: 49 additions & 0 deletions src/dispatcher/async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

use reqwest::{Client, Response};
use url::Url;

use crate::{NtfyError, Payload};

/// Async dispatcher
#[derive(Debug, Clone)]
pub struct Async {
client: Client,
}

impl Async {
#[inline]
pub(crate) fn new(client: Client) -> Self {
Self { client }
}

/// Send payload to ntfy server
pub(crate) async fn send(&self, url: &Url, payload: &Payload) -> Result<(), NtfyError> {
// Build request
let mut builder = self.client.post(url.as_str());

// If markdown, set headers
if payload.markdown {
builder = builder.header("Markdown", "yes");
}

// Add payload
builder = builder.json(payload);

// Send request
let res: Response = builder.send().await?;
let res: Response = res.error_for_status()?;

// Get full response text
let text: String = res.text().await?;

if text.is_empty() {
return Err(NtfyError::EmptyResponse);
}

// TODO: check the text?

Ok(())
}
}
44 changes: 44 additions & 0 deletions src/dispatcher/blocking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

use ureq::{Agent, Response};
use url::Url;

use crate::{NtfyError, Payload};

/// Blocking dispatcher
#[derive(Debug, Clone)]
pub struct Blocking {
client: Agent,
}

impl Blocking {
#[inline]
pub(crate) fn new(client: Agent) -> Self {
Self { client }
}

pub(crate) fn send(&self, url: &Url, payload: &Payload) -> Result<(), NtfyError> {
// Build request
let mut builder = self.client.post(url.as_str());

// If markdown, set headers
if payload.markdown {
builder = builder.set("Markdown", "yes");
}

// Send request
let res: Response = builder.send_json(payload)?;

// Get full response text
let text: String = res.into_string()?;

if text.is_empty() {
return Err(NtfyError::EmptyResponse);
}

// TODO: check the text?

Ok(())
}
}
53 changes: 32 additions & 21 deletions src/dispatcher/builder.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
// Copyright (c) 2022 Yuki Kishimoto
// Distributed under the MIT software license

use std::str::FromStr;

#[cfg(not(feature = "blocking"))]
use reqwest::header::{HeaderMap, HeaderValue};
#[cfg(not(feature = "blocking"))]
use reqwest::ClientBuilder;
#[cfg(not(feature = "blocking"))]
use reqwest::Proxy;

#[cfg(any(feature = "async", feature = "blocking"))]
use url::Url;

use super::{Auth, Dispatcher};
use crate::error::NtfyError;
#[cfg(feature = "async")]
use super::Async;
use super::Auth;
#[cfg(feature = "blocking")]
use super::Blocking;
#[cfg(any(feature = "async", feature = "blocking"))]
use super::{Dispatcher, NtfyError};

#[derive(Debug, Clone)]
pub struct DispatcherBuilder {
Expand All @@ -23,6 +20,7 @@ pub struct DispatcherBuilder {
}

impl DispatcherBuilder {
#[inline]
pub fn new<S>(url: S) -> Self
where
S: Into<String>,
Expand All @@ -40,6 +38,7 @@ impl DispatcherBuilder {
self
}

#[inline]
pub fn proxy<S>(mut self, proxy: S) -> Self
where
S: Into<String>,
Expand All @@ -48,8 +47,20 @@ impl DispatcherBuilder {
self
}

#[cfg(not(feature = "blocking"))]
pub fn build(self) -> Result<Dispatcher, NtfyError> {
#[cfg(feature = "async")]
#[deprecated(
since = "0.7.0",
note = "Please use `build_async` or `build_blocking` instead"
)]
pub fn build(self) -> Result<Dispatcher<Async>, NtfyError> {
self.build_async()
}

#[cfg(feature = "async")]
pub fn build_async(self) -> Result<Dispatcher<Async>, NtfyError> {
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{ClientBuilder, Proxy};

let mut client = ClientBuilder::new();

if let Some(auth) = self.auth {
Expand All @@ -65,22 +76,22 @@ impl DispatcherBuilder {
}

Ok(Dispatcher {
url: Url::from_str(&self.url)?,
client: client.build()?,
url: Url::parse(&self.url)?,
inner: Async::new(client.build()?),
})
}

#[cfg(feature = "blocking")]
pub fn build(self) -> Result<Dispatcher, NtfyError> {
pub fn build_blocking(self) -> Result<Dispatcher<Blocking>, NtfyError> {
use ureq::{Error, MiddlewareNext, Request, Response};

let mut agent = ureq::builder();
let mut client = ureq::builder();

if let Some(auth) = self.auth {
let heaver_value = auth.to_header_value();

// Set the authorization headers of every request using a middleware function
agent = agent.middleware(
client = client.middleware(
move |req: Request, next: MiddlewareNext| -> Result<Response, Error> {
next.handle(req.set("Authorization", &heaver_value))
},
Expand All @@ -89,12 +100,12 @@ impl DispatcherBuilder {

if let Some(proxy) = self.proxy {
let proxy = ureq::Proxy::new(proxy)?;
agent = agent.proxy(proxy);
client = client.proxy(proxy);
}

Ok(Dispatcher {
url: Url::from_str(&self.url)?,
agent: agent.build(),
url: Url::parse(&self.url)?,
inner: Blocking::new(client.build()),
})
}
}
Loading

0 comments on commit 2050a07

Please sign in to comment.