1use std::{error::Error, fmt, mem};
2
3use serde::{Deserialize, Serialize};
4use steel_utils::Identifier;
5
6use super::{
7 PermissionContext, PermissionContextKeyError, PermissionResolutionSource,
8 PermissionRuleContext, PermissionRuleContextError,
9 rule_expression::{PermissionExpressionContextError, parse_context, write_context},
10};
11
12#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
14#[serde(untagged)]
15pub enum PermissionMetadataValue {
16 Bool(bool),
18 Integer(i64),
20 String(String),
22}
23
24impl PermissionMetadataValue {
25 #[must_use]
27 pub const fn as_bool(&self) -> Option<bool> {
28 match self {
29 Self::Bool(value) => Some(*value),
30 Self::Integer(_) | Self::String(_) => None,
31 }
32 }
33
34 #[must_use]
36 pub const fn as_i64(&self) -> Option<i64> {
37 match self {
38 Self::Integer(value) => Some(*value),
39 Self::Bool(_) | Self::String(_) => None,
40 }
41 }
42
43 #[must_use]
45 pub fn as_str(&self) -> Option<&str> {
46 match self {
47 Self::String(value) => Some(value),
48 Self::Bool(_) | Self::Integer(_) => None,
49 }
50 }
51}
52
53impl fmt::Display for PermissionMetadataValue {
54 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::Bool(value) => value.fmt(formatter),
57 Self::Integer(value) => value.fmt(formatter),
58 Self::String(value) => formatter.write_str(value),
59 }
60 }
61}
62
63#[derive(Clone, Debug, PartialEq, Eq)]
65pub struct PermissionMetadataExpression {
66 key: Identifier,
67 context: PermissionRuleContext,
68}
69
70impl PermissionMetadataExpression {
71 #[must_use]
73 pub const fn new(key: Identifier, context: PermissionRuleContext) -> Self {
74 Self { key, context }
75 }
76
77 pub fn parse(value: impl Into<String>) -> Result<Self, PermissionMetadataExpressionError> {
83 let value = value.into();
84 let Some(context_start) = value.find('{') else {
85 let key = parse_permission_metadata_key(value.as_str()).map_err(|source| {
86 PermissionMetadataExpressionError::InvalidMetadataKey { value, source }
87 })?;
88 return Ok(Self::new(key, PermissionRuleContext::Global));
89 };
90
91 if !value.ends_with('}') {
92 return Err(PermissionMetadataExpressionError::UnclosedContext);
93 }
94
95 let key_value = &value[..context_start];
96 let key = parse_permission_metadata_key(key_value).map_err(|source| {
97 PermissionMetadataExpressionError::InvalidMetadataKey {
98 value: key_value.to_owned(),
99 source,
100 }
101 })?;
102 let context_value = &value[context_start + 1..value.len() - 1];
103 let context = parse_context(context_value)
104 .map_err(PermissionMetadataExpressionError::from_context_error)?;
105 Ok(Self::new(key, context))
106 }
107
108 #[must_use]
110 pub const fn key(&self) -> &Identifier {
111 &self.key
112 }
113
114 #[must_use]
116 pub const fn context(&self) -> &PermissionRuleContext {
117 &self.context
118 }
119
120 #[must_use]
122 pub fn into_parts(self) -> (Identifier, PermissionRuleContext) {
123 (self.key, self.context)
124 }
125}
126
127impl fmt::Display for PermissionMetadataExpression {
128 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
129 self.key.fmt(formatter)?;
130 write_context(formatter, &self.context)
131 }
132}
133
134pub fn parse_permission_metadata_key(
140 value: impl Into<String>,
141) -> Result<Identifier, PermissionMetadataKeyError> {
142 let value = value.into();
143 let Some((namespace, path)) = value.split_once(':') else {
144 return Err(PermissionMetadataKeyError::InvalidFormat);
145 };
146 if namespace.is_empty() {
147 return Err(PermissionMetadataKeyError::EmptyNamespace);
148 }
149 if path.is_empty() {
150 return Err(PermissionMetadataKeyError::EmptyPath);
151 }
152 if path.contains(':') {
153 return Err(PermissionMetadataKeyError::InvalidFormat);
154 }
155 if namespace.split('.').any(str::is_empty) {
156 return Err(PermissionMetadataKeyError::InvalidNamespace);
157 }
158 if path.split(['.', '/']).any(str::is_empty) {
159 return Err(PermissionMetadataKeyError::InvalidPath);
160 }
161 if !Identifier::validate_namespace(namespace) {
162 return Err(PermissionMetadataKeyError::InvalidNamespace);
163 }
164 if !Identifier::validate_path(path) {
165 return Err(PermissionMetadataKeyError::InvalidPath);
166 }
167 Ok(Identifier::new(namespace.to_owned(), path.to_owned()))
168}
169
170#[derive(Clone, Copy, Debug, PartialEq, Eq)]
172pub enum PermissionMetadataKeyError {
173 InvalidFormat,
175 EmptyNamespace,
177 EmptyPath,
179 InvalidNamespace,
181 InvalidPath,
183}
184
185impl fmt::Display for PermissionMetadataKeyError {
186 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
187 match self {
188 Self::InvalidFormat => {
189 formatter.write_str("permission metadata key must be a namespaced id")
190 }
191 Self::EmptyNamespace => {
192 formatter.write_str("permission metadata key namespace is empty")
193 }
194 Self::EmptyPath => formatter.write_str("permission metadata key path is empty"),
195 Self::InvalidNamespace => {
196 formatter.write_str("permission metadata key namespace contains invalid characters")
197 }
198 Self::InvalidPath => {
199 formatter.write_str("permission metadata key path contains invalid characters")
200 }
201 }
202 }
203}
204
205impl Error for PermissionMetadataKeyError {}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum PermissionMetadataExpressionError {
210 InvalidMetadataKey {
212 value: String,
214 source: PermissionMetadataKeyError,
216 },
217 UnclosedContext,
219 EmptyContext,
221 InvalidContextEntry(String),
223 InvalidContextValue {
225 key: String,
227 value: String,
229 },
230 DuplicateContextKey(String),
232 InvalidDomain(String),
234 InvalidWorld(String),
236 InvalidContextKey {
238 key: String,
240 source: PermissionContextKeyError,
242 },
243 InvalidRuleContext(PermissionRuleContextError),
245}
246
247impl PermissionMetadataExpressionError {
248 fn from_context_error(error: PermissionExpressionContextError) -> Self {
249 match error {
250 PermissionExpressionContextError::EmptyContext => Self::EmptyContext,
251 PermissionExpressionContextError::InvalidContextEntry(entry) => {
252 Self::InvalidContextEntry(entry)
253 }
254 PermissionExpressionContextError::InvalidContextValue { key, value } => {
255 Self::InvalidContextValue { key, value }
256 }
257 PermissionExpressionContextError::DuplicateContextKey(key) => {
258 Self::DuplicateContextKey(key)
259 }
260 PermissionExpressionContextError::InvalidDomain(domain) => Self::InvalidDomain(domain),
261 PermissionExpressionContextError::InvalidWorld(world) => Self::InvalidWorld(world),
262 PermissionExpressionContextError::InvalidContextKey { key, source } => {
263 Self::InvalidContextKey { key, source }
264 }
265 PermissionExpressionContextError::InvalidRuleContext(source) => {
266 Self::InvalidRuleContext(source)
267 }
268 }
269 }
270}
271
272impl fmt::Display for PermissionMetadataExpressionError {
273 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match self {
275 Self::InvalidMetadataKey { value, source } => {
276 write!(
277 formatter,
278 "invalid permission metadata key '{value}': {source}"
279 )
280 }
281 Self::UnclosedContext => {
282 formatter.write_str("permission metadata context selector is not closed")
283 }
284 Self::EmptyContext => {
285 formatter.write_str("permission metadata context selector is empty")
286 }
287 Self::InvalidContextEntry(entry) => {
288 write!(
289 formatter,
290 "invalid permission metadata context entry '{entry}'"
291 )
292 }
293 Self::InvalidContextValue { key, value } => write!(
294 formatter,
295 "invalid permission metadata context value '{value}' for '{key}'"
296 ),
297 Self::DuplicateContextKey(key) => write!(
298 formatter,
299 "permission metadata context key '{key}' appears more than once"
300 ),
301 Self::InvalidDomain(domain) => write!(formatter, "invalid domain context '{domain}'"),
302 Self::InvalidWorld(world) => write!(formatter, "invalid world context '{world}'"),
303 Self::InvalidContextKey { key, source } => {
304 write!(
305 formatter,
306 "invalid permission context key '{key}': {source}"
307 )
308 }
309 Self::InvalidRuleContext(source) => source.fmt(formatter),
310 }
311 }
312}
313
314impl Error for PermissionMetadataExpressionError {}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct PermissionMetadataEntry {
319 key: Identifier,
320 context: PermissionRuleContext,
321 value: PermissionMetadataValue,
322}
323
324impl PermissionMetadataEntry {
325 #[must_use]
327 pub const fn new(key: Identifier, value: PermissionMetadataValue) -> Self {
328 Self {
329 key,
330 context: PermissionRuleContext::Global,
331 value,
332 }
333 }
334
335 #[must_use]
337 pub const fn new_with_context(
338 key: Identifier,
339 context: PermissionRuleContext,
340 value: PermissionMetadataValue,
341 ) -> Self {
342 Self {
343 key,
344 context,
345 value,
346 }
347 }
348
349 #[must_use]
351 pub const fn key(&self) -> &Identifier {
352 &self.key
353 }
354
355 #[must_use]
357 pub const fn context(&self) -> &PermissionRuleContext {
358 &self.context
359 }
360
361 #[must_use]
363 pub const fn value(&self) -> &PermissionMetadataValue {
364 &self.value
365 }
366}
367
368#[derive(Clone, Debug, Default, PartialEq, Eq)]
370pub struct PermissionMetadataSet {
371 entries: Vec<PermissionMetadataEntry>,
372 sources: Vec<PermissionResolutionSource>,
373}
374
375impl PermissionMetadataSet {
376 #[must_use]
378 pub const fn new() -> Self {
379 Self {
380 entries: Vec::new(),
381 sources: Vec::new(),
382 }
383 }
384
385 #[must_use]
387 pub fn from_entries(entries: impl IntoIterator<Item = PermissionMetadataEntry>) -> Self {
388 let entries = entries.into_iter().collect::<Vec<_>>();
389 let sources = vec![PermissionResolutionSource::Subject; entries.len()];
390 Self { entries, sources }
391 }
392
393 #[must_use]
395 pub fn entries(&self) -> &[PermissionMetadataEntry] {
396 &self.entries
397 }
398
399 #[must_use]
401 pub const fn is_empty(&self) -> bool {
402 self.entries.is_empty()
403 }
404
405 pub fn push(&mut self, entry: PermissionMetadataEntry) {
407 self.push_with_source(entry, PermissionResolutionSource::Subject);
408 }
409
410 pub fn set(&mut self, key: Identifier, value: PermissionMetadataValue) {
412 self.set_in(key, PermissionRuleContext::Global, value);
413 }
414
415 pub fn set_in(
417 &mut self,
418 key: Identifier,
419 context: PermissionRuleContext,
420 value: PermissionMetadataValue,
421 ) {
422 self.retain_entries(|entry| entry.key != key || entry.context != context);
423 self.push(PermissionMetadataEntry::new_with_context(
424 key, context, value,
425 ));
426 }
427
428 pub fn unset(&mut self, key: &Identifier) -> bool {
430 self.unset_in(key, &PermissionRuleContext::Global)
431 }
432
433 pub fn unset_in(&mut self, key: &Identifier, context: &PermissionRuleContext) -> bool {
435 let old_len = self.entries.len();
436 self.retain_entries(|entry| entry.key() != key || entry.context() != context);
437 self.entries.len() != old_len
438 }
439
440 #[must_use]
442 pub fn resolve(&self, key: &Identifier) -> Option<&PermissionMetadataValue> {
443 self.resolve_in(key, &PermissionContext::global())
444 }
445
446 #[must_use]
451 pub fn resolve_in(
452 &self,
453 key: &Identifier,
454 context: &PermissionContext,
455 ) -> Option<&PermissionMetadataValue> {
456 self.best_candidate(key, context)
457 .map(|candidate| self.entries[candidate.entry_index].value())
458 }
459
460 #[must_use]
462 pub fn resolve_detailed(&self, key: &Identifier) -> Option<PermissionMetadataResolution> {
463 self.resolve_in_detailed(key, &PermissionContext::global())
464 }
465
466 #[must_use]
468 pub fn resolve_in_detailed(
469 &self,
470 key: &Identifier,
471 context: &PermissionContext,
472 ) -> Option<PermissionMetadataResolution> {
473 self.best_candidate(key, context)
474 .map(|candidate| self.resolution(candidate))
475 }
476
477 pub(super) fn push_group(
478 &mut self,
479 entry: PermissionMetadataEntry,
480 group: &str,
481 group_priority: i32,
482 ) {
483 self.push_with_source(
484 entry,
485 PermissionResolutionSource::Group {
486 name: group.to_owned(),
487 priority: group_priority,
488 },
489 );
490 }
491
492 fn push_with_source(
493 &mut self,
494 entry: PermissionMetadataEntry,
495 source: PermissionResolutionSource,
496 ) {
497 self.entries.push(entry);
498 self.sources.push(source);
499 }
500
501 fn retain_entries(&mut self, mut keep: impl FnMut(&PermissionMetadataEntry) -> bool) {
502 let entries = mem::take(&mut self.entries);
503 let sources = mem::take(&mut self.sources);
504 for (entry, source) in entries.into_iter().zip(sources) {
505 if keep(&entry) {
506 self.entries.push(entry);
507 self.sources.push(source);
508 }
509 }
510 }
511
512 fn best_candidate(
513 &self,
514 key: &Identifier,
515 context: &PermissionContext,
516 ) -> Option<PermissionMetadataCandidate> {
517 let mut best: Option<PermissionMetadataCandidate> = None;
518 for (index, (entry, source)) in self.entries.iter().zip(&self.sources).enumerate() {
519 if entry.key() != key || !entry.context().matches(context) {
520 continue;
521 }
522 let candidate = PermissionMetadataCandidate {
523 entry_index: index,
524 context_specificity: entry.context().specificity(),
525 source: source.clone(),
526 };
527 if best
528 .as_ref()
529 .is_none_or(|current| candidate.order() > current.order())
530 {
531 best = Some(candidate);
532 }
533 }
534 best
535 }
536
537 fn resolution(&self, candidate: PermissionMetadataCandidate) -> PermissionMetadataResolution {
538 PermissionMetadataResolution {
539 entry: self.entries[candidate.entry_index].clone(),
540 source: candidate.source,
541 context_specificity: candidate.context_specificity,
542 insertion_index: candidate.entry_index,
543 }
544 }
545}
546
547#[derive(Clone, Debug, PartialEq, Eq)]
549pub struct PermissionMetadataResolution {
550 entry: PermissionMetadataEntry,
551 source: PermissionResolutionSource,
552 context_specificity: usize,
553 insertion_index: usize,
554}
555
556impl PermissionMetadataResolution {
557 #[must_use]
559 pub const fn entry(&self) -> &PermissionMetadataEntry {
560 &self.entry
561 }
562
563 #[must_use]
565 pub const fn source(&self) -> &PermissionResolutionSource {
566 &self.source
567 }
568
569 #[must_use]
571 pub const fn value(&self) -> &PermissionMetadataValue {
572 self.entry.value()
573 }
574
575 #[must_use]
577 pub const fn key(&self) -> &Identifier {
578 self.entry.key()
579 }
580
581 #[must_use]
583 pub const fn context(&self) -> &PermissionRuleContext {
584 self.entry.context()
585 }
586
587 #[must_use]
589 pub const fn context_specificity(&self) -> usize {
590 self.context_specificity
591 }
592
593 #[must_use]
595 pub const fn insertion_index(&self) -> usize {
596 self.insertion_index
597 }
598}
599
600#[derive(Clone, Debug, PartialEq, Eq)]
601struct PermissionMetadataCandidate {
602 entry_index: usize,
603 context_specificity: usize,
604 source: PermissionResolutionSource,
605}
606
607impl PermissionMetadataCandidate {
608 const fn order(&self) -> (usize, usize, i32, usize) {
609 (
610 self.context_specificity,
611 self.source.rank(),
612 self.source.tie_priority(),
613 self.entry_index,
614 )
615 }
616}