dregs 1.0.0

The official Rust client for Dregs, which scores the users of your application for fraud and abuse.
Documentation
//! The `client.devices()` namespace.

use reqwest::Method;
use serde_json::json;

use crate::client::{Client, object_or_null, segment};
use crate::error::Result;
use crate::models::Device;
use crate::paging::{ItemStream, ListCall, Page, Paging, filter, paging_setters};

/// Read the devices Dregs has seen, and disregard them. Get one from [`Client::devices`].
#[derive(Debug, Clone, Copy)]
pub struct Devices<'a> {
    client: &'a Client,
}

impl<'a> Devices<'a> {
    pub(crate) fn new(client: &'a Client) -> Self {
        Devices { client }
    }

    /// Returns one page of devices, most recently seen first.
    ///
    /// Use [`list_all`](Self::list_all) to walk every page.
    pub async fn list(&self, params: ListDevices) -> Result<Page<Device>> {
        params.call().page(self.client).await
    }

    /// Every device that matches, fetching pages as it goes. See [`list`](Self::list).
    pub fn list_all(&self, params: ListDevices) -> ItemStream<Device> {
        params.call().walk(self.client)
    }

    /// Returns the device with this fingerprint.
    ///
    /// Returns an error of kind [`NotFound`](crate::ApiErrorKind::NotFound) when no device has
    /// this fingerprint.
    pub async fn get(&self, fingerprint: &str) -> Result<Device> {
        let payload = self
            .client
            .request(Method::GET, &path(fingerprint)?, &[], None)
            .await?;

        Ok(Device::from_value(object_or_null(payload)))
    }

    /// Sets whether the device is disregarded, and returns it.
    ///
    /// A disregarded device, such as a shared office machine, no longer links the identities seen
    /// on it.
    ///
    /// Returns an error of kind [`NotFound`](crate::ApiErrorKind::NotFound) when no device has
    /// this fingerprint.
    pub async fn update(&self, fingerprint: &str, disregarded: bool) -> Result<Device> {
        let path = path(fingerprint)?;
        let body = json!({ "disregarded": disregarded });
        let payload = self
            .client
            .request(Method::PATCH, &path, &[], Some(&body))
            .await?;

        Ok(Device::from_value(object_or_null(payload)))
    }
}

/// The filters and paging for [`Devices::list`] and [`Devices::list_all`].
///
/// Sort on fields such as `"lastSeenAt"` or `"country"`. A filter left unset is not sent.
#[derive(Debug, Clone, Default)]
pub struct ListDevices {
    identity: Option<String>,
    term: Option<String>,
    paging: Paging,
}

impl ListDevices {
    /// Every device, from the first page.
    pub fn new() -> Self {
        ListDevices::default()
    }

    /// Only devices this identity was seen on.
    pub fn identity(mut self, identity_id: impl Into<String>) -> Self {
        self.identity = Some(identity_id.into());
        self
    }

    /// Free text matched against fingerprints, addresses, places, and user agents.
    pub fn term(mut self, term: impl Into<String>) -> Self {
        self.term = Some(term.into());
        self
    }

    paging_setters!();

    fn call(self) -> ListCall<Device> {
        let mut filters = Vec::new();

        filter(&mut filters, "identity", self.identity);
        filter(&mut filters, "term", self.term);

        ListCall {
            path: "/devices",
            filters,
            paging: self.paging,
            parse: Device::from_value,
        }
    }
}

fn path(fingerprint: &str) -> Result<String> {
    Ok(format!(
        "/devices/{}",
        segment(fingerprint, "A device fingerprint")?
    ))
}