From a46b2e066bfdc4f0b9bbb244522ba8d05a915169 Mon Sep 17 00:00:00 2001 From: Jeremy Yin Date: Mon, 13 Feb 2023 23:00:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95rust=E9=87=8C=E9=9D=A2?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=9E=B6=E6=9E=84=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + Cargo.lock | 7 +++ Cargo.toml | 8 ++++ src/api/mod.rs | 0 src/cli/mod.rs | 0 src/domain/create_pokemon.rs | 83 ++++++++++++++++++++++++++++++++++ src/domain/entity.rs | 87 ++++++++++++++++++++++++++++++++++++ src/domain/mod.rs | 2 + src/main.rs | 8 ++++ src/repo/mod.rs | 1 + src/repo/pokemon.rs | 32 +++++++++++++ 11 files changed, 230 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/api/mod.rs create mode 100644 src/cli/mod.rs create mode 100644 src/domain/create_pokemon.rs create mode 100644 src/domain/entity.rs create mode 100644 src/domain/mod.rs create mode 100644 src/main.rs create mode 100644 src/repo/mod.rs create mode 100644 src/repo/pokemon.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a8cabc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8d36a61 --- /dev/null +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bb6c581 --- /dev/null +++ b/Cargo.toml @@ -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] diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/domain/create_pokemon.rs b/src/domain/create_pokemon.rs new file mode 100644 index 0000000..460808b --- /dev/null +++ b/src/domain/create_pokemon.rs @@ -0,0 +1,83 @@ +use crate::domain::entity::{PokemonName, PokemonNumber, PokemonTypes}; +use crate::repo::pokemon::Repository; + +struct Request { + number: u16, + name: String, + types: Vec, +} + +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!(), + } + } +} \ No newline at end of file diff --git a/src/domain/entity.rs b/src/domain/entity.rs new file mode 100644 index 0000000..fb443fe --- /dev/null +++ b/src/domain/entity.rs @@ -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 for PokemonNumber { + type Error = (); + + fn try_from(value: u16) -> Result { + if value > 0 && value < 899 { + Ok(Self(value)) + } else { + Err(()) + } + } +} + +impl From for u16 { + fn from(p: PokemonNumber) -> Self { + p.0 + } +} + +pub struct PokemonName(String); + +impl TryFrom for PokemonName { + type Error = (); + + fn try_from(value: String) -> Result { + if value.is_empty() { + Err(()) + } else { + Ok(Self(value)) + } + } +} + +pub struct PokemonTypes(Vec); + +impl TryFrom> for PokemonTypes { + type Error = (); + + fn try_from(value: Vec) -> Result { + 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 for PokemonType { + type Error = (); + + fn try_from(value: String) -> Result { + match value.as_str() { + "Electric" => Ok(Self::Electric), + "Fire" => Ok(Self::Fire), + _ => Err(()) + } + } +} diff --git a/src/domain/mod.rs b/src/domain/mod.rs new file mode 100644 index 0000000..fd01c4e --- /dev/null +++ b/src/domain/mod.rs @@ -0,0 +1,2 @@ +pub mod create_pokemon; +pub mod entity; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9c93b55 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,8 @@ +mod repo; +mod domain; +mod cli; +mod api; + +fn main() { + println!("Hello, world!"); +} diff --git a/src/repo/mod.rs b/src/repo/mod.rs new file mode 100644 index 0000000..14f51f6 --- /dev/null +++ b/src/repo/mod.rs @@ -0,0 +1 @@ +pub mod pokemon; \ No newline at end of file diff --git a/src/repo/pokemon.rs b/src/repo/pokemon.rs new file mode 100644 index 0000000..b54c138 --- /dev/null +++ b/src/repo/pokemon.rs @@ -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 +} +impl InMemoryRepository { + pub fn new() -> Self { + let pokemons: Vec = 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) + } +} \ No newline at end of file