context69-contracts 0.1.0

Shared request and response types for the Context69 API.
Documentation
use chrono::{DateTime, Utc};
use rmcp::schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Visibility {
    Public,
    Private,
}

impl Visibility {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Public => "public",
            Self::Private => "private",
        }
    }
}

impl std::str::FromStr for Visibility {
    type Err = anyhow::Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "public" => Ok(Self::Public),
            "private" => Ok(Self::Private),
            other => Err(anyhow::anyhow!("unsupported visibility: {other}")),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum MembershipRole {
    Owner,
    Maintainer,
    Viewer,
}

impl MembershipRole {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Owner => "owner",
            Self::Maintainer => "maintainer",
            Self::Viewer => "viewer",
        }
    }

    pub fn rank(self) -> i16 {
        match self {
            Self::Viewer => 1,
            Self::Maintainer => 2,
            Self::Owner => 3,
        }
    }
}

impl std::str::FromStr for MembershipRole {
    type Err = anyhow::Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "owner" => Ok(Self::Owner),
            "maintainer" => Ok(Self::Maintainer),
            "viewer" => Ok(Self::Viewer),
            other => Err(anyhow::anyhow!("unsupported membership role: {other}")),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum GroupKind {
    Personal,
    Shared,
}

impl GroupKind {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Personal => "personal",
            Self::Shared => "shared",
        }
    }
}

impl std::str::FromStr for GroupKind {
    type Err = anyhow::Error;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "personal" => Ok(Self::Personal),
            "shared" => Ok(Self::Shared),
            other => Err(anyhow::anyhow!("unsupported group kind: {other}")),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct GroupResponse {
    pub group_id: i64,
    pub group_key: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub parent_group_key: Option<String>,
    pub name: String,
    pub visibility: Visibility,
    pub kind: GroupKind,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub current_role: Option<MembershipRole>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProjectResponse {
    pub project_id: i64,
    pub group_key: String,
    pub project_key: String,
    pub name: String,
    pub visibility: Visibility,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub current_role: Option<MembershipRole>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct GroupMemberResponse {
    pub user_id: i64,
    pub login_name: String,
    pub display_name: String,
    pub role: MembershipRole,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProjectMemberResponse {
    pub user_id: i64,
    pub login_name: String,
    pub display_name: String,
    pub role: MembershipRole,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserDirectoryEntryResponse {
    pub user_id: i64,
    pub login_name: String,
    pub display_name: String,
}