Allow constructing Scopes from a string

master
Paul Woolcock 5 years ago
parent 32e40edd6c
commit 8b7ed19396
  1. 131
      src/scopes.rs

@ -3,10 +3,13 @@ use std::{
collections::HashSet,
fmt,
ops::BitOr,
str::FromStr,
};
use serde::ser::{Serialize, Serializer};
use errors::Error;
/// Represents a set of OAuth scopes
///
/// # Example
@ -24,6 +27,21 @@ pub struct Scopes {
scopes: HashSet<Scope>,
}
impl FromStr for Scopes {
type Err = Error;
fn from_str(s: &str) -> Result<Scopes, Self::Err> {
let mut set = HashSet::new();
for scope in s.split_whitespace() {
let scope = Scope::from_str(&scope)?;
set.insert(scope);
}
Ok(Scopes {
scopes: set,
})
}
}
impl Serialize for Scopes {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
@ -250,7 +268,7 @@ impl fmt::Display for Scopes {
/// Permission scope of the application.
/// [Details on what each permission provides][1]
/// [1]: https://github.com/tootsuite/documentation/blob/master/Using-the-API/OAuth-details.md)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Hash, Serialize)]
enum Scope {
/// Read only permissions.
#[serde(rename = "read")]
@ -266,9 +284,25 @@ enum Scope {
Push,
}
impl PartialOrd for Scope {
fn partial_cmp(&self, other: &Scope) -> Option<Ordering> {
Some(self.cmp(other))
impl FromStr for Scope {
type Err = Error;
fn from_str(s: &str) -> Result<Scope, Self::Err> {
Ok(match s {
"read" => Scope::Read(None),
"write" => Scope::Write(None),
"follow" => Scope::Follow,
"push" => Scope::Push,
read if read.starts_with("read:") => {
let r: Read = Read::from_str(&read[5..])?;
Scope::Read(Some(r))
},
write if write.starts_with("write:") => {
let w: Write = Write::from_str(&write[6..])?;
Scope::Write(Some(w))
},
_ => return Err(Error::Other("Unknown scope".to_string())),
})
}
}
@ -363,6 +397,27 @@ pub enum Read {
Statuses,
}
impl FromStr for Read {
type Err = Error;
fn from_str(s: &str) -> Result<Read, Self::Err> {
Ok(match s {
"accounts" => Read::Accounts,
"blocks" => Read::Blocks,
"favourites" => Read::Favourites,
"filters" => Read::Filters,
"follows" => Read::Follows,
"lists" => Read::Lists,
"mutes" => Read::Mutes,
"notifications" => Read::Notifications,
"reports" => Read::Reports,
"search" => Read::Search,
"statuses" => Read::Statuses,
_ => return Err(Error::Other("Unknown 'read' subcategory".to_string())),
})
}
}
impl PartialOrd for Read {
fn partial_cmp(&self, other: &Read) -> Option<Ordering> {
Some(self.cmp(other))
@ -437,6 +492,27 @@ pub enum Write {
Statuses,
}
impl FromStr for Write {
type Err = Error;
fn from_str(s: &str) -> Result<Write, Self::Err> {
Ok(match s {
"accounts" => Write::Accounts,
"blocks" => Write::Blocks,
"favourites" => Write::Favourites,
"filters" => Write::Filters,
"follows" => Write::Follows,
"lists" => Write::Lists,
"media" => Write::Media,
"mutes" => Write::Mutes,
"notifications" => Write::Notifications,
"reports" => Write::Reports,
"statuses" => Write::Statuses,
_ => return Err(Error::Other("Unknown 'write' subcategory".to_string())),
})
}
}
impl PartialOrd for Write {
fn partial_cmp(&self, other: &Write) -> Option<Ordering> {
Some(self.cmp(other))
@ -664,4 +740,51 @@ mod tests {
assert_eq!(&ser, &expected);
}
}
#[test]
fn test_scope_from_str() {
let tests = [
("read", Scope::Read(None)),
("read:accounts", Scope::Read(Some(Read::Accounts))),
("read:blocks", Scope::Read(Some(Read::Blocks))),
("read:favourites", Scope::Read(Some(Read::Favourites))),
("read:filters", Scope::Read(Some(Read::Filters))),
("read:follows", Scope::Read(Some(Read::Follows))),
("read:lists", Scope::Read(Some(Read::Lists))),
("read:mutes", Scope::Read(Some(Read::Mutes))),
("read:notifications", Scope::Read(Some(Read::Notifications))),
("read:reports", Scope::Read(Some(Read::Reports))),
("read:search", Scope::Read(Some(Read::Search))),
("read:statuses", Scope::Read(Some(Read::Statuses))),
("write", Scope::Write(None)),
("write:accounts", Scope::Write(Some(Write::Accounts))),
("write:blocks", Scope::Write(Some(Write::Blocks))),
("write:favourites", Scope::Write(Some(Write::Favourites))),
("write:filters", Scope::Write(Some(Write::Filters))),
("write:follows", Scope::Write(Some(Write::Follows))),
("write:lists", Scope::Write(Some(Write::Lists))),
("write:media", Scope::Write(Some(Write::Media))),
("write:mutes", Scope::Write(Some(Write::Mutes))),
(
"write:notifications",
Scope::Write(Some(Write::Notifications)),
),
("write:reports", Scope::Write(Some(Write::Reports))),
("write:statuses", Scope::Write(Some(Write::Statuses))),
("follow", Scope::Follow),
("push", Scope::Push),
];
for (source, expected) in &tests {
let result = Scope::from_str(source).expect(&format!("Couldn't parse '{}'", &source));
assert_eq!(result, *expected);
}
}
#[test]
fn test_scopes_str_round_trip() {
let original = "read write follow push";
let scopes = Scopes::from_str(original).expect("Couldn't convert to Scopes");
let result = format!("{}", scopes);
assert_eq!(original, result);
}
}

Loading…
Cancel
Save