尝试rust里面实现架构设计
This commit is contained in:
commit
a46b2e066b
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.idea
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pokemon"
|
||||||
|
version = "0.1.0"
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "pokemon"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::domain::entity::{PokemonName, PokemonNumber, PokemonTypes};
|
||||||
|
use crate::repo::pokemon::Repository;
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
number: u16,
|
||||||
|
name: String,
|
||||||
|
types: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Response {
|
||||||
|
Ok(u16),
|
||||||
|
BadRequest,
|
||||||
|
Conflict,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(repo: &mut dyn Repository, req: Request) -> Response {
|
||||||
|
match (
|
||||||
|
PokemonNumber::try_from(req.number),
|
||||||
|
PokemonName::try_from(req.name),
|
||||||
|
PokemonTypes::try_from(req.types)
|
||||||
|
) {
|
||||||
|
(Ok(number), Ok(_), Ok(_)) => Response::Ok(u16::from(number)),
|
||||||
|
_ => Response::BadRequest,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::repo::pokemon::InMemoryRepository;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_should_return_the_pokemon_number_otherwise() {
|
||||||
|
let mut repo = InMemoryRepository::new();
|
||||||
|
let number = 25;
|
||||||
|
let req = Request {
|
||||||
|
number: number,
|
||||||
|
name: "Pikachu".to_string(),
|
||||||
|
types: vec!["Electric".to_string(), ],
|
||||||
|
};
|
||||||
|
let res = execute(&mut repo, req);
|
||||||
|
match res {
|
||||||
|
Response::Ok(num) => assert_eq!(num, number),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_should_return_a_bad_request_error_when_request_is_invalid() {
|
||||||
|
let mut repo = InMemoryRepository::new();
|
||||||
|
let number = 25;
|
||||||
|
let req = Request {
|
||||||
|
number: number,
|
||||||
|
name: "".to_string(),
|
||||||
|
types: vec!["Electric".to_string(), ],
|
||||||
|
};
|
||||||
|
let res = execute(&mut repo, req);
|
||||||
|
match res {
|
||||||
|
Response::Ok(num) => assert_eq!(num, number),
|
||||||
|
Response::BadRequest => {},
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_should_return_a_conflict_error_when_pokemon_number_already_exists() {
|
||||||
|
let number = PokemonNumber::try_from(25).unwrap();
|
||||||
|
let name = PokemonName::try_from("Pikachu".to_string()).unwrap();
|
||||||
|
let types = PokemonTypes::try_from(vec!["Electric".to_string(),]).unwrap();
|
||||||
|
let mut repo = InMemoryRepository::new();
|
||||||
|
repo.insert(number, name, types);
|
||||||
|
let req = Request {
|
||||||
|
number: 25,
|
||||||
|
name: "Charmander".to_string(),
|
||||||
|
types: vec!["File".to_string()],
|
||||||
|
};
|
||||||
|
let res = execute(&mut repo, req);
|
||||||
|
match res {
|
||||||
|
Response::Conflict => {},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
pub struct Pokemon {
|
||||||
|
pub number: PokemonNumber,
|
||||||
|
name: PokemonName,
|
||||||
|
types: PokemonTypes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pokemon {
|
||||||
|
pub fn new(number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Self {
|
||||||
|
Self {
|
||||||
|
number,
|
||||||
|
name,
|
||||||
|
types,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PokemonNumber(u16);
|
||||||
|
|
||||||
|
impl TryFrom<u16> for PokemonNumber {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||||
|
if value > 0 && value < 899 {
|
||||||
|
Ok(Self(value))
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PokemonNumber> for u16 {
|
||||||
|
fn from(p: PokemonNumber) -> Self {
|
||||||
|
p.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PokemonName(String);
|
||||||
|
|
||||||
|
impl TryFrom<String> for PokemonName {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
if value.is_empty() {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(Self(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PokemonTypes(Vec<PokemonType>);
|
||||||
|
|
||||||
|
impl TryFrom<Vec<String>> for PokemonTypes {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: Vec<String>) -> Result<Self, Self::Error> {
|
||||||
|
if value.is_empty() {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
let mut pts = vec![];
|
||||||
|
for t in value.iter() {
|
||||||
|
match PokemonType::try_from(t.to_string()) {
|
||||||
|
Ok(pt) => pts.push(pt),
|
||||||
|
_ => return Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self(pts))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PokemonType {
|
||||||
|
Electric,
|
||||||
|
Fire,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for PokemonType {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
match value.as_str() {
|
||||||
|
"Electric" => Ok(Self::Electric),
|
||||||
|
"Fire" => Ok(Self::Fire),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod create_pokemon;
|
||||||
|
pub mod entity;
|
|
@ -0,0 +1,8 @@
|
||||||
|
mod repo;
|
||||||
|
mod domain;
|
||||||
|
mod cli;
|
||||||
|
mod api;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod pokemon;
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::domain::entity::{Pokemon, PokemonName, PokemonNumber, PokemonTypes};
|
||||||
|
|
||||||
|
pub trait Repository {
|
||||||
|
fn insert(&self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> PokemonNumber;
|
||||||
|
}
|
||||||
|
pub struct InMemoryRepository {
|
||||||
|
pokemons: Vec<Pokemon>
|
||||||
|
}
|
||||||
|
impl InMemoryRepository {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let pokemons: Vec<Pokemon> = vec![];
|
||||||
|
Self {
|
||||||
|
pokemons
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Insert {
|
||||||
|
Ok(PokemonNumber),
|
||||||
|
Conflict,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Repository for InMemoryRepository {
|
||||||
|
fn insert(&mut self, number: PokemonNumber, name: PokemonName, types: PokemonTypes) -> Insert {
|
||||||
|
if self.pokemons.iter().any(|pokemon| pokemon.number == number) {
|
||||||
|
return Insert::Conflict;
|
||||||
|
}
|
||||||
|
let number_clone = number.clone();
|
||||||
|
self.pokemons.push(Pokemon::new(number_clone, name, types));
|
||||||
|
Insert::Ok(number)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue