[\`uuid::Uuid\`] resolves as an intra-doc link, but the uuid crate is only in scope when the optional "uuid" feature is enabled, so a default `cargo doc` failed to resolve it. Use a plain code span instead.
163 lines
4.7 KiB
Rust
163 lines
4.7 KiB
Rust
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<chrono::Utc>;
|
|
|
|
/// 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<String>) -> 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<uuid::Uuid> {
|
|
self.0.parse().ok()
|
|
}
|
|
}
|
|
|
|
impl From<&str> for Id {
|
|
fn from(value: &str) -> Self {
|
|
Id(value.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<String> 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<String>),
|
|
}
|
|
|
|
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<String, Ability>,
|
|
}
|
|
|
|
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<String>,
|
|
/// The document title.
|
|
#[serde(default)]
|
|
pub title: String,
|
|
/// Child documents nested under this one.
|
|
#[serde(default)]
|
|
pub children: Vec<NavigationNode>,
|
|
}
|