类型改为Result

This commit is contained in:
Jeremy Yin 2023-07-03 20:59:03 +08:00
parent 52b8d0a235
commit 499bdfa9fc
4 changed files with 117 additions and 38 deletions

View File

@ -6,7 +6,9 @@ use crate::repo::pokemon::Repository;
#[derive(Serialize)] #[derive(Serialize)]
struct Response{ struct Response{
message: String, number: u16,
name: String,
types: Vec<String>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
struct Request { struct Request {
@ -24,10 +26,19 @@ pub fn serve(repo: Arc<dyn Repository>, req: &rouille::Request) -> rouille::Resp
}, },
_ => return rouille::Response::from(Status::BadRequest), _ => return rouille::Response::from(Status::BadRequest),
}; };
// rouille::Response::from(Status::InternalServerError)
match create_pokemon::execute(repo, req) { match create_pokemon::execute(repo, req) {
create_pokemon::Response::Ok(number) => rouille::Response::json(&Response{message: number.to_string()}), Ok(create_pokemon::Response {
create_pokemon::Response::BadRequest => rouille::Response::from(Status::BadRequest), number,
create_pokemon::Response::Conflict => rouille::Response::from(Status::Conflict), name,
create_pokemon::Response::Error => rouille::Response::from(Status::InternalServerError), types,
}) => rouille::Response::json(&Response{
number,
name,
types,
}),
Err(create_pokemon::Error::BadRequest) => rouille::Response::from(Status::BadRequest),
Err(create_pokemon::Error::Conflict) => rouille::Response::from(Status::Conflict),
Err(create_pokemon::Error::Unknown) => rouille::Response::from(Status::InternalServerError),
} }
} }

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::domain::entity::{PokemonName, PokemonNumber, PokemonTypes}; use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes};
use crate::repo::pokemon::{Insert, Repository}; use crate::repo::pokemon::{InsertError, Repository};
pub struct Request { pub struct Request {
pub(crate) number: u16, pub(crate) number: u16,
@ -8,14 +8,19 @@ pub struct Request {
pub(crate) types: Vec<String>, pub(crate) types: Vec<String>,
} }
pub enum Response { pub enum Error {
Ok(u16),
BadRequest, BadRequest,
Conflict, Conflict,
Error, Unknown,
} }
pub fn execute(repo: Arc<dyn Repository>, req: Request) -> Response { pub struct Response {
pub number: u16,
pub name: String,
pub types: Vec<String>,
}
pub fn execute(repo: Arc<dyn Repository>, req: Request) -> Result<Response, Error> {
println!("execute"); println!("execute");
match ( match (
PokemonNumber::try_from(req.number), PokemonNumber::try_from(req.number),
@ -23,11 +28,19 @@ pub fn execute(repo: Arc<dyn Repository>, req: Request) -> Response {
PokemonTypes::try_from(req.types) PokemonTypes::try_from(req.types)
) { ) {
(Ok(number), Ok(name), Ok(types)) => match repo.insert(number, name, types){ (Ok(number), Ok(name), Ok(types)) => match repo.insert(number, name, types){
Insert::Ok(num) => Response::Ok(u16::from(num)), Ok(Pokemon {
Insert::Conflict => Response::Conflict, number,
Insert::Error => Response::Error, name,
types,
}) => Ok(Response{
number: u16::from(number),
name: String::from(name),
types: Vec::<String>::from(types),
}),
Err(InsertError::Conflict) => Err(Error::Conflict),
Err(InsertError::Error) => Err(Error::Unknown),
}, },
_ => Response::BadRequest, _ => Err(Error::BadRequest),
} }
} }
@ -37,7 +50,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn it_should_return_the_pokemon_number_otherwise() { fn it_should_return_an_unknown_error_when_an_unexpected_error_happens(){
let mut repo = Arc::new(InMemoryRepository::new()); let mut repo = Arc::new(InMemoryRepository::new());
let number = 25; let number = 25;
let req = Request { let req = Request {
@ -47,7 +60,27 @@ mod tests {
}; };
let res = execute(repo, req); let res = execute(repo, req);
match res { match res {
Response::Ok(num) => assert_eq!(num, number), Err(Error::Unknown) => {},
_ => unreachable!(),
}
}
#[test]
fn it_should_return_the_pokemon_number_otherwise() {
let repo = Arc::new(InMemoryRepository::new());
let num = 25;
let req = Request {
number: num,
name: "Pikachu".to_string(),
types: vec!["Electric".to_string(), ],
};
let res = execute(repo, req);
match res {
Ok(Response {
number,
name,
types,
}) => assert_eq!(number, num),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -55,16 +88,20 @@ mod tests {
#[test] #[test]
fn it_should_return_a_bad_request_error_when_request_is_invalid() { fn it_should_return_a_bad_request_error_when_request_is_invalid() {
let mut repo = Arc::new(InMemoryRepository::new()); let mut repo = Arc::new(InMemoryRepository::new());
let number = 25; let num = 25;
let req = Request { let req = Request {
number: number, number: num,
name: "".to_string(), name: "".to_string(),
types: vec!["Electric".to_string(), ], types: vec!["Electric".to_string(), ],
}; };
let res = execute(repo, req); let res = execute(repo, req);
match res { match res {
Response::Ok(num) => assert_eq!(num, number), Ok(Response{
Response::BadRequest => {}, number,
name,
types,
}) => assert_eq!(number, num),
Err(Error::BadRequest) => {},
_ => unreachable!() _ => unreachable!()
} }
} }
@ -83,7 +120,7 @@ mod tests {
}; };
let res = execute(repo, req); let res = execute(repo, req);
match res { match res {
Response::Conflict => {}, Err(Error::Conflict) => {},
_ => unreachable!(), _ => unreachable!(),
} }
} }

View File

@ -1,8 +1,8 @@
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Pokemon { pub struct Pokemon {
pub number: PokemonNumber, pub number: PokemonNumber,
name: PokemonName, pub name: PokemonName,
types: PokemonTypes, pub types: PokemonTypes,
} }
impl Pokemon { impl Pokemon {
@ -36,7 +36,7 @@ impl From<PokemonNumber> for u16 {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct PokemonName(String); pub struct PokemonName(String);
impl TryFrom<String> for PokemonName { impl TryFrom<String> for PokemonName {
@ -51,7 +51,13 @@ impl TryFrom<String> for PokemonName {
} }
} }
#[derive(Debug)] impl From<PokemonName> for String {
fn from(value: PokemonName) -> Self {
value.0
}
}
#[derive(Debug, Clone)]
pub struct PokemonTypes(Vec<PokemonType>); pub struct PokemonTypes(Vec<PokemonType>);
impl TryFrom<Vec<String>> for PokemonTypes { impl TryFrom<Vec<String>> for PokemonTypes {
@ -73,7 +79,26 @@ impl TryFrom<Vec<String>> for PokemonTypes {
} }
} }
#[derive(Debug)] impl From<PokemonTypes> for Vec<String> {
fn from(value: PokemonTypes) -> Self {
let mut ts = vec![];
for pt in value.0.into_iter() {
ts.push(String::from(pt));
}
ts
}
}
impl From<PokemonType> for String {
fn from(value: PokemonType) -> Self {
String::from(match value {
PokemonType::Electric => "Electric",
PokemonType::Fire => "Fire",
})
}
}
#[derive(Debug, Clone)]
enum PokemonType { enum PokemonType {
Electric, Electric,
Fire, Fire,

View File

@ -2,7 +2,7 @@ use std::sync::Mutex;
use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes}; use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes};
pub trait Repository: Send + Sync { pub trait Repository: Send + Sync {
fn insert(&self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Insert; fn insert(&self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Result<Pokemon, InsertError>;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct InMemoryRepository { pub struct InMemoryRepository {
@ -17,32 +17,38 @@ impl InMemoryRepository {
pokemons pokemons
} }
} }
#[cfg(test)]
pub fn with_error(self) -> Self {
Self {
error: true,
..self
}
}
} }
pub enum Insert { pub enum InsertError {
Ok(PokemonNumber),
Conflict, Conflict,
Error, Error,
} }
impl Repository for InMemoryRepository { impl Repository for InMemoryRepository {
fn insert(&self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Insert { fn insert(&self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Result<Pokemon, InsertError> {
if self.error { if self.error {
return Insert::Error; return Err(InsertError::Error);
} }
let mut lock = match self.pokemons.lock() { let mut lock = match self.pokemons.lock() {
Ok(lock) => lock, Ok(lock) => lock,
_ => return Insert::Error, _ => return Err(InsertError::Error),
}; };
println!("{number:?} {:?}", self.pokemons); println!("{number:?} {:?}", self.pokemons);
if lock.iter().any(|pokemon| pokemon.number == number) { if lock.iter().any(|pokemon| pokemon.number == number) {
return Insert::Conflict; return Err(InsertError::Conflict);
} }
let number_clone = number.clone(); let number_clone = number.clone();
lock.push(Pokemon::new(number_clone, name, types)); let pokemon = Pokemon::new(number_clone, name, types);
lock.push(pokemon.clone());
println!("{number:?} {:?}", self.pokemons); println!("{number:?} {:?}", self.pokemons);
Insert::Ok(number) Ok(pokemon)
} }
} }