use crate::errors::{Error, Result};
use std::default;
use std::fmt;
use url::Url;
#[derive(
Debug, Clone, PartialEq, Eq, Hash, simd_json_derive::Serialize, simd_json_derive::Deserialize,
)]
pub struct EventOriginUri {
pub scheme: String,
pub host: String,
pub port: Option<u16>,
pub path: Vec<String>,
}
impl EventOriginUri {
pub fn parse(url: &str) -> Result<Self> {
match Url::parse(url) {
Ok(r) => {
let host = r
.host_str()
.ok_or_else(|| Error::from("EventOriginUri Parse Error: Missing host"))?;
Ok(Self {
scheme: r.scheme().to_string(),
host: host.to_string(),
port: r.port(),
path: r
.path_segments()
.map_or_else(Vec::new, |segs| segs.map(String::from).collect()),
})
}
Err(e) => Err(e.into()),
}
}
#[must_use]
pub fn scheme(&self) -> &str {
&self.scheme
}
#[must_use]
pub fn host(&self) -> &str {
&self.host
}
#[must_use]
pub fn port(&self) -> Option<u16> {
self.port
}
#[must_use]
pub fn path(&self) -> &[String] {
&self.path
}
#[must_use]
pub fn host_port(&self) -> String {
if let Some(port) = self.port() {
format!("{}:{}", self.host(), port)
} else {
self.host().to_string()
}
}
}
impl fmt::Display for EventOriginUri {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}://{}", self.scheme, self.host)?;
if let Some(port) = self.port {
write!(f, ":{}", port)?;
}
let maybe_sep = if self.path.is_empty() { "" } else { "/" };
write!(f, "{}{}", maybe_sep, self.path.join("/"))
}
}
impl default::Default for EventOriginUri {
fn default() -> Self {
Self {
scheme: "tremor-script".to_string(),
host: "localhost".to_string(),
port: None,
path: Vec::new(),
}
}
}
pub(crate) const NO_CONTEXT: EventContext<'static> = EventContext {
at: 0,
origin_uri: None,
panic_on_assert: false,
cardinality: 0,
};
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, simd_json_derive::Serialize)]
pub struct EventContext<'run> {
at: u64,
pub origin_uri: Option<&'run EventOriginUri>,
pub panic_on_assert: bool,
pub cardinality: usize,
}
impl<'run> EventContext<'run> {
#[must_use]
pub fn new(ingest_ns: u64, origin_uri: Option<&'run EventOriginUri>) -> Self {
Self {
at: ingest_ns,
origin_uri,
panic_on_assert: false,
cardinality: 0,
}
}
#[must_use]
pub fn ingest_ns(&self) -> u64 {
self.at
}
#[must_use]
pub fn origin_uri(&self) -> Option<&EventOriginUri> {
self.origin_uri
}
}
#[cfg(test)]
mod tests {
use super::{EventContext, EventOriginUri};
#[test]
fn valid_event_origin_uris() {
let eouri = EventOriginUri::parse("protocol://the.host.name").expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "the.host.name");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "the.host.name");
assert!(eouri.path().is_empty());
assert_eq!(eouri.to_string(), "protocol://the.host.name");
let eouri = EventOriginUri::parse("protocol://192.168.1.1").expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "192.168.1.1");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "192.168.1.1");
assert!(eouri.path().is_empty());
assert_eq!(eouri.to_string(), "protocol://192.168.1.1");
let eouri = EventOriginUri::parse("protocol://the.host.name:8080").expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "the.host.name");
assert_eq!(eouri.port(), Some(8080));
assert_eq!(eouri.host_port().as_str(), "the.host.name:8080");
assert!(eouri.path().is_empty());
assert_eq!(eouri.to_string(), "protocol://the.host.name:8080");
let eouri = EventOriginUri::parse("protocol://the.host.name/").expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "the.host.name");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "the.host.name");
assert_eq!(eouri.path(), &[""]);
assert_eq!(eouri.to_string(), "protocol://the.host.name/");
let eouri = EventOriginUri::parse("protocol://the.host.name/some/path/segments")
.expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "the.host.name");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "the.host.name");
assert_eq!(eouri.path(), &["some", "path", "segments"]);
assert_eq!(
eouri.to_string(),
"protocol://the.host.name/some/path/segments"
);
let eouri = EventOriginUri::parse("protocol://the.host.name/some/path/segments/")
.expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "the.host.name");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "the.host.name");
assert_eq!(eouri.path(), &["some", "path", "segments", ""]);
assert_eq!(
eouri.to_string(),
"protocol://the.host.name/some/path/segments/"
);
let eouri = EventOriginUri::parse("protocol://host.names.are.🔥").expect("Valid URI");
assert_eq!(eouri.scheme(), "protocol");
assert_eq!(eouri.host(), "host.names.are.%F0%9F%94%A5"); assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "host.names.are.%F0%9F%94%A5");
assert!(eouri.path().is_empty());
assert_eq!(eouri.to_string(), "protocol://host.names.are.%F0%9F%94%A5");
let eouri = EventOriginUri::default();
assert_eq!(eouri.scheme(), "tremor-script");
assert_eq!(eouri.host(), "localhost");
assert_eq!(eouri.port(), None);
assert_eq!(eouri.host_port().as_str(), "localhost");
assert!(eouri.path().is_empty());
assert_eq!(eouri.to_string(), "tremor-script://localhost");
}
#[test]
fn invalid_event_origin_uris() {
let err = EventOriginUri::parse("protocol:///the.host.name").expect_err("Invalid URI");
assert_eq!(
err.description(),
"EventOriginUri Parse Error: Missing host"
);
let err = EventOriginUri::parse("protocol:/the.host.name").expect_err("Invalid URI");
assert_eq!(
err.description(),
"EventOriginUri Parse Error: Missing host"
);
let err = EventOriginUri::parse("protocol://the.host.name:66000").expect_err("Invalid URI");
assert_eq!(err.description(), "Url Parse Error: invalid port number");
let err = EventOriginUri::parse("protocol://oops.a space").expect_err("Invalid URI");
assert_eq!(
err.description(),
"Url Parse Error: invalid domain character"
);
}
#[test]
fn event_context() {
let ctx = EventContext::default();
assert_eq!(ctx.ingest_ns(), 0);
assert_eq!(ctx.origin_uri(), None, "Default has no origin URI");
}
}