use std::sync::Arc;
use serde::{Deserialize, Serialize};
use crate::{
Result,
client::AnytypeClient,
filters::{Query, QueryWithFilters},
http_client::{GetPaged, HttpClient},
prelude::*,
};
#[derive(
Debug, Deserialize, Serialize, Clone, PartialEq, Eq, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MemberRole {
Viewer,
Editor,
Owner,
NoPermission,
}
#[derive(
Debug, Deserialize, Serialize, Clone, PartialEq, Eq, strum::Display, strum::EnumString,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum MemberStatus {
Joining,
Active,
Removed,
Declined,
Removing,
Canceled,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Member {
pub global_name: Option<String>,
pub icon: Option<serde_json::Value>,
pub id: String,
pub identity: Option<String>,
pub name: Option<String>,
pub role: MemberRole,
pub status: MemberStatus,
}
impl Member {
pub fn is_active(&self) -> bool {
self.status == MemberStatus::Active
}
pub fn is_owner(&self) -> bool {
self.role == MemberRole::Owner
}
pub fn can_edit(&self) -> bool {
matches!(self.role, MemberRole::Editor | MemberRole::Owner)
}
pub fn display_name(&self) -> &str {
self.name
.as_deref()
.or(self.global_name.as_deref())
.unwrap_or("Unknown")
}
}
#[derive(Debug, Deserialize)]
struct MemberResponse {
member: Member,
}
#[derive(Debug)]
pub struct MemberRequest {
client: Arc<HttpClient>,
limits: ValidationLimits,
space_id: String,
member_id: String,
}
impl MemberRequest {
pub(crate) fn new(
client: Arc<HttpClient>,
limits: ValidationLimits,
space_id: impl Into<String>,
member_id: impl Into<String>,
) -> Self {
Self {
client,
limits,
space_id: space_id.into(),
member_id: member_id.into(),
}
}
pub async fn get(self) -> Result<Member> {
self.limits.validate_id(&self.space_id, "space_id")?;
self.limits.validate_id(&self.member_id, "member_id")?;
let response: MemberResponse = self
.client
.get_request(
&format!("/v1/spaces/{}/members/{}", self.space_id, self.member_id),
QueryWithFilters::default(),
)
.await?;
Ok(response.member)
}
}
#[derive(Debug)]
pub struct ListMembersRequest {
client: Arc<HttpClient>,
limits: ValidationLimits,
space_id: String,
limit: Option<u32>,
offset: Option<u32>,
filters: Vec<Filter>,
}
impl ListMembersRequest {
pub(crate) fn new(
client: Arc<HttpClient>,
limits: ValidationLimits,
space_id: impl Into<String>,
) -> Self {
Self {
client,
limits,
space_id: space_id.into(),
limit: None,
offset: None,
filters: Vec::new(),
}
}
#[must_use]
pub fn limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
#[must_use]
pub fn offset(mut self, offset: u32) -> Self {
self.offset = Some(offset);
self
}
#[must_use]
pub fn filter(mut self, filter: Filter) -> Self {
self.filters.push(filter);
self
}
pub async fn list(self) -> Result<PagedResult<Member>> {
self.limits.validate_id(&self.space_id, "space_id")?;
let query = Query::default()
.set_limit_opt(self.limit)
.set_offset_opt(self.offset)
.add_filters(&self.filters);
self.client
.get_request_paged(&format!("/v1/spaces/{}/members", self.space_id), query)
.await
}
}
impl AnytypeClient {
pub fn member(
&self,
space_id: impl Into<String>,
member_id: impl Into<String>,
) -> MemberRequest {
MemberRequest::new(
self.client.clone(),
self.config.limits.clone(),
space_id,
member_id,
)
}
pub fn members(&self, space_id: impl Into<String>) -> ListMembersRequest {
ListMembersRequest::new(self.client.clone(), self.config.limits.clone(), space_id)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_member(role: MemberRole, status: MemberStatus) -> Member {
Member {
global_name: None,
icon: None,
id: "test".to_string(),
identity: None,
name: None,
role,
status,
}
}
#[test]
fn test_member_is_active() {
assert!(make_member(MemberRole::Editor, MemberStatus::Active).is_active());
assert!(!make_member(MemberRole::Editor, MemberStatus::Joining).is_active());
}
#[test]
fn test_member_is_owner() {
assert!(make_member(MemberRole::Owner, MemberStatus::Active).is_owner());
assert!(!make_member(MemberRole::Editor, MemberStatus::Active).is_owner());
}
#[test]
fn test_member_can_edit() {
assert!(make_member(MemberRole::Owner, MemberStatus::Active).can_edit());
assert!(make_member(MemberRole::Editor, MemberStatus::Active).can_edit());
assert!(!make_member(MemberRole::Viewer, MemberStatus::Active).can_edit());
}
#[test]
fn test_member_display_name() {
let mut member = make_member(MemberRole::Editor, MemberStatus::Active);
assert_eq!(member.display_name(), "Unknown");
member.global_name = Some("john.any".to_string());
assert_eq!(member.display_name(), "john.any");
member.name = Some("John Doe".to_string());
assert_eq!(member.display_name(), "John Doe");
}
}