diff --git a/influxdb2-derive/src/expand_writable.rs b/influxdb2-derive/src/expand_writable.rs index e037fa9..3f6b00a 100644 --- a/influxdb2-derive/src/expand_writable.rs +++ b/influxdb2-derive/src/expand_writable.rs @@ -56,15 +56,17 @@ pub fn impl_writeable(tokens: TokenStream) -> TokenStream { let ident_str = ident.to_string(); let kind = f.kind.clone(); Some(quote! { - w.write_all(format!("{}", #ident_str).as_bytes())?; - w.write_all(b"=")?; - w.write_all(<#kind as #writable_krate::KeyWritable>::encode_key(&self.#ident).into_bytes().as_slice())?; + if <#kind as #writable_krate::KeyWritable>::encode_key(&self.#ident) != "None"{ + w.write_all(b",")?; + w.write_all(format!("{}", #ident_str).as_bytes())?; + w.write_all(b"=")?; + w.write_all(<#kind as #writable_krate::KeyWritable>::encode_key(&self.#ident).into_bytes().as_slice())?; + } }) } _ => None, }) .collect(); - let fields_writes: Vec = fields .iter() .filter_map(|f| match f.field_type { @@ -73,9 +75,15 @@ pub fn impl_writeable(tokens: TokenStream) -> TokenStream { let ident_str = ident.to_string(); let kind = f.kind.clone(); Some(quote! { - w.write_all(format!("{}", #ident_str).as_bytes())?; - w.write_all(b"=")?; - w.write_all(<#kind as #writable_krate::ValueWritable>::encode_value(&self.#ident).into_bytes().as_slice())?; + if <#kind as #writable_krate::ValueWritable>::encode_value(&self.#ident) != "None" { + if first_field_write == false { + w.write_all(b",")?; + } + w.write_all(format!("{}", #ident_str).as_bytes())?; + w.write_all(b"=")?; + w.write_all(<#kind as #writable_krate::ValueWritable>::encode_value(&self.#ident).into_bytes().as_slice())?; + first_field_write = false; + } }) } _ => None, @@ -89,6 +97,7 @@ pub fn impl_writeable(tokens: TokenStream) -> TokenStream { let ident = f.ident.clone(); let kind = f.kind.clone(); Some(quote! { + w.write_all(b" ")?; w.write_all(<#kind as #writable_krate::TimestampWritable>::encode_timestamp(&self.#ident).into_bytes().as_slice())?; }) } @@ -96,48 +105,30 @@ pub fn impl_writeable(tokens: TokenStream) -> TokenStream { }) .collect(); - if tag_writes.len() < 1 { - panic!("You have to specify at least one #[tag] field.") - } - if timestamp_writes.len() != 1 { - panic!("You have to specify at exact one #[timestamp] field.") + if timestamp_writes.len() > 1 { + panic!("You have to specify at most one #[timestamp] field.") } if fields_writes.len() < 1 { panic!("You have to specify at least one #[field] field.") } - let mut combined_tag_writes = vec![]; - for (index, tag_write) in tag_writes.iter().enumerate() { - if index > 0 { - combined_tag_writes.push(quote!(w.write_all(b",")?;)); - } - combined_tag_writes.push(tag_write.clone()); - } - - let mut combined_fields_writes = vec![]; - for (index, fields_write) in fields_writes.iter().enumerate() { - if index > 0 { - combined_fields_writes.push(quote!(w.write_all(b",")?;)); - } - combined_fields_writes.push(fields_write.clone()); - } - let output = quote! { impl #generics #krate::models::WriteDataPoint for #ident #generics { fn write_data_point_to(&self,mut w: W) -> std::io::Result<()> where W: std::io::Write{ - w.write_all(format!("{},", #measure).as_bytes())?; + w.write_all(format!("{}", #measure).as_bytes())?; #( - #combined_tag_writes + #tag_writes )* w.write_all(b" ")?; + let mut first_field_write = true; #( - #combined_fields_writes + #fields_writes )* - w.write_all(b" ")?; + #( #timestamp_writes )* diff --git a/influxdb2-derive/tests/writable.rs b/influxdb2-derive/tests/writable.rs index 4ab3ea7..5062f18 100644 --- a/influxdb2-derive/tests/writable.rs +++ b/influxdb2-derive/tests/writable.rs @@ -9,7 +9,9 @@ struct Item { name2: String, #[influxdb(field)] field1: u64, + #[influxdb(field)] field2: i64, + #[influxdb(field)] field3: String, #[influxdb(timestamp)] time: u64, @@ -19,15 +21,114 @@ struct Item { #[measurement = "something"] struct Item2 { #[influxdb(tag)] - name: Option, + name: String, #[influxdb(tag)] - name2: Option, + name2: String, #[influxdb(field)] field1: Option, - field2: i64, + #[influxdb(field)] + field2: Option, #[influxdb(timestamp)] time: u64, } + +#[derive(WriteDataPoint)] +#[measurement = "barfoo"] +struct Item3 { + #[influxdb(tag)] + name: String, + #[influxdb(field)] + field1: Option, + #[influxdb(field)] + field2: Option, + #[influxdb(field)] + field3: Option, + #[influxdb(field)] + field4: Option, + #[influxdb(field)] + field5: Option, + #[influxdb(timestamp)] + time: u64, +} + +#[derive(WriteDataPoint)] +#[measurement = "barfoo2"] +struct Item4 { + #[influxdb(tag)] + tag1: Option, + #[influxdb(tag)] + tag2: Option, + #[influxdb(tag)] + tag3: Option, + #[influxdb(tag)] + tag4: Option, + #[influxdb(tag)] + tag5: Option, + #[influxdb(field)] + field1: String, + #[influxdb(timestamp)] + time: u64, +} + + +#[derive(WriteDataPoint)] +#[measurement = "foobar"] +struct Item5 { + #[influxdb(tag)] + tag1: Option, + #[influxdb(tag)] + tag2: Option, + #[influxdb(tag)] + tag3: Option, + #[influxdb(tag)] + tag4: Option, + #[influxdb(tag)] + tag5: Option, + #[influxdb(field)] + field1: Option, + #[influxdb(field)] + field2: Option, + #[influxdb(field)] + field3: Option, + #[influxdb(field)] + field4: Option, + #[influxdb(field)] + field5: Option, + #[influxdb(timestamp)] + time: u64, +} + +#[derive(WriteDataPoint)] +#[measurement = "allTagsNone"] +struct Item6 { + #[influxdb(tag)] + tag1: Option, + #[influxdb(tag)] + tag2: Option, + #[influxdb(field)] + field1: String, + #[influxdb(timestamp)] + time: u64, +} + +#[derive(WriteDataPoint)] +#[measurement = "noTags"] +struct Item7 { + #[influxdb(field)] + field1: String, + #[influxdb(timestamp)] + time: u64, +} + +#[derive(WriteDataPoint)] +#[measurement = "noTimestamp"] +struct Item8 { + #[influxdb(tag)] + tag1: String, + #[influxdb(field)] + field1: String, +} + fn main() { use influxdb2::models::WriteDataPoint; use std::io::Write; @@ -37,32 +138,137 @@ fn main() { field1: 32u64, field2: 33i64, field3: "hello".to_string(), - time: 222222u64, + time: 222233u64, }; let mut writer = Vec::new(); item.write_data_point_to(&mut writer).unwrap(); writer.flush().unwrap(); - println!("{}", std::str::from_utf8(&writer).unwrap()); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); assert_eq!( &writer[..], - b"something,name=foo,name2=bar field1=32u,field2=33i,field3=\"hello\" 222222\n" + b"something,name=foo,name2=bar field1=32u,field2=33i,field3=\"hello\" 222233\n" ); let item = Item2 { - name: Some("foo".to_string()), - name2: None, + name: "foo".to_string(), + name2: "bar".to_string(), field1: None, - field2: 33i64, + field2: Some(33i64), time: 222222u64, }; let mut writer = Vec::new(); item.write_data_point_to(&mut writer).unwrap(); writer.flush().unwrap(); - println!("{}", std::str::from_utf8(&writer).unwrap()); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); assert_eq!( &writer[..], - b"something,name=foo,name2=None field1=\"None\",field2=33i 222222\n" - ) + b"something,name=foo,name2=bar field2=33i 222222\n" + ); + + let item = Item3 { + name: "foo".to_string(), + field1: None, + field2: None, + field3: Some(12.34), + field4: None, + field5: None, + time: 222222u64, + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"barfoo,name=foo field3=12.34 222222\n" + ); + + let item = Item4 { + tag1: None, + tag2: None, + tag3: Some("thisIsATag".to_string()), + tag4: None, + tag5: None, + field1: "asdf".to_string(), + time: 222222u64, + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"barfoo2,tag3=thisIsATag field1=\"asdf\" 222222\n" + ); + + let item = Item5 { + tag1: None, + tag2: None, + tag3: Some("thisIsATag".to_string()), + tag4: None, + tag5: None, + field1: None, + field2: None, + field3: Some(12.34), + field4: None, + field5: None, + time: 222222u64, + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"foobar,tag3=thisIsATag field3=12.34 222222\n" + ); + + let item = Item6 { + tag1: None, + tag2: None, + field1: "abc".to_string(), + time: 122222u64 + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"allTagsNone field1=\"abc\" 122222\n" + ); + + let item = Item7 { + field1: "def".to_string(), + time: 122222u64, + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"noTags field1=\"def\" 122222\n" + ); + + let item = Item8 { + tag1: "abc".to_string(), + field1: "def".to_string(), + }; + + let mut writer = Vec::new(); + item.write_data_point_to(&mut writer).unwrap(); + writer.flush().unwrap(); + println!("Writer: {}", std::str::from_utf8(&writer).unwrap()); + assert_eq!( + &writer[..], + b"noTimestamp,tag1=abc field1=\"def\"\n" + ); } diff --git a/src/writable.rs b/src/writable.rs index 236c177..d4cd813 100644 --- a/src/writable.rs +++ b/src/writable.rs @@ -77,7 +77,7 @@ impl ValueWritable for Option { fn encode_value(&self) -> String { match self { Some(v) => v.encode_value(), - None => "\"None\"".to_string(), + None => "None".to_string(), } } } @@ -197,6 +197,18 @@ mod tests { assert_eq!(a.encode_value(), "33i") } + #[test] + fn value_writable_option_f64_not_none() { + let a: Option = Some(78.4); + assert_eq!(a.encode_value(), "78.4"); + } + + #[test] + fn value_writable_option_f64_none() { + let a: Option = None; + assert_eq!(a.encode_value(), "None"); + } + #[test] fn tags_tuple() { let a: (&str, &str) = ("33", "str");