├── .dockerignore ├── .gitignore ├── Cargo.toml ├── Dockerfile ├── README.md ├── docker-compose.yml ├── nightly └── Dockerfile └── src └── main.rs /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | **/*.rs.bk 4 | files 5 | *.db 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-microservice" 3 | version = "0.1.0" 4 | authors = ["Gene Kuo "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | hyper = "0.12" 9 | futures = "0.1" 10 | slab = "0.4.2" 11 | lazy_static = "1.4.0" 12 | regex = "1.4.2" 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:nightly 2 | 3 | RUN USER=root cargo new --bin rust-microservices 4 | WORKDIR /rust-microservices 5 | COPY ./Cargo.toml ./Cargo.toml 6 | RUN cargo build 7 | 8 | RUN rm src/*.rs 9 | COPY ./src ./src 10 | RUN rm ./target/debug/deps/rust_microservice* 11 | RUN cargo build 12 | 13 | CMD ["./target/debug/rust-microservice"] 14 | 15 | EXPOSE 8080 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-microservices 2 | Microservices with Rust 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | rust-microservice: 5 | build: . 6 | ports: 7 | - "8080:8080" 8 | -------------------------------------------------------------------------------- /nightly/Dockerfile: -------------------------------------------------------------------------------- 1 | # https://github.com/rust-lang-nursery/docker-rust-nightly/blob/master/nightly/Dockerfile 2 | FROM buildpack-deps:stretch 3 | 4 | ENV RUSTUP_HOME=/usr/local/rustup \ 5 | CARGO_HOME=/usr/local/cargo \ 6 | PATH=/usr/local/cargo/bin:$PATH 7 | 8 | RUN set -eux; \ 9 | url="https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init"; \ 10 | wget "$url"; \ 11 | chmod +x rustup-init; \ 12 | ./rustup-init -y --no-modify-path --default-toolchain nightly; \ 13 | rm rustup-init; \ 14 | chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \ 15 | rustup --version; \ 16 | cargo --version; \ 17 | rustc --version; 18 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::{Arc, Mutex}; 3 | use slab::Slab; 4 | use lazy_static::lazy_static; 5 | use futures::{future, Future}; 6 | use hyper::{Body, Error, Method, Request, Response, Server, StatusCode}; 7 | use hyper::service::service_fn; 8 | use regex::Regex; 9 | 10 | type UserId = u64; 11 | 12 | struct UserData; 13 | 14 | impl fmt::Display for UserData { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | f.write_str("{}") 17 | } 18 | } 19 | 20 | type UserDb = Arc>>; 21 | 22 | lazy_static! { 23 | static ref USER_PATH: Regex = Regex::new("^/user/((?P\\d+?)/?)?$").unwrap(); 24 | static ref USERS_PATH: Regex = Regex::new("^/users/?$").unwrap(); 25 | } 26 | 27 | fn user_handler(req: Request, user_db: &UserDb) 28 | -> impl Future, Error=Error> { 29 | 30 | let response = { 31 | let method = req.method(); 32 | let path = req.uri().path(); 33 | let mut users = user_db.lock().unwrap(); 34 | if USERS_PATH.is_match(path) { 35 | if method == &Method::GET { 36 | let list = users.iter() 37 | .map(|(id, _)| id.to_string()) 38 | .collect::>() 39 | .join(","); 40 | Response::new(list.into()) 41 | } else { 42 | response_status(StatusCode::METHOD_NOT_ALLOWED) 43 | } 44 | } else if let Some(cap) = USER_PATH.captures(path) { 45 | let user_id = cap.name("user_id").and_then(|m| { 46 | m.as_str() 47 | .parse::() 48 | .ok() 49 | .map(|x| x as usize) 50 | }); 51 | match (method, user_id) { 52 | (&Method::GET, Some(id)) => { 53 | if let Some(data) = users.get(id) { 54 | Response::new(data.to_string().into()) 55 | } else { 56 | response_status(StatusCode::NOT_FOUND) 57 | } 58 | }, 59 | (&Method::POST, None) => { 60 | let id = users.insert(UserData); 61 | Response::new(id.to_string().into()) 62 | }, 63 | (&Method::POST, Some(_)) => { 64 | response_status(StatusCode::BAD_REQUEST) 65 | }, 66 | (&Method::PUT, Some(id)) => { 67 | if let Some(user) = users.get_mut(id) { 68 | *user = UserData; 69 | response_status(StatusCode::OK) 70 | } else { 71 | response_status(StatusCode::NOT_FOUND) 72 | } 73 | }, 74 | (&Method::DELETE, Some(id)) => { 75 | if users.contains(id) { 76 | users.remove(id); 77 | response_status(StatusCode::OK) 78 | } else { 79 | response_status(StatusCode::NOT_FOUND) 80 | } 81 | }, 82 | _ => { 83 | response_status(StatusCode::METHOD_NOT_ALLOWED) 84 | }, 85 | } 86 | } else { 87 | response_status(StatusCode::NOT_FOUND) 88 | } 89 | }; 90 | future::ok(response) 91 | } 92 | 93 | fn response_status(status_code: StatusCode) -> Response { 94 | Response::builder() 95 | .status(status_code) 96 | .body(Body::empty()) 97 | .unwrap() 98 | } 99 | 100 | fn main() { 101 | let addr = ([0, 0, 0, 0], 8080).into(); 102 | let builder = Server::bind(&addr); 103 | let user_db: UserDb = Arc::new(Mutex::new(Slab::new())); 104 | let server = builder.serve(move || { 105 | let user_db = user_db.clone(); 106 | service_fn(move |req| user_handler(req, &user_db)) 107 | }); 108 | let server = server.map_err(drop); 109 | hyper::rt::run(server); 110 | } 111 | --------------------------------------------------------------------------------