#[derive(Debug)]
pub struct LineProtocol(String);
pub trait IntoFieldData {
fn field_data(&self) -> FieldData;
}
#[derive(Debug, Clone, PartialEq)]
pub enum FieldData {
Boolean(bool),
UNumber(u64),
Number(i64),
Float(f64),
Str(String),
}
#[derive(Debug)]
pub enum Attr {
Tag(Tag),
Field(Field),
Timestamp(Timestamp),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Tag {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Field {
pub name: String,
pub value: FieldData,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Timestamp {
pub value: u64,
}
impl LineProtocol {
pub fn new(
measurement: String,
tags: Option<String>,
fields: String,
timestamp: Option<String>,
) -> Self {
let mut lp = match tags {
Some(t) => format!("{},{} {}", measurement, t, fields),
None => format!("{} {}", measurement, fields),
};
if let Some(ts) = timestamp {
lp.push_str(&format!(" {}", ts));
}
lp.push('\n');
Self(lp)
}
pub fn to_str(&self) -> &str {
&self.0
}
}
impl IntoFieldData for bool {
fn field_data(&self) -> FieldData {
FieldData::Boolean(*self)
}
}
impl IntoFieldData for u8 {
fn field_data(&self) -> FieldData {
FieldData::UNumber(*self as u64)
}
}
impl IntoFieldData for u16 {
fn field_data(&self) -> FieldData {
FieldData::UNumber(*self as u64)
}
}
impl IntoFieldData for u32 {
fn field_data(&self) -> FieldData {
FieldData::UNumber(*self as u64)
}
}
impl IntoFieldData for u64 {
fn field_data(&self) -> FieldData {
FieldData::UNumber(*self)
}
}
impl IntoFieldData for i8 {
fn field_data(&self) -> FieldData {
FieldData::Number(*self as i64)
}
}
impl IntoFieldData for i16 {
fn field_data(&self) -> FieldData {
FieldData::Number(*self as i64)
}
}
impl IntoFieldData for i32 {
fn field_data(&self) -> FieldData {
FieldData::Number(*self as i64)
}
}
impl IntoFieldData for i64 {
fn field_data(&self) -> FieldData {
FieldData::Number(*self)
}
}
impl IntoFieldData for f32 {
fn field_data(&self) -> FieldData {
FieldData::Float(*self as f64)
}
}
impl IntoFieldData for f64 {
fn field_data(&self) -> FieldData {
FieldData::Float(*self)
}
}
impl IntoFieldData for &str {
fn field_data(&self) -> FieldData {
FieldData::Str(String::from(*self))
}
}
impl IntoFieldData for String {
fn field_data(&self) -> FieldData {
FieldData::Str(self.to_string())
}
}
pub fn get_field_string(value: &FieldData) -> String {
match value {
FieldData::Boolean(b) => format!("{}", b),
FieldData::UNumber(n) => format!("{}u", n),
FieldData::Number(n) => format!("{}i", n),
FieldData::Float(f) => format!("{}", f),
FieldData::Str(s) => format!(r#""{}""#, s),
}
}
pub fn format_attr(attrs: Vec<Attr>) -> String {
let mut out: Vec<String> = attrs
.into_iter()
.map(|a| match a {
Attr::Tag(t) => format!("{}={}", escape_spaces(&t.name), escape_spaces(&t.value)),
Attr::Field(f) => format!("{}={}", escape_spaces(&f.name), get_field_string(&f.value)),
Attr::Timestamp(t) => format!("{}", t.value),
})
.collect();
out.sort();
out.join(",")
}
fn escape_spaces(s: &str) -> String {
s.replace(' ', r#"\ "#)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_get_string_field_string() {
let s = get_field_string(&FieldData::Str(String::from("hello")));
assert_eq!(s, String::from(r#""hello""#));
}
#[test]
fn can_get_bool_field_string() {
let s1 = get_field_string(&FieldData::Boolean(true));
let s2 = get_field_string(&FieldData::Boolean(false));
assert_eq!(s1, String::from("true"));
assert_eq!(s2, String::from("false"));
}
#[test]
fn can_get_uint_field_string() {
let s = get_field_string(&FieldData::UNumber(10));
assert_eq!(s, String::from("10u"));
}
#[test]
fn can_get_signed_int_field_string() {
let s1 = get_field_string(&FieldData::Number(10));
let s2 = get_field_string(&FieldData::Number(-10));
assert_eq!(s1, String::from("10i"));
assert_eq!(s2, String::from("-10i"));
}
#[test]
fn can_format_field_attr() {
let v1: Vec<Attr> = vec![
Attr::Field(Field {
name: String::from("f1"),
value: FieldData::Number(1),
}),
Attr::Field(Field {
name: String::from("f2"),
value: FieldData::Number(2),
}),
];
let v2: Vec<Attr> = vec![
Attr::Field(Field {
name: String::from("f1"),
value: FieldData::Number(1),
}),
Attr::Field(Field {
name: String::from("f2"),
value: FieldData::Str(String::from("2")),
}),
];
assert_eq!(format_attr(v1), String::from("f1=1i,f2=2i"));
assert_eq!(format_attr(v2), String::from("f1=1i,f2=\"2\""));
}
}