Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ impl FromStr for OAuthToken {
}
}

// These trait impls are similar to FromStr (but are infallible)
/// Converts a string slice into an `AppId`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `AppId` always succeeds (it simply wraps the input string without validation).
impl<'a> From<&'a str> for AppId
where
Self: FromStr,
Expand All @@ -87,6 +90,10 @@ where
}
}

/// Converts a string slice into an `AppKey`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `AppKey` always succeeds (it simply wraps the input string without validation).
impl<'a> From<&'a str> for AppKey
where
Self: FromStr,
Expand All @@ -96,6 +103,10 @@ where
}
}

/// Converts a string slice into a `UserKey`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `UserKey` always succeeds (it simply wraps the input string without validation).
impl<'a> From<&'a str> for UserKey
where
Self: FromStr,
Expand All @@ -105,6 +116,10 @@ where
}
}

/// Converts a string slice into an `OAuthToken`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `OAuthToken` always succeeds (it simply wraps the input string without validation).
impl<'a> From<&'a str> for OAuthToken
where
Self: FromStr,
Expand Down
13 changes: 12 additions & 1 deletion src/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ impl FromStr for ServiceToken {
}
}

// These trait impls are similar to FromStr (but are infallible)
/// Converts a string slice into a `ProviderKey`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `ProviderKey` always succeeds (it simply wraps the input string without validation).
impl From<&str> for ProviderKey
where
Self: FromStr,
Expand All @@ -59,6 +62,10 @@ where
}
}

/// Converts a string slice into a `ServiceToken`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `ServiceToken` always succeeds (it simply wraps the input string without validation).
impl From<&str> for ServiceToken
where
Self: FromStr,
Expand Down Expand Up @@ -162,6 +169,10 @@ impl FromStr for ServiceId {
}
}

/// Converts a string slice into a `ServiceId`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `ServiceId` always succeeds (it simply wraps the input string without validation).
impl From<&str> for ServiceId
where
Self: FromStr,
Expand Down
9 changes: 9 additions & 0 deletions src/extensions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
//! Extensions for 3scale API calls.
//!
//! Extensions allow you to request additional data or modify the behavior of API calls
//! to the 3scale Service Management API. Common extensions include:
//! - `list_app_keys`: Request the list of application keys for an app
//! - `hierarchy`: Request the metrics hierarchy in the response
//! - `flat_usage`: Use a flat structure for usage data instead of nested
//! - `no_body`: Request that the response body be omitted

mod extension;
mod list;

Expand Down
23 changes: 23 additions & 0 deletions src/extensions/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

use std::borrow::Cow;

/// Represents an extension parameter for 3scale API calls.
///
/// Extensions modify the behavior or output of API calls. Each variant represents a different
/// type of extension that can be added to an API request.
///
/// # Variants
///
/// - `FlatUsage`: Changes usage reporting format to a flat structure
/// - `Hierarchy`: Requests the metrics hierarchy in the response
/// - `NoBody`: Requests that the response body be omitted
/// - `ListAppKeys`: Requests the list of application keys for an app
/// - `Other`: Any custom extension with arbitrary key-value pairs
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Extension<'s> {
Expand All @@ -13,6 +25,9 @@
}

impl Extension<'_> {
/// Returns the key name for this extension.
///
/// The key is used when serializing the extension as a parameter.
pub fn key(&self) -> &'_ str {
match self {
Extension::Other(k, _) => k,
Expand All @@ -23,13 +38,21 @@
}
}

/// Returns the value for this extension.
///
/// For known extensions, this returns the configured value.
/// For extensions without values, this returns "1".
pub fn value(&self) -> &'_ str {
match self {
Extension::Other(_, v) | Extension::FlatUsage(v) | Extension::ListAppKeys(v) => v,
Extension::Hierarchy | Extension::NoBody => "1",
}
}

/// Returns this extension as a URL-encoded key-value pair.
///
/// For known extensions, the key is not encoded. For custom extensions,
/// both key and value are URL-encoded.
pub fn to_cow(&self) -> Cow<'_, str> {
use crate::encoding::encode;

Expand All @@ -56,11 +79,11 @@
}
}

impl ToString for Extension<'_> {
fn to_string(&self) -> String {
self.to_cow().into()
}
}

Check failure on line 86 in src/extensions/extension.rs

View workflow job for this annotation

GitHub Actions / clippy

direct implementation of `ToString`

error: direct implementation of `ToString` --> src/extensions/extension.rs:82:1 | 82 | / impl ToString for Extension<'_> { 83 | | fn to_string(&self) -> String { 84 | | self.to_cow().into() 85 | | } 86 | | } | |_^ | = help: prefer implementing `Display` instead = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#to_string_trait_impl note: the lint level is defined here --> src/lib.rs:1:9 | 1 | #![deny(clippy::all, clippy::cargo)] | ^^^^^^^^^^^ = note: `#[deny(clippy::to_string_trait_impl)]` implied by `#[deny(clippy::all)]`

Check failure on line 86 in src/extensions/extension.rs

View workflow job for this annotation

GitHub Actions / clippy

direct implementation of `ToString`

error: direct implementation of `ToString` --> src/extensions/extension.rs:82:1 | 82 | / impl ToString for Extension<'_> { 83 | | fn to_string(&self) -> String { 84 | | self.to_cow().into() 85 | | } 86 | | } | |_^ | = help: prefer implementing `Display` instead = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#to_string_trait_impl note: the lint level is defined here --> src/lib.rs:1:9 | 1 | #![deny(clippy::all, clippy::cargo)] | ^^^^^^^^^^^ = note: `#[deny(clippy::to_string_trait_impl)]` implied by `#[deny(clippy::all)]`

#[cfg(test)]
mod tests {
Expand Down
37 changes: 37 additions & 0 deletions src/extensions/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

use super::Extension;

/// A list of extensions for a 3scale API call.
///
/// `List` provides a builder-like API for constructing a set of extensions to include
/// in an API request. Most methods are chainable and return `self` to allow fluent construction.
///
/// # Examples
///
/// ```
/// use threescalers::extensions::{List, Extension};
///
/// let extensions = List::new()
/// .hierarchy()
/// .no_body();
/// ```
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct List<'s>(Vec<Extension<'s>>);
Expand All @@ -15,103 +29,126 @@
}

impl<'s> List<'s> {
/// Creates a new empty list of extensions.
pub fn new() -> Self {
Self(Vec::new())
}

/// Creates a new list with space for at least `capacity` extensions.
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
}

/// Consumes this list and returns the underlying vector of extensions.
pub fn into_inner(self) -> Vec<Extension<'s>> {
self.0
}

/// Returns a reference to the underlying vector of extensions.
pub fn as_vec(&self) -> &Vec<Extension<'s>> {
self.0.as_ref()
}

/// Returns a mutable reference to the underlying vector of extensions.
pub fn as_mut_vec(&mut self) -> &mut Vec<Extension<'s>> {
self.0.as_mut()
}

/// Returns the number of extensions in this list.
pub fn len(&self) -> usize {
self.0.len()
}

/// Returns `true` if this list contains no extensions.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Removes all extensions from the list and returns the number that were removed.
pub fn clear(&mut self) -> usize {
let cleared = self.len();
self.0.clear();
cleared
}

/// Returns the number of extensions this list can hold without reallocating.
pub fn capacity(&self) -> usize {
self.0.capacity()
}

/// Reserves space for at least `additional` more extensions.
pub fn reserve(mut self, additional: usize) -> Self {
self.0.reserve(additional);
self
}

/// Shrinks the capacity of this list to match its current length.
pub fn shrink_to_fit(mut self) -> Self {
self.0.shrink_to_fit();
self
}

/// Appends an extension to the list and returns self for chaining.
pub fn push(mut self, e: Extension<'s>) -> Self {
self.0.push(e);
self
}

/// Appends a custom extension with the given key and value.
pub fn push_other(self, key: Cow<'s, str>, value: Cow<'s, str>) -> Self {
self.push(Extension::Other(key, value))
}

/// Removes the first occurrence of the given extension from the list.
///
/// Returns `Some` containing the removed extension, or `None` if the extension was not found.
pub fn remove_item(&mut self, e: &Extension<'s>) -> Option<Extension<'s>> {
match self.0.iter().position(|elem| elem == e) {
Some(idx) => Some(self.0.remove(idx)),
_ => None,
}
}

/// Removes all occurrences of the given extension from the list.
///
/// Returns the number of extensions that were removed.
pub fn remove_all(&mut self, e: &Extension<'s>) -> usize {
let before = self.len();
self.0.retain(|elem| elem != e);
// side-effect free: length before retain is >= self.len()
before - self.len()
}

/// Adds a `no_body` extension to request that the response body be omitted.
pub fn no_body(self) -> Self {
self.push(Extension::NoBody)
}

/// Adds a `hierarchy` extension to request the metrics hierarchy in the response.
pub fn hierarchy(self) -> Self {
self.push(Extension::Hierarchy)
}

/// Adds a `flat_usage` extension with the specified nesting level.
pub fn flat_usage(self, level: u32) -> Self {
self.push(Extension::FlatUsage(level.to_string().into()))
}

/// Adds a `list_app_keys` extension with the specified level.
pub fn list_app_keys(self, level: u32) -> Self {
self.push(Extension::ListAppKeys(level.to_string().into()))
}
}

impl ToString for List<'_> {
fn to_string(&self) -> String {
self.0
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("&")
}
}

Check failure on line 151 in src/extensions/list.rs

View workflow job for this annotation

GitHub Actions / clippy

direct implementation of `ToString`

error: direct implementation of `ToString` --> src/extensions/list.rs:143:1 | 143 | / impl ToString for List<'_> { 144 | | fn to_string(&self) -> String { 145 | | self.0 146 | | .iter() ... | 151 | | } | |_^ | = help: prefer implementing `Display` instead = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#to_string_trait_impl

Check failure on line 151 in src/extensions/list.rs

View workflow job for this annotation

GitHub Actions / clippy

direct implementation of `ToString`

error: direct implementation of `ToString` --> src/extensions/list.rs:143:1 | 143 | / impl ToString for List<'_> { 144 | | fn to_string(&self) -> String { 145 | | self.0 146 | | .iter() ... | 151 | | } | |_^ | = help: prefer implementing `Display` instead = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#to_string_trait_impl

impl<'s> Extend<Extension<'s>> for List<'s> {
fn extend<T: IntoIterator<Item = Extension<'s>>>(&mut self, iter: T) {
Expand Down
14 changes: 14 additions & 0 deletions src/response/app_keys_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ use crate::{
credentials::ServiceId,
};

/// Contains the list of application keys returned by the `list_app_keys` extension.
///
/// This structure holds the keys associated with a 3scale application, along with
/// metadata about which service and application they belong to.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListAppKeys {
service_id: Option<ServiceId>,
Expand All @@ -20,6 +24,13 @@ pub struct ListAppKeys {
}

impl ListAppKeys {
/// Creates a new `ListAppKeys` structure.
///
/// # Arguments
///
/// * `service_id` - Optional service ID
/// * `app_id` - Optional application ID
/// * `keys` - Iterator of application keys
pub fn new<S: Into<ServiceId>, A: Into<AppId>, K: Into<AppKey>, I: IntoIterator<Item = K>>(
service_id: Option<S>,
app_id: Option<A>,
Expand All @@ -32,14 +43,17 @@ impl ListAppKeys {
}
}

/// Returns the service ID if available.
pub fn service_id(&self) -> Option<&ServiceId> {
self.service_id.as_ref()
}

/// Returns the application ID if available.
pub fn app_id(&self) -> Option<&AppId> {
self.app_id.as_ref()
}

/// Returns the list of application keys as a slice.
pub fn keys(&self) -> &[AppKey] {
self.keys.as_slice()
}
Expand Down
9 changes: 8 additions & 1 deletion src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ impl FromStr for OAuthToken {
}
}

// These trait impls are similar to FromStr (but are infallible)
/// Converts a string slice into a `UserId`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `UserId` always succeeds (it simply wraps the input string without validation).
impl From<&str> for UserId
where
Self: FromStr,
Expand All @@ -52,6 +55,10 @@ where
}
}

/// Converts a string slice into an `OAuthToken`.
///
/// This implementation is infallible because the underlying `FromStr::from_str`
/// for `OAuthToken` always succeeds (it simply wraps the input string without validation).
impl From<&str> for OAuthToken
where
Self: FromStr,
Expand Down
Loading