prax 0.1.1

a web penetration proxy hosted in neovim
Documentation
use std::str::FromStr;

use http::{uri::PathAndQuery, HeaderName, HeaderValue, Uri};
use hyper::Method;

use super::Attr;

#[derive(thiserror::Error, Debug)]
pub enum AttrError {
    #[error("Value not utf8")]
    Str(#[from] std::str::Utf8Error),

    #[error("Value not utf8")]
    String(#[from] std::string::FromUtf8Error),

    #[error("{0}")]
    Method(#[from] http::method::InvalidMethod),

    #[error("")]
    Uri(#[from] http::uri::InvalidUri),

    #[error("{0}")]
    HeaderName(#[from] http::header::InvalidHeaderName),

    #[error("{0}")]
    HeaderValue(#[from] http::header::InvalidHeaderValue),
}

pub trait Attributable {
    fn set(&mut self, attr: &Attr, value: Vec<u8>) -> Result<(), AttrError>;
}

impl Attributable for hyper::Request<Vec<u8>> {
    fn set(&mut self, attr: &Attr, value: Vec<u8>) -> Result<(), AttrError> {
        match attr {
            super::Attr::Method => {
                let update = Method::from_bytes(&value)?;

                *self.method_mut() = update;
            }
            Attr::Status => {}
            Attr::Path => {
                let value = String::from_utf8(value)?;

                let mut parts = self.uri().clone().into_parts();
                let pq = if let Some(pq) = parts.path_and_query {
                    if let Some(query) = pq.query() {
                        PathAndQuery::from_str(&format!("{}?{}", value, query))?
                    } else {
                        PathAndQuery::from_str(&value)?
                    }
                } else {
                    PathAndQuery::from_str(&value)?
                };

                parts.path_and_query = Some(pq);

                *self.uri_mut() = Uri::from_parts(parts).unwrap();
            }
            Attr::Query(key) => {
                let value = String::from_utf8(value)?;

                let val = if value.is_empty() {
                    "".to_string()
                } else {
                    format!("={value}")
                };

                let mut parts = self.uri().clone().into_parts();
                let pq = if let Some(pq) = parts.path_and_query {
                    if let Some(query) = pq.query() {
                        if query.is_empty() {
                            PathAndQuery::from_str(&format!("{}?{}{}", value, key, value))?
                        } else {
                            PathAndQuery::from_str(&format!("{}?{}&{}{}", value, query, key, val))?
                        }
                    } else {
                        PathAndQuery::from_str(&format!("{}?{}{}", pq.path(), key, val))?
                    }
                } else {
                    PathAndQuery::from_str(&format!("/?{key}{val}"))?
                };

                parts.path_and_query = Some(pq);

                *self.uri_mut() = Uri::from_parts(parts).unwrap();
            }
            Attr::Header(key) => {
                let header = HeaderValue::from_bytes(&value)?;

                self.headers_mut()
                    .insert(HeaderName::from_bytes(key.as_bytes())?, header);
            }
            Attr::Body => {
                *self.body_mut() = value;
            }
        }

        Ok(())
    }
}