use core::ffi::{c_char, c_int, CStr};
use core::fmt;
use nwep_sys as sys;
#[repr(i32)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Method {
Read = 0,
Write = 1,
Update = 2,
Delete = 3,
Heartbeat = 6,
Head = 7,
}
impl Method {
pub fn code(self) -> i32 {
self as i32
}
pub fn as_str(self) -> &'static str {
static_token(unsafe { sys::nwep_method_str(self as c_int) })
}
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[repr(i32)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Status {
Ok = 0,
Created = 1,
Accepted = 2,
NoContent = 3,
PartialContent = 4,
Moved = 5,
NotModified = 6,
BadRequest = 7,
Unauthorized = 8,
Forbidden = 9,
NotFound = 10,
NotAllowed = 11,
Conflict = 12,
Gone = 13,
TooLarge = 14,
PreconditionFailed = 15,
RangeNotSatisfiable = 16,
RateLimited = 17,
Error = 18,
Unavailable = 19,
Timeout = 20,
NotImplemented = 21,
}
impl Status {
pub fn code(self) -> i32 {
self as i32
}
pub fn as_str(self) -> &'static str {
static_token(unsafe { sys::nwep_status_str(self as c_int) })
}
pub fn from_token(token: &str) -> Status {
use Status::*;
match token {
"ok" => Ok,
"created" => Created,
"accepted" => Accepted,
"no-content" => NoContent,
"partial-content" => PartialContent,
"moved" => Moved,
"not-modified" => NotModified,
"bad-request" => BadRequest,
"unauthorized" => Unauthorized,
"forbidden" => Forbidden,
"not-found" => NotFound,
"not-allowed" => NotAllowed,
"conflict" => Conflict,
"gone" => Gone,
"too-large" => TooLarge,
"precondition-failed" => PreconditionFailed,
"range-not-satisfiable" => RangeNotSatisfiable,
"rate-limited" => RateLimited,
"unavailable" => Unavailable,
"timeout" => Timeout,
"not-implemented" => NotImplemented,
_ => Error,
}
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
fn static_token(ptr: *const c_char) -> &'static str {
debug_assert!(!ptr.is_null(), "a known method or status index has a token");
unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or("error")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn method_tokens_and_codes() {
assert_eq!(Method::Read.as_str(), "read");
assert_eq!(Method::Head.as_str(), "head");
assert_eq!(Method::Heartbeat.code(), 6);
assert_eq!(Method::Head.code(), 7);
assert_eq!(Method::Delete.to_string(), "delete");
}
#[test]
fn status_tokens_round_trip() {
assert_eq!(Status::NotFound.as_str(), "not-found");
assert_eq!(Status::NotAllowed.as_str(), "not-allowed");
assert_eq!(Status::Gone.as_str(), "gone");
assert_eq!(Status::TooLarge.as_str(), "too-large");
assert_eq!(Status::PreconditionFailed.as_str(), "precondition-failed");
assert_eq!(Status::Timeout.as_str(), "timeout");
assert_eq!(Status::NotImplemented.as_str(), "not-implemented");
assert_eq!(Status::Moved.as_str(), "moved");
assert_eq!(
Status::RangeNotSatisfiable.as_str(),
"range-not-satisfiable"
);
assert_eq!(Status::from_token("not-found"), Status::NotFound);
assert_eq!(Status::from_token("moved"), Status::Moved);
assert_eq!(Status::from_token("gone"), Status::Gone);
assert_eq!(Status::from_token("timeout"), Status::Timeout);
assert_eq!(
Status::from_token("not-implemented"),
Status::NotImplemented
);
assert_eq!(
Status::from_token("partial-content"),
Status::PartialContent
);
}
#[test]
fn unknown_status_token_degrades_to_error() {
assert_eq!(Status::from_token("teapot"), Status::Error);
}
}