1use std::collections::HashSet;
4
5use crate::common::secret;
6use crate::handle::{Handle, Handles};
7use crate::id::Id;
8use crate::phcrypto;
9use crate::servers::yivi;
10
11#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
12pub struct Type {
13 pub id: Id,
15
16 pub handles: Handles,
18
19 pub bannable: bool,
22
23 pub identifying: bool,
25
26 pub sources: Vec<SourceDetails>,
30
31 #[serde(default)]
32 pub not_addable_by_default: bool,
36}
37
38impl std::fmt::Display for Type {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 write!(f, "{}", self.handles.preferred())
41 }
42}
43
44impl Type {
45 pub fn yivi_attr_type_ids(&self) -> impl Iterator<Item = &yivi::AttributeTypeIdentifier> {
48 #[expect(clippy::unnecessary_filter_map)] self.sources.iter().filter_map(|source| match source {
50 SourceDetails::Yivi { attr_type_id } => Some(attr_type_id),
51 })
52 }
53
54 pub(crate) fn filter_sources(&mut self, accept_source: impl Fn(Source) -> bool) {
56 self.sources
57 .retain(|source_details| accept_source(source_details.source()))
58 }
59}
60
61#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
63#[serde(rename_all = "snake_case")]
64pub enum SourceDetails {
65 Yivi {
66 attr_type_id: yivi::AttributeTypeIdentifier,
68 },
69}
70
71impl SourceDetails {
72 pub fn source(&self) -> Source {
73 match &self {
74 SourceDetails::Yivi { .. } => Source::Yivi,
75 }
76 }
77}
78
79#[derive(serde::Deserialize, serde::Serialize, Hash, Debug, Clone, Copy, PartialEq, Eq)]
80pub enum Source {
81 Yivi,
82}
83
84impl crate::map::Handled for Type {
85 fn handles(&self) -> &[Handle] {
86 &self.handles
87 }
88
89 fn id(&self) -> &Id {
90 &self.id
91 }
92}
93
94#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
95pub struct Attr {
96 pub attr_type: Id,
98
99 pub value: String,
101
102 #[serde(skip_serializing_if = "std::ops::Not::not")]
103 #[serde(default)]
104 pub bannable: bool,
105
106 #[serde(skip_serializing_if = "std::ops::Not::not")]
109 #[serde(default)]
110 pub not_identifying: bool,
111
112 #[serde(skip_serializing_if = "std::ops::Not::not")]
116 #[serde(default)]
117 pub not_addable: bool,
118}
119
120impl Attr {
121 pub fn id(&self, secret: impl secret::DigestibleSecret) -> Id {
124 phcrypto::attr_id(self, secret)
125 }
126}
127
128crate::api::having_message_code! {Attr, Attr}
130
131#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
133pub struct AttrState {
134 pub attr: Id,
135
136 #[serde(default)]
138 pub banned: bool,
139
140 #[serde(default)]
147 pub may_identify_user: Option<Id>,
148
149 #[serde(default)]
153 pub bans_users: HashSet<Id>,
154}
155
156impl AttrState {
157 pub fn new(attr_id: Id, attr: &Attr, user_id: Id) -> Self {
158 Self {
159 attr: attr_id,
160 banned: false,
161 may_identify_user: if attr.not_identifying {
162 None
163 } else {
164 Some(user_id)
165 },
166 bans_users: if attr.bannable {
167 std::iter::once(user_id).collect()
168 } else {
169 Default::default()
170 },
171 }
172 }
173}