h10 0.6.17

Simple HTTP/1.0 Server
Documentation
use std::marker::PhantomData;

use crate::http::{
    headers::{ContentLength, IntoHeader},
    method::Method,
    url_path::UrlPath,
    version::Version,
};

use super::{Body, Headers, QsEntry, QueryString, Request};

pub trait RequestBuilderState {}

#[derive(Debug)]
pub struct WithBody;

#[derive(Debug)]
pub struct WithOutBody;

impl RequestBuilderState for WithBody {}

impl RequestBuilderState for WithOutBody {}

#[derive(Debug)]
pub struct RequestBuilderDelete;

impl RequestBuilderDelete {
    pub fn path(self, path: UrlPath) -> Step3<WithOutBody> {
        let http_version = Version::Http1_0;
        let method = Method::Delete;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderGet;
impl RequestBuilderGet {
    pub fn path(self, path: UrlPath) -> Step3<WithOutBody> {
        let http_version = Version::Http1_0;
        let method = Method::Get;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderHead;

impl RequestBuilderHead {
    pub fn path(self, path: UrlPath) -> Step3<WithOutBody> {
        let http_version = Version::Http1_0;
        let method = Method::Head;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderLink;

impl RequestBuilderLink {
    pub fn path(self, path: UrlPath) -> Step3<WithOutBody> {
        let http_version = Version::Http1_0;
        let method = Method::Link;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderPost;

impl RequestBuilderPost {
    pub fn path(self, path: UrlPath) -> Step3<WithBody> {
        let http_version = Version::Http1_0;
        let method = Method::Post;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderPut;

impl RequestBuilderPut {
    pub fn path(self, path: UrlPath) -> Step3<WithBody> {
        let http_version = Version::Http1_0;
        let method = Method::Put;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct RequestBuilderUnlink;

impl RequestBuilderUnlink {
    pub fn path(self, path: UrlPath) -> Step3<WithOutBody> {
        let http_version = Version::Http1_0;
        let method = Method::Unlink;

        Step3 {
            http_version,
            method,
            path,
            query_string: QueryString::empty(),
            headers: Headers::default_for_request(),
            _state: PhantomData,
        }
    }
}

#[derive(Debug)]
pub struct Step3<B: RequestBuilderState> {
    http_version: Version,
    method: Method,
    path: UrlPath,
    query_string: QueryString,
    headers: Headers,
    _state: PhantomData<B>,
}

impl Step3<WithOutBody> {
    pub fn add_qs_entry(self, qs_entry: QsEntry) -> Step3<WithOutBody> {
        let Self {
            http_version,
            method,
            path,
            mut query_string,
            headers,
            ..
        } = self;

        query_string.add(qs_entry);

        Step3 {
            http_version,
            method,
            path,
            query_string,
            headers,
            _state: PhantomData,
        }
    }
    pub fn add_header<H: IntoHeader>(self, header: H) -> Step3<WithOutBody> {
        let Self {
            http_version,
            method,
            path,
            query_string,
            mut headers,
            ..
        } = self;

        headers.add(header.into_header());

        Step3 {
            http_version,
            method,
            path,
            query_string,
            headers,
            _state: PhantomData,
        }
    }

    pub fn finish(self) -> Request {
        let Self {
            http_version,
            method,
            path,
            query_string,
            headers,
            ..
        } = self;

        Request {
            http_version,
            method,
            path,
            query_string,
            headers,
            body: None,
        }
    }
}

impl Step3<WithBody> {
    pub fn add_qs_entry(self, qs_entry: QsEntry) -> Step3<WithBody> {
        let Self {
            http_version,
            method,
            path,
            mut query_string,
            headers,
            ..
        } = self;

        query_string.add(qs_entry);

        Step3 {
            http_version,
            method,
            path,
            query_string,
            headers,
            _state: PhantomData,
        }
    }
    pub fn add_header<H: IntoHeader>(self, header: H) -> Step3<WithBody> {
        let Self {
            http_version,
            method,
            path,
            query_string,
            mut headers,
            ..
        } = self;

        headers.add(header.into_header());

        Step3 {
            http_version,
            method,
            path,
            query_string,
            headers,
            _state: PhantomData,
        }
    }
    pub fn set_body(self, body: Body) -> Step4 {
        let Self {
            http_version,
            method,
            path,
            query_string,
            mut headers,
            ..
        } = self;

        headers.add(ContentLength::length((*body).len()).into_header());

        Step4 {
            http_version,
            method,
            path,
            query_string,
            headers,
            body: Some(body),
        }
    }
}

#[derive(Debug)]
pub struct Step4 {
    http_version: Version,
    method: Method,
    path: UrlPath,
    query_string: QueryString,
    headers: Headers,
    body: Option<Body>,
}
impl Step4 {
    pub fn finish(self) -> Request {
        let Self {
            http_version,
            method,
            path,
            query_string,
            headers,
            body,
        } = self;

        Request {
            http_version,
            method,
            path,
            query_string,
            headers,
            body,
        }
    }
}