use std::collections::BTreeMap; use std::fmt; use serde::{Deserialize, Serialize}; /// A timestamp as returned by the Outline API (ISO 8601 / RFC 3339, UTC). pub type Timestamp = chrono::DateTime; /// An opaque Outline resource identifier. /// /// Outline generally uses UUIDs for `id` fields, but some fields /// (`urlId`, `shareId`) use a different, shorter format. Modeling `Id` as a /// transparent string newtype (rather than `uuid::Uuid`) means an /// unexpected value from a self-hosted or future Outline version cannot /// fail deserialization of an entire response. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(transparent)] pub struct Id(String); impl Id { /// Creates a new [`Id`] from any string-like value. pub fn new(value: impl Into) -> Self { Id(value.into()) } /// Returns the identifier as a string slice. pub fn as_str(&self) -> &str { &self.0 } /// Parses this identifier as a [`uuid::Uuid`], if it is one. #[cfg(feature = "uuid")] pub fn as_uuid(&self) -> Option { self.0.parse().ok() } } impl From<&str> for Id { fn from(value: &str) -> Self { Id(value.to_string()) } } impl From for Id { fn from(value: String) -> Self { Id(value) } } impl From<&Id> for Id { fn from(value: &Id) -> Self { value.clone() } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } /// A sharing/collection permission level. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[non_exhaustive] pub enum Permission { /// Read-only access. Read, /// Read and write access. ReadWrite, /// A value not recognized by this version of the crate. #[serde(untagged)] Unknown(UnknownVariant), } /// A string value that did not match any known enum variant. /// /// Carrying the original string (rather than silently discarding it via /// `#[serde(other)]`) keeps the API forward-compatible with server versions /// newer than this crate: unrecognized values round-trip instead of causing /// a hard deserialization failure. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct UnknownVariant(pub String); impl fmt::Display for UnknownVariant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } /// Sort direction used by list endpoints. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] pub enum SortDirection { /// Ascending order. Asc, /// Descending order. Desc, } /// Either a boolean or a list of membership names granting an ability. /// /// The Outline API models `Policy.abilities` values as `oneOf [boolean, string[]]`. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] pub enum Ability { /// The ability is simply allowed or denied. Allowed(bool), /// The ability is granted through the listed memberships. Memberships(Vec), } impl Ability { /// Whether this ability is granted, treating a non-empty membership list as `true`. pub fn is_allowed(&self) -> bool { match self { Ability::Allowed(allowed) => *allowed, Ability::Memberships(memberships) => !memberships.is_empty(), } } } /// Describes which actions the current actor may perform on a resource. /// /// Returned alongside `data` on most endpoints. `policy.id` matches the /// `id` of the resource it describes. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Policy { /// The id of the resource this policy describes. pub id: Id, /// A map of ability name (e.g. `"update"`, `"delete"`) to its status. #[serde(default)] pub abilities: BTreeMap, } impl Policy { /// Whether the current actor is allowed to perform `ability` on this resource. /// /// Returns `false` if the ability is not present in the map at all. pub fn can(&self, ability: &str) -> bool { self.abilities.get(ability).is_some_and(Ability::is_allowed) } } /// A node in a collection's document tree, as returned by `collections.documents`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NavigationNode { /// The document id. pub id: Id, /// The short, URL-friendly id of the document. #[serde(default)] pub url_id: Option, /// The document title. #[serde(default)] pub title: String, /// Child documents nested under this one. #[serde(default)] pub children: Vec, }