Skip to content

Instantly share code, notes, and snippets.

@cogas
Last active July 28, 2017 13:51
Show Gist options
  • Save cogas/a9261bf62c9fd7037d985d8d334cb3c7 to your computer and use it in GitHub Desktop.
Save cogas/a9261bf62c9fd7037d985d8d334cb3c7 to your computer and use it in GitHub Desktop.
damepo.rs
// TRPL 17.3 Listing 17-18 のちょい下の3つの拡張問題の1つ目(Draft以外では投稿不能)を実装しようとしてのこれ
// https://doc.rust-lang.org/book/second-edition/ch17-03-oo-design-patterns.html
pub struct Post {
state: Option<Box<State>>,
content: String,
}
impl Post {
pub fn new() -> Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
pub fn add_text(&mut self, text: &str) {
let state = self.state.as_ref().unwrap();
state.add_text(self, text);
}
pub fn content(&self) -> &str {
self.state.as_ref().unwrap().content(&self)
}
pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review())
}
}
pub fn approve(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.approve())
}
}
pub fn reject(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.reject())
}
}
pub fn state(&self) -> String {
self.state.as_ref().unwrap().state()
}
}
trait State {
fn request_review(self: Box<Self>) -> Box<State>;
fn approve(self: Box<Self>) -> Box<State>;
fn content<'a>(&self, post: &'a Post) -> &'a str {
""
}
fn add_text(&self, post: &mut Post, text: &str) {}
fn reject(self: Box<Self>) -> Box<State>;
fn state(&self) -> String;
}
struct Draft {}
impl State for Draft {
fn request_review(self: Box<Self>) -> Box<State> {
Box::new(PendingReview { count: 0 })
}
fn approve(self: Box<Self>) -> Box<State> {
self
}
fn add_text(&self, post: &mut Post, text: &str) {
post.content.push_str(text)
}
fn reject(self: Box<Self>) -> Box<State> {
self
}
fn state(&self) -> String {
String::from("Draft")
}
}
struct PendingReview {
count: u8,
}
impl State for PendingReview {
fn request_review(self: Box<Self>) -> Box<State> {
self
}
fn approve(self: Box<Self>) -> Box<State> {
if self.count == 1 {
Box::new(Published {})
} else {
Box::new(PendingReview { count: 1 })
}
}
fn reject(self: Box<Self>) -> Box<State> {
Box::new(Draft {})
}
fn state(&self) -> String {
String::from("PendingReview")
}
}
struct Published {}
impl State for Published {
fn request_review(self: Box<Self>) -> Box<State> {
self
}
fn approve(self: Box<Self>) -> Box<State> {
self
}
fn content<'a>(&self, post: &'a Post) -> &'a str {
&post.content
}
fn reject(self: Box<Self>) -> Box<State> {
self
}
fn state(&self) -> String {
String::from("Published")
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_works() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
post.request_review();
assert_eq!("", post.content());
post.approve();
post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
#[test]
fn rejected() {
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content());
post.request_review();
assert_eq!(String::from("PendingReview"), post.state());
post.reject();
assert_eq!(String::from("Draft"), post.state());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment