ocm-types 0.2.1

Types required to implement the OpenCloudMesh filesharing protocol
Documentation
// SPDX-FileCopyrightText: 2026 Matthias Kraus <info@opengeomesh.org>
//
// SPDX-License-Identifier: LGPL-3.0-or-later

use serde::{Deserialize, Serialize};
use serde_json::{Map, Value, json};

use super::AccessType;
use super::default_access_types;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WebDavProperties {
    /// The type of access being granted to the remote resource.
    /// If omitted, it defaults to `['remote']`.
    /// - `remote` signals the recipient that the resource is available
    ///   for remote access and interactive browsing.
    /// - `datatx` signals the recipient to transfer the resource
    ///   from the given URI. The recipient MAY delegate a third-party
    ///   service to execute the data transfer on their behalf.
    #[serde(
        skip_serializing_if = "Vec::is_empty",
        default = "default_access_types"
    )]
    pub access_types: Vec<AccessType>,
    /// An URI to access the remote resource. The URI SHOULD be
    /// relative,
    /// such as a key or a UUID, in which case the prefix exposed by the
    /// `/.well-known/ocm` endpoint MUST be used to access the resource,
    /// or it MAY be absolute, including a hostname. The latter is NOT
    /// recommended because of security concerns.
    /// In all cases, for a `folder` resource, the composed URI acts
    /// as the root path, such that other files located within SHOULD
    /// be accessible by appending their relative path to that URI.
    pub uri: String,
    /// An optional secret to be used to access the resource, such
    /// as
    /// a bearer token. If a `refreshToken` is provided, it SHOULD be used
    /// instead via the code flow interaction, and the `sharedSecret`
    /// SHOULD be omitted. To prevent leaking it in logs it MUST NOT
    /// appear in any URI.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub shared_secret: Option<String>,
    /// A Token to be exchanged for a short-lived bearer token at the tokenEndPoint
    /// of the Sending Server for access to the shared resource.
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub refresh_token: Option<String>,
    /// The permissions granted to the sharee.
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub permissions: Vec<WebDavPermissions>,
    /// A list of requirements that the recipient provider MUST
    /// fulfill
    /// to access the resource. Requirements are optional, but if it is
    /// present it MUST NOT be empty. A recipient provider MUST reject
    /// a share whose requirements it does not understand.
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub requirements: Vec<WebDavRequirements>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum WebDavRequirements {
    /// `must-use-mfa` requires the user accessing the resource to be
    /// MFA-authenticated. This requirement MAY be used if the
    /// recipient provider exposes the `enforce-mfa` capability.
    MustUseMfa,
    /// `must-exchange-token` requires the recipient to exchange the given
    /// `refreshToken` via a signed HTTPS request to tokenEndPoint at the Sending
    /// Server, in order to get a short-lived token to be used for
    /// subsequent access. This requirement MAY be used if the
    /// recipient provider exposes the [`exchange-token`
    /// capability](crate::discovery::Capability::ExchangeToken).
    MustExchangeToken,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum WebDavPermissions {
    /// allows read-only access including download of a copy.
    Read,
    /// allows create, update, and delete rights on the resource.
    Write,
    /// allows re-share rights on the resource.
    Share,
}

impl TryFrom<WebDavProperties> for Map<String, Value> {
    // FIXME use proper error
    type Error = String;

    fn try_from(value: WebDavProperties) -> Result<Self, Self::Error> {
        let json = json!(value);
        serde_json::from_value(json).map_err(|e| e.to_string())
    }
}