use crate::abi;
use fastly_shared::XqdStatus;
use lazy_static::lazy_static;
use regex::Regex;
use thiserror::Error;
#[derive(Eq, Hash, PartialEq)]
pub struct Endpoint {
handle: u32,
name: String,
}
impl std::fmt::Debug for Endpoint {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("Endpoint")
.field("name", &self.name)
.finish()
}
}
#[derive(Copy, Clone, Debug, Error, PartialEq, Eq)]
pub enum LogError {
#[error("endpoint not found, or is reserved")]
InvalidEndpoint,
#[error("malformed endpoint name")]
MalformedEndpointName,
}
impl std::convert::TryFrom<&str> for Endpoint {
type Error = LogError;
fn try_from(name: &str) -> Result<Self, Self::Error> {
Self::try_from_name(name)
}
}
impl std::convert::TryFrom<String> for Endpoint {
type Error = LogError;
fn try_from(name: String) -> Result<Self, Self::Error> {
Self::try_from_name(&name)
}
}
impl std::io::Write for Endpoint {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut nwritten = 0;
let status =
unsafe { abi::xqd_log_write(self.handle(), buf.as_ptr(), buf.len(), &mut nwritten) };
match status {
XqdStatus::OK => Ok(nwritten),
XqdStatus::BADF => Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"xqd_log_write failed: invalid log endpoint handle",
)),
XqdStatus::BUFLEN => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"xqd_log_write failed: log line too long",
)),
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("xqd_log_write failed: {:?}", status),
)),
}
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Endpoint {
pub(crate) unsafe fn handle(&self) -> u32 {
self.handle
}
pub fn from_name(name: &str) -> Self {
Self::try_from_name(name).unwrap()
}
pub fn try_from_name(name: &str) -> Result<Self, LogError> {
validate_endpoint_name(name)?;
let mut handle = 0u32;
let status = unsafe { abi::xqd_log_endpoint_get(name.as_ptr(), name.len(), &mut handle) };
match status {
XqdStatus::OK => Ok(Endpoint {
handle,
name: name.to_owned(),
}),
XqdStatus::INVAL => Err(LogError::InvalidEndpoint),
_ => panic!("xqd_log_endpoint_get failed"),
}
}
}
fn validate_endpoint_name(name: &str) -> Result<(), LogError> {
lazy_static! {
static ref VALID_ENDPOINT_RE: Regex = Regex::new(r"^[^\n:]*$").unwrap();
}
match name {
"" => Err(LogError::MalformedEndpointName),
name if !VALID_ENDPOINT_RE.is_match(name) => Err(LogError::MalformedEndpointName),
_ => Ok(()),
}
}