use crate::client::{HttpMethod, Rerout};
use crate::error::Result;
use crate::models::{CreateWebhookInput, CreatedWebhook, DeleteWebhookResult, ListWebhooksResult};
#[derive(Debug, Clone)]
pub struct Webhooks<'a> {
client: &'a Rerout,
}
impl<'a> Webhooks<'a> {
pub(crate) fn new(client: &'a Rerout) -> Self {
Self { client }
}
pub async fn create(&self, input: &CreateWebhookInput) -> Result<CreatedWebhook> {
self.client
.request_json::<CreatedWebhook, _>(
HttpMethod::Post,
"/v1/projects/me/webhooks",
None,
Some(input),
)
.await
}
pub async fn list(&self) -> Result<ListWebhooksResult> {
self.client
.request_json::<ListWebhooksResult, ()>(
HttpMethod::Get,
"/v1/projects/me/webhooks",
None,
None,
)
.await
}
pub async fn delete(&self, endpoint_id: &str) -> Result<DeleteWebhookResult> {
let path = format!("/v1/projects/me/webhooks/{}", percent_encode_segment(endpoint_id));
self.client
.request_json::<DeleteWebhookResult, ()>(HttpMethod::Delete, &path, None, None)
.await
}
}
fn percent_encode_segment(segment: &str) -> String {
let mut out = String::with_capacity(segment.len());
for byte in segment.bytes() {
if is_path_unreserved(byte) {
out.push(byte as char);
} else {
out.push('%');
out.push(hex_upper(byte >> 4));
out.push(hex_upper(byte & 0x0f));
}
}
out
}
#[inline]
fn is_path_unreserved(byte: u8) -> bool {
matches!(byte, b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~')
}
#[inline]
fn hex_upper(nibble: u8) -> char {
match nibble {
0..=9 => (b'0' + nibble) as char,
10..=15 => (b'A' + (nibble - 10)) as char,
_ => unreachable!("nibble out of range"),
}
}
#[cfg(test)]
mod tests {
use super::percent_encode_segment;
#[test]
fn encodes_plain_id_unchanged() {
assert_eq!(percent_encode_segment("wh_abc123"), "wh_abc123");
}
#[test]
fn encodes_slash() {
assert_eq!(percent_encode_segment("wh_a/b"), "wh_a%2Fb");
}
#[test]
fn encodes_spaces() {
assert_eq!(percent_encode_segment("wh a"), "wh%20a");
}
}