1. Create the project using cargo
  2. List all the dependencies in Cargo.toml
  3. Set up the web app in src/main.rs:
use messages::MessageApp;

fn main() -> std::io::Result<()> {
    std::env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let app = MessageApp::new(8080);

    app.run() 
}

In a new file src/lib.rs, create the MessageApp class:

#[macro_use]
extern crate actix_web;

use actix_web::{middleware, web, App, HttpRequest, HttpServer, Result};
use serde::Serialize;

#[derive(Serialize)]
struct IndexResponse {
    message: String,
}

pub struct MessageApp {
    port: u16,
}

impl MessageApp {
    pub fn new(port: u16) -> Self {
        MessageApp { port }
    }

    pub fn run(&self) -> std::io::Result<()> {
        println!("Starting http server: 127.0.0.1:{}", self.port);
        HttpServer::new(move || {
            App::new()
                .wrap(middleware::Logger::default())
                .service(index)
        })
        .bind(("127.0.0.1", self.port))?
        .workers(8)
        .run()
    }
}

#[get("/")]
fn index(req: HttpRequest) -> Result<web::Json<IndexResponse>> {
    let hello = req
        .headers()
        .get("hello")
        .and_then(|v| v.to_str().ok())
        .unwrap_or_else(|| "world");

    Ok(web::Json(IndexResponse {
        message: hello.to_owned(),
    }))
}

The MessageApp class

Associated function and instance method

Rust closure

HttpServer::new(
	move || {
		App::new()
		.wrap(middleware::Logger::default())
		.service(index)
	}
)

The move keyword in a closure

The move keyword is … used to allow the closure to outlive the captured values, such as if the closure is being returned or used to spawn a new thread.
The Rust Reference

The ? operator

Makes error handling more pleasant by reducing the visual noise involved. Instead of an entire match statement, now we are just using the single ? character to indicate that here we are handling errors in the standard way, by passing them up the call stack.

The Edition Guide

    pub fn run(&self) -> Result {
        println!();
        HttpServer::new(move || {
        })

        .bind(("127.0.0.1", self.port))?  👈🏻👀

        .workers(8)
        .run()
    }

The handler index

Summary

The web app has one simple functionality so far. It listens for a GET request with a hello header. If it receives such request, it sends back a response consisting of the value of the hello header. However, if it receives non-hello header, the value of the response will be world.

Source code on GitHub: github.com/islandjoe/messages