use std::future::Future;
#[non_exhaustive]
pub struct TrawlerRequest {
pub user: Option<UserId>,
pub page: LobstersRequest,
pub is_priming: bool,
}
pub trait AsyncShutdown {
type Future: Future<Output = ()>;
fn shutdown(self) -> Self::Future;
}
pub type StoryId = [u8; 6];
pub type CommentId = [u8; 6];
pub type UserId = u32;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Vote {
Up,
Down,
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum LobstersRequest {
Frontpage,
Recent,
Comments,
User(UserId),
Story(StoryId),
Login,
Logout,
StoryVote(StoryId, Vote),
CommentVote(CommentId, Vote),
Submit {
id: StoryId,
title: String,
},
Comment {
id: CommentId,
story: StoryId,
parent: Option<CommentId>,
},
}
use std::mem;
use std::vec;
impl LobstersRequest {
pub fn all() -> vec::IntoIter<mem::Discriminant<Self>> {
vec![
mem::discriminant(&LobstersRequest::Story([0; 6])),
mem::discriminant(&LobstersRequest::Frontpage),
mem::discriminant(&LobstersRequest::User(0)),
mem::discriminant(&LobstersRequest::Comments),
mem::discriminant(&LobstersRequest::Recent),
mem::discriminant(&LobstersRequest::CommentVote([0; 6], Vote::Up)),
mem::discriminant(&LobstersRequest::StoryVote([0; 6], Vote::Up)),
mem::discriminant(&LobstersRequest::Comment {
id: [0; 6],
story: [0; 6],
parent: None,
}),
mem::discriminant(&LobstersRequest::Login),
mem::discriminant(&LobstersRequest::Submit {
id: [0; 6],
title: String::new(),
}),
mem::discriminant(&LobstersRequest::Logout),
]
.into_iter()
}
pub fn variant_name(v: &mem::Discriminant<Self>) -> &'static str {
match *v {
d if d == mem::discriminant(&LobstersRequest::Frontpage) => "Frontpage",
d if d == mem::discriminant(&LobstersRequest::Recent) => "Recent",
d if d == mem::discriminant(&LobstersRequest::Comments) => "Comments",
d if d == mem::discriminant(&LobstersRequest::User(0)) => "User",
d if d == mem::discriminant(&LobstersRequest::Story([0; 6])) => "Story",
d if d == mem::discriminant(&LobstersRequest::Login) => "Login",
d if d == mem::discriminant(&LobstersRequest::Logout) => "Logout",
d if d == mem::discriminant(&LobstersRequest::StoryVote([0; 6], Vote::Up)) => {
"StoryVote"
}
d if d == mem::discriminant(&LobstersRequest::CommentVote([0; 6], Vote::Up)) => {
"CommentVote"
}
d if d
== mem::discriminant(&LobstersRequest::Submit {
id: [0; 6],
title: String::new(),
}) =>
{
"Submit"
}
d if d
== mem::discriminant(&LobstersRequest::Comment {
id: [0; 6],
story: [0; 6],
parent: None,
}) =>
{
"Comment"
}
_ => unreachable!(),
}
}
pub fn describe(&self) -> String {
match *self {
LobstersRequest::Frontpage => String::from("GET /"),
LobstersRequest::Recent => String::from("GET /recent"),
LobstersRequest::Comments => String::from("GET /comments"),
LobstersRequest::User(uid) => format!("GET /u/#{}", uid),
LobstersRequest::Story(ref slug) => {
format!("GET /s/{}", ::std::str::from_utf8(&slug[..]).unwrap())
}
LobstersRequest::Login => String::from("POST /login"),
LobstersRequest::Logout => String::from("POST /logout"),
LobstersRequest::StoryVote(ref story, v) => format!(
"POST /stories/{}/{}",
::std::str::from_utf8(&story[..]).unwrap(),
match v {
Vote::Up => "upvote",
Vote::Down => "downvote",
},
),
LobstersRequest::CommentVote(ref comment, v) => format!(
"POST /comments/{}/{}",
::std::str::from_utf8(&comment[..]).unwrap(),
match v {
Vote::Up => "upvote",
Vote::Down => "downvote",
},
),
LobstersRequest::Submit { ref id, .. } => format!(
"POST /stories [{}]",
::std::str::from_utf8(&id[..]).unwrap(),
),
LobstersRequest::Comment {
ref id,
ref story,
ref parent,
} => match *parent {
Some(ref parent) => format!(
"POST /comments/{} [{}; {}]",
::std::str::from_utf8(&parent[..]).unwrap(),
::std::str::from_utf8(&id[..]).unwrap(),
::std::str::from_utf8(&story[..]).unwrap(),
),
None => format!(
"POST /comments [{}; {}]",
::std::str::from_utf8(&id[..]).unwrap(),
::std::str::from_utf8(&story[..]).unwrap(),
),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn textual_requests() {
assert_eq!(LobstersRequest::Frontpage.describe(), "GET /");
assert_eq!(LobstersRequest::Recent.describe(), "GET /recent");
assert_eq!(LobstersRequest::Comments.describe(), "GET /comments");
assert_eq!(LobstersRequest::User(3).describe(), "GET /u/#3");
assert_eq!(
LobstersRequest::Story([48, 48, 48, 48, 57, 97]).describe(),
"GET /s/00009a"
);
assert_eq!(LobstersRequest::Login.describe(), "POST /login");
assert_eq!(LobstersRequest::Logout.describe(), "POST /logout");
assert_eq!(
LobstersRequest::StoryVote([48, 48, 48, 98, 57, 97], Vote::Up).describe(),
"POST /stories/000b9a/upvote"
);
assert_eq!(
LobstersRequest::StoryVote([48, 48, 48, 98, 57, 97], Vote::Down).describe(),
"POST /stories/000b9a/downvote"
);
assert_eq!(
LobstersRequest::CommentVote([48, 48, 48, 98, 57, 97], Vote::Up).describe(),
"POST /comments/000b9a/upvote"
);
assert_eq!(
LobstersRequest::CommentVote([48, 48, 48, 98, 57, 97], Vote::Down).describe(),
"POST /comments/000b9a/downvote"
);
assert_eq!(
LobstersRequest::Submit {
id: [48, 48, 48, 48, 57, 97],
title: String::from("foo"),
}
.describe(),
"POST /stories [00009a]"
);
assert_eq!(
LobstersRequest::Comment {
id: [48, 48, 48, 48, 57, 97],
story: [48, 48, 48, 48, 57, 98],
parent: Some([48, 48, 48, 48, 57, 99]),
}
.describe(),
"POST /comments/00009c [00009a; 00009b]"
);
assert_eq!(
LobstersRequest::Comment {
id: [48, 48, 48, 48, 57, 97],
story: [48, 48, 48, 48, 57, 98],
parent: None,
}
.describe(),
"POST /comments [00009a; 00009b]"
);
}
}