1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::{
	HttpError, Header, RequestHeader, ResponseHeader,
	data::{
		Data,
		encodings::{ Ascii, HeaderFieldKey, Uri }
	}
};
use std::{ collections::HashMap, convert::TryFrom };


/// A HTTP-response header builder
pub struct RequestBuilder {
	method: Option<Data<Ascii>>,
	uri: Option<Data<Uri>>,
	version: Option<Data<Ascii>>,
	header_fields: HashMap<Data<HeaderFieldKey>, Data<Ascii>>
}
impl RequestBuilder {
	/// Creates a new request builder
	pub fn new() -> Self {
		Self{ method: None, uri: None, version: None, header_fields: HashMap::new() }
	}
	
	/// Sets the request method
	pub fn method(mut self, method: Data<Ascii>) -> Self {
		self.method = Some(method);
		self
	}
	/// Sets the request URI
	pub fn uri(mut self, uri: Data<Uri>) -> Self {
		self.uri = Some(uri);
		self
	}
	/// Sets the HTTP version
	pub fn version(mut self, version: Data<Ascii>) -> Self {
		self.version = Some(version);
		self
	}
	
	/// Inserts a header field with `key`-`value`
	pub fn field(mut self, key: Data<HeaderFieldKey>, value: Data<Ascii>) -> Self {
		self.header_fields.insert(key, value);
		self
	}
	
	/// Builds the request header
	pub fn build(self) -> Result<RequestHeader, HttpError> {
		// Unwrap status fields
		let method = self.method.ok_or(HttpError::ApiMisuse)?;
		let uri = self.uri.ok_or(HttpError::ApiMisuse)?;
		let version = self.version.ok_or(HttpError::ApiMisuse)?;
		
		// Convert the URI into a generic ASCII field
		let uri_ascii: Data<Ascii> = Data::try_from(&uri as &[u8])?;
		Ok(RequestHeader {
			header: Header {
				status_line: (method, uri_ascii, version),
				fields: self.header_fields
			}, uri
		})
	}
}


/// A HTTP-response header builder
pub struct ResponseBuilder {
	version: Option<Data<Ascii>>,
	status: Option<u16>,
	reason: Option<Data<Ascii>>,
	header_fields: HashMap<Data<HeaderFieldKey>, Data<Ascii>>
}
impl ResponseBuilder {
	/// Creates a new response builder
	pub fn new() -> Self {
		Self{ version: None, status: None, reason: None, header_fields: HashMap::new() }
	}
	
	/// Sets the HTTP version to the literal `version`
	pub fn version(mut self, version: Data<Ascii>) -> Self {
		self.version = Some(version);
		self
	}
	/// Sets the status code
	pub fn status(mut self, status: u16) -> Self {
		self.status = Some(status);
		self
	}
	/// Sets the status reason
	pub fn reason(mut self, info: Data<Ascii>) -> Self {
		self.reason = Some(info);
		self
	}
	
	/// Inserts a header field with `key`-`value`
	pub fn field(mut self, key: Data<HeaderFieldKey>, value: Data<Ascii>) -> Self {
		self.header_fields.insert(key, value);
		self
	}
	
	/// Builds the response header
	pub fn build(self) -> Result<ResponseHeader, HttpError> {
		// Unwrap status fields
		let version = self.version.ok_or(HttpError::ApiMisuse)?;
		let status = self.status.ok_or(HttpError::ApiMisuse)?;
		let reason = self.reason.ok_or(HttpError::ApiMisuse)?;
		
		// Convert the status integer into a generic ASCII field
		let status_ascii: Data<Ascii> = Data::try_from(status.to_string().into_bytes())
			.expect("Should never fail because all number literals are a subset of ASCII");
		Ok(ResponseHeader {
			header: Header {
				status_line: (version, status_ascii, reason),
				fields: self.header_fields
			}, status
		})
	}
}