1use std::{
2 cmp::{Ordering, PartialEq, PartialOrd},
3 collections::HashSet,
4 fmt,
5 ops::BitOr,
6 str::FromStr,
7};
8
9use serde::ser::{Serialize, Serializer};
10
11use crate::errors::Error;
12use derive_is_enum_variant::is_enum_variant;
13use serde::{
14 de::{self, Visitor},
15 Deserialize, Deserializer,
16};
17
18#[derive(Clone)]
31pub struct Scopes {
32 scopes: HashSet<Scope>,
33}
34
35impl FromStr for Scopes {
36 type Err = Error;
37
38 fn from_str(s: &str) -> Result<Scopes, Self::Err> {
39 let mut set = HashSet::new();
40 for scope in s.split_whitespace() {
41 let scope = Scope::from_str(scope)?;
42 set.insert(scope);
43 }
44 Ok(Scopes { scopes: set })
45 }
46}
47
48impl Serialize for Scopes {
49 fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
50 where
51 S: Serializer,
52 {
53 let repr = format!("{self}");
54 serializer.serialize_str(&repr)
55 }
56}
57
58struct DeserializeScopesVisitor;
59
60impl<'de> Visitor<'de> for DeserializeScopesVisitor {
61 type Value = Scopes;
62
63 fn expecting(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
64 write!(formatter, "space separated scopes")
65 }
66
67 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
68 where
69 E: de::Error,
70 {
71 Scopes::from_str(v).map_err(de::Error::custom)
72 }
73}
74
75impl<'de> Deserialize<'de> for Scopes {
76 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
77 where
78 D: Deserializer<'de>,
79 {
80 deserializer.deserialize_str(DeserializeScopesVisitor)
81 }
82}
83
84impl Scopes {
85 pub fn all() -> Scopes {
94 Scopes::read_all() | Scopes::write_all() | Scopes::follow() | Scopes::push()
95 }
96
97 pub fn read_all() -> Scopes {
106 Scopes::_read(None)
107 }
108
109 pub fn read(subscope: Read) -> Scopes {
118 Scopes::_read(Some(subscope))
119 }
120
121 pub fn write_all() -> Scopes {
130 Scopes::_write(None)
131 }
132
133 pub fn write(subscope: Write) -> Scopes {
142 Scopes::_write(Some(subscope))
143 }
144
145 pub fn follow() -> Scopes {
154 Scopes::new(Scope::Follow)
155 }
156
157 pub fn push() -> Scopes {
166 Scopes::new(Scope::Push)
167 }
168
169 pub fn and(self, other: Scopes) -> Scopes {
181 let new_set: HashSet<_> = self.scopes.union(&other.scopes).copied().collect();
182 Scopes { scopes: new_set }
183 }
184
185 fn _write(subscope: Option<Write>) -> Scopes {
186 Scopes::new(Scope::Write(subscope))
187 }
188
189 fn _read(subscope: Option<Read>) -> Scopes {
190 Scopes::new(Scope::Read(subscope))
191 }
192
193 fn new(scope: Scope) -> Scopes {
194 let mut set = HashSet::new();
195 set.insert(scope);
196 Scopes { scopes: set }
197 }
198}
199
200impl BitOr for Scopes {
201 type Output = Scopes;
202
203 fn bitor(self, other: Scopes) -> Self::Output {
204 self.and(other)
205 }
206}
207
208impl PartialEq for Scopes {
209 fn eq(&self, other: &Scopes) -> bool {
210 self.scopes
211 .symmetric_difference(&other.scopes)
212 .next()
213 .is_none()
214 }
215}
216
217impl Default for Scopes {
218 fn default() -> Scopes {
219 Scopes::read_all()
220 }
221}
222
223impl fmt::Debug for Scopes {
224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
225 write!(f, "[")?;
226 for scope in &self.scopes {
227 write!(f, "{:?}", &scope)?;
228 }
229 Ok(write!(f, "]")?)
230 }
231}
232
233impl fmt::Display for Scopes {
234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235 let mut start = true;
236 let scopes = {
237 let mut scopes = self.scopes.iter().collect::<Vec<_>>();
238 scopes.sort();
239 scopes
240 };
241 for scope in &scopes {
242 if !start {
243 write!(f, " ")?;
244 } else {
245 start = false;
246 }
247 write!(f, "{}", &scope)?;
248 }
249 Ok(())
250 }
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, is_enum_variant)]
257#[serde(rename_all = "lowercase")]
258pub enum Scope {
259 Read(Option<Read>),
261 Write(Option<Write>),
263 Follow,
265 Push,
267}
268
269impl FromStr for Scope {
270 type Err = Error;
271
272 fn from_str(s: &str) -> Result<Scope, Self::Err> {
273 Ok(match s {
274 "read" => Scope::Read(None),
275 "write" => Scope::Write(None),
276 "follow" => Scope::Follow,
277 "push" => Scope::Push,
278 read if read.starts_with("read:") => {
279 let r: Read = Read::from_str(&read[5..])?;
280 Scope::Read(Some(r))
281 }
282 write if write.starts_with("write:") => {
283 let w: Write = Write::from_str(&write[6..])?;
284 Scope::Write(Some(w))
285 }
286 _ => return Err(Error::Other("Unknown scope".to_string())),
287 })
288 }
289}
290
291impl PartialOrd for Scope {
292 fn partial_cmp(&self, other: &Scope) -> Option<Ordering> {
293 Some(self.cmp(other))
294 }
295}
296
297impl Ord for Scope {
298 fn cmp(&self, other: &Scope) -> Ordering {
299 match (*self, *other) {
300 (Scope::Read(None), Scope::Read(None)) => Ordering::Equal,
301 (Scope::Read(None), Scope::Read(Some(..))) => Ordering::Less,
302 (Scope::Read(Some(..)), Scope::Read(None)) => Ordering::Greater,
303 (Scope::Read(Some(ref a)), Scope::Read(Some(ref b))) => a.cmp(b),
304
305 (Scope::Write(None), Scope::Write(None)) => Ordering::Equal,
306 (Scope::Write(None), Scope::Write(Some(..))) => Ordering::Less,
307 (Scope::Write(Some(..)), Scope::Write(None)) => Ordering::Greater,
308 (Scope::Write(Some(ref a)), Scope::Write(Some(ref b))) => a.cmp(b),
309
310 (Scope::Read(..), Scope::Write(..)) => Ordering::Less,
311 (Scope::Read(..), Scope::Follow) => Ordering::Less,
312 (Scope::Read(..), Scope::Push) => Ordering::Less,
313
314 (Scope::Write(..), Scope::Read(..)) => Ordering::Greater,
315 (Scope::Write(..), Scope::Follow) => Ordering::Less,
316 (Scope::Write(..), Scope::Push) => Ordering::Less,
317
318 (Scope::Follow, Scope::Read(..)) => Ordering::Greater,
319 (Scope::Follow, Scope::Write(..)) => Ordering::Greater,
320 (Scope::Follow, Scope::Follow) => Ordering::Equal,
321 (Scope::Follow, Scope::Push) => Ordering::Less,
322
323 (Scope::Push, Scope::Push) => Ordering::Equal,
324 (Scope::Push, _) => Ordering::Greater,
325 }
326 }
327}
328
329impl fmt::Display for Scope {
330 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
331 use self::Scope::*;
332 let s = match *self {
333 Read(Some(ref r)) => return fmt::Display::fmt(r, f),
334 Read(None) => "read",
335 Write(Some(ref w)) => return fmt::Display::fmt(w, f),
336 Write(None) => "write",
337 Follow => "follow",
338 Push => "push",
339 };
340 write!(f, "{s}")
341 }
342}
343
344impl Default for Scope {
345 fn default() -> Self {
346 Scope::Read(None)
347 }
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, is_enum_variant)]
352pub enum Read {
353 #[serde(rename = "accounts")]
355 Accounts,
356 #[serde(rename = "blocks")]
358 Blocks,
359 #[serde(rename = "favourites")]
361 Favourites,
362 #[serde(rename = "filters")]
364 Filters,
365 #[serde(rename = "follows")]
367 Follows,
368 #[serde(rename = "lists")]
370 Lists,
371 #[serde(rename = "mutes")]
373 Mutes,
374 #[serde(rename = "notifications")]
376 Notifications,
377 #[serde(rename = "reports")]
379 Reports,
380 #[serde(rename = "search")]
382 Search,
383 #[serde(rename = "statuses")]
385 Statuses,
386}
387
388impl FromStr for Read {
389 type Err = Error;
390
391 fn from_str(s: &str) -> Result<Read, Self::Err> {
392 Ok(match s {
393 "accounts" => Read::Accounts,
394 "blocks" => Read::Blocks,
395 "favourites" => Read::Favourites,
396 "filters" => Read::Filters,
397 "follows" => Read::Follows,
398 "lists" => Read::Lists,
399 "mutes" => Read::Mutes,
400 "notifications" => Read::Notifications,
401 "reports" => Read::Reports,
402 "search" => Read::Search,
403 "statuses" => Read::Statuses,
404 _ => return Err(Error::Other("Unknown 'read' subcategory".to_string())),
405 })
406 }
407}
408
409impl PartialOrd for Read {
410 fn partial_cmp(&self, other: &Read) -> Option<Ordering> {
411 Some(self.cmp(other))
412 }
413}
414
415impl Ord for Read {
416 fn cmp(&self, other: &Read) -> Ordering {
417 let a = format!("{self:?}");
418 let b = format!("{other:?}");
419 a.cmp(&b)
420 }
421}
422
423impl fmt::Display for Read {
424 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425 write!(
426 f,
427 "read:{}",
428 match *self {
429 Read::Accounts => "accounts",
430 Read::Blocks => "blocks",
431 Read::Favourites => "favourites",
432 Read::Filters => "filters",
433 Read::Follows => "follows",
434 Read::Lists => "lists",
435 Read::Mutes => "mutes",
436 Read::Notifications => "notifications",
437 Read::Reports => "reports",
438 Read::Search => "search",
439 Read::Statuses => "statuses",
440 }
441 )
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, is_enum_variant)]
447pub enum Write {
448 #[serde(rename = "accounts")]
450 Accounts,
451 #[serde(rename = "blocks")]
453 Blocks,
454 #[serde(rename = "favourites")]
456 Favourites,
457 #[serde(rename = "filters")]
459 Filters,
460 #[serde(rename = "follows")]
462 Follows,
463 #[serde(rename = "lists")]
465 Lists,
466 #[serde(rename = "media")]
468 Media,
469 #[serde(rename = "mutes")]
471 Mutes,
472 #[serde(rename = "notifications")]
474 Notifications,
475 #[serde(rename = "reports")]
477 Reports,
478 #[serde(rename = "statuses")]
480 Statuses,
481}
482
483impl FromStr for Write {
484 type Err = Error;
485
486 fn from_str(s: &str) -> Result<Write, Self::Err> {
487 Ok(match s {
488 "accounts" => Write::Accounts,
489 "blocks" => Write::Blocks,
490 "favourites" => Write::Favourites,
491 "filters" => Write::Filters,
492 "follows" => Write::Follows,
493 "lists" => Write::Lists,
494 "media" => Write::Media,
495 "mutes" => Write::Mutes,
496 "notifications" => Write::Notifications,
497 "reports" => Write::Reports,
498 "statuses" => Write::Statuses,
499 _ => return Err(Error::Other("Unknown 'write' subcategory".to_string())),
500 })
501 }
502}
503
504impl PartialOrd for Write {
505 fn partial_cmp(&self, other: &Write) -> Option<Ordering> {
506 Some(self.cmp(other))
507 }
508}
509
510impl Ord for Write {
511 fn cmp(&self, other: &Write) -> Ordering {
512 let a = format!("{self:?}");
513 let b = format!("{other:?}");
514 a.cmp(&b)
515 }
516}
517
518impl fmt::Display for Write {
519 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520 write!(
521 f,
522 "write:{}",
523 match *self {
524 Write::Accounts => "accounts",
525 Write::Blocks => "blocks",
526 Write::Favourites => "favourites",
527 Write::Filters => "filters",
528 Write::Follows => "follows",
529 Write::Lists => "lists",
530 Write::Media => "media",
531 Write::Mutes => "mutes",
532 Write::Notifications => "notifications",
533 Write::Reports => "reports",
534 Write::Statuses => "statuses",
535 }
536 )
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543 use serde_json;
544
545 #[test]
546 fn test_write_cmp() {
547 let tests = [
548 (Write::Accounts, Write::Blocks),
549 (Write::Blocks, Write::Favourites),
550 (Write::Favourites, Write::Filters),
551 (Write::Filters, Write::Follows),
552 (Write::Follows, Write::Lists),
553 (Write::Lists, Write::Media),
554 (Write::Media, Write::Mutes),
555 (Write::Mutes, Write::Notifications),
556 (Write::Notifications, Write::Reports),
557 (Write::Reports, Write::Statuses),
558 ];
559
560 for (a, b) in &tests {
561 assert!(a < b);
562 assert!(b > a);
563 }
564 }
565
566 #[test]
567 fn test_read_cmp() {
568 let tests = [
569 (Read::Accounts, Read::Blocks),
570 (Read::Blocks, Read::Favourites),
571 (Read::Favourites, Read::Filters),
572 (Read::Filters, Read::Follows),
573 (Read::Follows, Read::Lists),
574 (Read::Lists, Read::Mutes),
575 (Read::Mutes, Read::Notifications),
576 (Read::Notifications, Read::Reports),
577 (Read::Reports, Read::Search),
578 (Read::Search, Read::Statuses),
579 ];
580 for (a, b) in &tests {
581 assert!(a < b);
582 assert!(b > a);
583 }
584 }
585
586 #[test]
587 fn test_scope_cmp() {
588 let tests = [
589 (Scope::Read(None), Scope::Read(Some(Read::Accounts))),
590 (Scope::Read(None), Scope::Read(Some(Read::Blocks))),
591 (Scope::Read(None), Scope::Read(Some(Read::Favourites))),
592 (Scope::Read(None), Scope::Read(Some(Read::Filters))),
593 (Scope::Read(None), Scope::Read(Some(Read::Follows))),
594 (Scope::Read(None), Scope::Read(Some(Read::Lists))),
595 (Scope::Read(None), Scope::Read(Some(Read::Mutes))),
596 (Scope::Read(None), Scope::Read(Some(Read::Notifications))),
597 (Scope::Read(None), Scope::Read(Some(Read::Reports))),
598 (Scope::Read(None), Scope::Read(Some(Read::Search))),
599 (Scope::Read(None), Scope::Read(Some(Read::Statuses))),
600 (Scope::Read(Some(Read::Statuses)), Scope::Write(None)),
601 (Scope::Read(Some(Read::Mutes)), Scope::Follow),
602 (Scope::Read(None), Scope::Push),
603 (Scope::Write(None), Scope::Write(Some(Write::Accounts))),
604 (Scope::Write(None), Scope::Write(Some(Write::Blocks))),
605 (Scope::Write(None), Scope::Write(Some(Write::Favourites))),
606 (Scope::Write(None), Scope::Write(Some(Write::Filters))),
607 (Scope::Write(None), Scope::Write(Some(Write::Follows))),
608 (Scope::Write(None), Scope::Write(Some(Write::Lists))),
609 (Scope::Write(None), Scope::Write(Some(Write::Media))),
610 (Scope::Write(None), Scope::Write(Some(Write::Mutes))),
611 (Scope::Write(None), Scope::Write(Some(Write::Notifications))),
612 (Scope::Write(None), Scope::Write(Some(Write::Reports))),
613 (Scope::Write(None), Scope::Write(Some(Write::Statuses))),
614 (Scope::Write(Some(Write::Statuses)), Scope::Follow),
615 (Scope::Write(Some(Write::Follows)), Scope::Push),
616 ];
617
618 for (a, b) in &tests {
619 assert!(a < b);
620 }
621 }
622
623 #[test]
624 fn test_scope_display() {
625 let values = [
626 Scope::Read(None),
627 Scope::Read(Some(Read::Accounts)),
628 Scope::Read(Some(Read::Blocks)),
629 Scope::Read(Some(Read::Favourites)),
630 Scope::Read(Some(Read::Filters)),
631 Scope::Read(Some(Read::Follows)),
632 Scope::Read(Some(Read::Lists)),
633 Scope::Read(Some(Read::Mutes)),
634 Scope::Read(Some(Read::Notifications)),
635 Scope::Read(Some(Read::Reports)),
636 Scope::Read(Some(Read::Search)),
637 Scope::Read(Some(Read::Statuses)),
638 Scope::Write(None),
639 Scope::Write(Some(Write::Accounts)),
640 Scope::Write(Some(Write::Blocks)),
641 Scope::Write(Some(Write::Favourites)),
642 Scope::Write(Some(Write::Filters)),
643 Scope::Write(Some(Write::Follows)),
644 Scope::Write(Some(Write::Lists)),
645 Scope::Write(Some(Write::Media)),
646 Scope::Write(Some(Write::Mutes)),
647 Scope::Write(Some(Write::Notifications)),
648 Scope::Write(Some(Write::Reports)),
649 Scope::Write(Some(Write::Statuses)),
650 Scope::Follow,
651 Scope::Push,
652 ];
653
654 let expecteds = [
655 "read".to_string(),
656 "read:accounts".to_string(),
657 "read:blocks".to_string(),
658 "read:favourites".to_string(),
659 "read:filters".to_string(),
660 "read:follows".to_string(),
661 "read:lists".to_string(),
662 "read:mutes".to_string(),
663 "read:notifications".to_string(),
664 "read:reports".to_string(),
665 "read:search".to_string(),
666 "read:statuses".to_string(),
667 "write".to_string(),
668 "write:accounts".to_string(),
669 "write:blocks".to_string(),
670 "write:favourites".to_string(),
671 "write:filters".to_string(),
672 "write:follows".to_string(),
673 "write:lists".to_string(),
674 "write:media".to_string(),
675 "write:mutes".to_string(),
676 "write:notifications".to_string(),
677 "write:reports".to_string(),
678 "write:statuses".to_string(),
679 "follow".to_string(),
680 "push".to_string(),
681 ];
682
683 let tests = values.iter().zip(expecteds.iter());
684
685 for (value, expected) in tests {
686 let result = value.to_string();
687 assert_eq!(&result, expected);
688 }
689 }
690
691 #[test]
692 fn test_scopes_default() {
693 let default: Scope = Default::default();
694 assert_eq!(default, Scope::Read(None));
695 }
696
697 #[test]
698 fn test_scopes_display() {
699 let tests = [
700 (
701 Scopes::read(Read::Accounts) | Scopes::follow(),
702 "read:accounts follow",
703 ),
704 (
705 Scopes::read(Read::Follows) | Scopes::read(Read::Accounts) | Scopes::write_all(),
706 "read:accounts read:follows write",
707 ),
708 ];
709
710 for (a, b) in &tests {
711 assert_eq!(&format!("{}", a), b);
712 }
713 }
714
715 #[test]
716 fn test_scopes_serialize_deserialize() {
717 let tests = [
718 (
719 Scopes::read_all() | Scopes::write(Write::Notifications) | Scopes::follow(),
720 "read write:notifications follow",
721 ),
722 (Scopes::follow() | Scopes::push(), "follow push"),
723 ];
724
725 for (a, b) in &tests {
726 let ser = serde_json::to_string(&a).expect("Couldn't serialize Scopes");
727 let expected = format!("\"{}\"", b);
728 assert_eq!(&ser, &expected);
729
730 let des: Scopes = serde_json::from_str(&ser).expect("Couldn't deserialize Scopes");
731 assert_eq!(&des, a);
732 }
733 }
734
735 #[test]
736 fn test_scope_from_str() {
737 let tests = [
738 ("read", Scope::Read(None)),
739 ("read:accounts", Scope::Read(Some(Read::Accounts))),
740 ("read:blocks", Scope::Read(Some(Read::Blocks))),
741 ("read:favourites", Scope::Read(Some(Read::Favourites))),
742 ("read:filters", Scope::Read(Some(Read::Filters))),
743 ("read:follows", Scope::Read(Some(Read::Follows))),
744 ("read:lists", Scope::Read(Some(Read::Lists))),
745 ("read:mutes", Scope::Read(Some(Read::Mutes))),
746 ("read:notifications", Scope::Read(Some(Read::Notifications))),
747 ("read:reports", Scope::Read(Some(Read::Reports))),
748 ("read:search", Scope::Read(Some(Read::Search))),
749 ("read:statuses", Scope::Read(Some(Read::Statuses))),
750 ("write", Scope::Write(None)),
751 ("write:accounts", Scope::Write(Some(Write::Accounts))),
752 ("write:blocks", Scope::Write(Some(Write::Blocks))),
753 ("write:favourites", Scope::Write(Some(Write::Favourites))),
754 ("write:filters", Scope::Write(Some(Write::Filters))),
755 ("write:follows", Scope::Write(Some(Write::Follows))),
756 ("write:lists", Scope::Write(Some(Write::Lists))),
757 ("write:media", Scope::Write(Some(Write::Media))),
758 ("write:mutes", Scope::Write(Some(Write::Mutes))),
759 (
760 "write:notifications",
761 Scope::Write(Some(Write::Notifications)),
762 ),
763 ("write:reports", Scope::Write(Some(Write::Reports))),
764 ("write:statuses", Scope::Write(Some(Write::Statuses))),
765 ("follow", Scope::Follow),
766 ("push", Scope::Push),
767 ];
768 for (source, expected) in &tests {
769 let result =
770 Scope::from_str(source).unwrap_or_else(|_| panic!("Couldn't parse '{}'", &source));
771 assert_eq!(result, *expected);
772 }
773 }
774
775 #[test]
776 fn test_scopes_str_round_trip() {
777 let original = "read write follow push";
778 let scopes = Scopes::from_str(original).expect("Couldn't convert to Scopes");
779 let result = format!("{}", scopes);
780 assert_eq!(original, result);
781 }
782}