类型改为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)]
struct Response{
message: String,
number: u16,
name: String,
types: Vec<String>,
}
#[derive(Deserialize)]
struct Request {
@ -24,10 +26,19 @@ pub fn serve(repo: Arc<dyn Repository>, req: &rouille::Request) -> rouille::Resp
},
_ => return rouille::Response::from(Status::BadRequest),
};
// rouille::Response::from(Status::InternalServerError)
match create_pokemon::execute(repo, req) {
create_pokemon::Response::Ok(number) => rouille::Response::json(&Response{message: number.to_string()}),
create_pokemon::Response::BadRequest => rouille::Response::from(Status::BadRequest),
create_pokemon::Response::Conflict => rouille::Response::from(Status::Conflict),
create_pokemon::Response::Error => rouille::Response::from(Status::InternalServerError),
Ok(create_pokemon::Response {
number,
name,
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 crate::domain::entity::{PokemonName, PokemonNumber, PokemonTypes};
use crate::repo::pokemon::{Insert, Repository};
use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes};
use crate::repo::pokemon::{InsertError, Repository};
pub struct Request {
pub(crate) number: u16,
@ -8,14 +8,19 @@ pub struct Request {
pub(crate) types: Vec<String>,
}
pub enum Response {
Ok(u16),
pub enum Error {
BadRequest,
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");
match (
PokemonNumber::try_from(req.number),
@ -23,11 +28,19 @@ pub fn execute(repo: Arc<dyn Repository>, req: Request) -> Response {
PokemonTypes::try_from(req.types)
) {
(Ok(number), Ok(name), Ok(types)) => match repo.insert(number, name, types){
Insert::Ok(num) => Response::Ok(u16::from(num)),
Insert::Conflict => Response::Conflict,
Insert::Error => Response::Error,
Ok(Pokemon {
number,
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::*;
#[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 number = 25;
let req = Request {
@ -47,7 +60,27 @@ mod tests {
};
let res = execute(repo, req);
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!(),
}
}
@ -55,16 +88,20 @@ mod tests {
#[test]
fn it_should_return_a_bad_request_error_when_request_is_invalid() {
let mut repo = Arc::new(InMemoryRepository::new());
let number = 25;
let num = 25;
let req = Request {
number: number,
number: num,
name: "".to_string(),
types: vec!["Electric".to_string(), ],
};
let res = execute(repo, req);
match res {
Response::Ok(num) => assert_eq!(num, number),
Response::BadRequest => {},
Ok(Response{
number,
name,
types,
}) => assert_eq!(number, num),
Err(Error::BadRequest) => {},
_ => unreachable!()
}
}
@ -83,7 +120,7 @@ mod tests {
};
let res = execute(repo, req);
match res {
Response::Conflict => {},
Err(Error::Conflict) => {},
_ => unreachable!(),
}
}

View File

@ -1,8 +1,8 @@
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Pokemon {
pub number: PokemonNumber,
name: PokemonName,
types: PokemonTypes,
pub name: PokemonName,
pub types: PokemonTypes,
}
impl Pokemon {
@ -36,7 +36,7 @@ impl From<PokemonNumber> for u16 {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct PokemonName(String);
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>);
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 {
Electric,
Fire,

View File

@ -2,7 +2,7 @@ use std::sync::Mutex;
use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes};
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)]
pub struct InMemoryRepository {
@ -17,32 +17,38 @@ impl InMemoryRepository {
pokemons
}
}
#[cfg(test)]
pub fn with_error(self) -> Self {
Self {
error: true,
..self
}
}
}
pub enum Insert {
Ok(PokemonNumber),
pub enum InsertError {
Conflict,
Error,
}
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 {
return Insert::Error;
return Err(InsertError::Error);
}
let mut lock = match self.pokemons.lock() {
Ok(lock) => lock,
_ => return Insert::Error,
_ => return Err(InsertError::Error),
};
println!("{number:?} {:?}", self.pokemons);
if lock.iter().any(|pokemon| pokemon.number == number) {
return Insert::Conflict;
return Err(InsertError::Conflict);
}
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);
Insert::Ok(number)
Ok(pokemon)
}
}