[client-libraries] Add rust client library

This is an improved copy of the traits.rs file from fuchsia.

Bug: 42930
Change-Id: I321fa07c2f5f9c6690852c52168cf1e920567b30
diff --git a/cobaltb.py b/cobaltb.py
index b38b30e..834c05d 100755
--- a/cobaltb.py
+++ b/cobaltb.py
@@ -289,7 +289,7 @@
   # it represents. Note that 'cloud_bt' and 'perf' tests are special. They are
   # not included in 'all'. They are only run if asked for explicitly.
   FILTER_MAP = {
-      'all': ['cpp', 'go', 'other'],
+      'all': ['cpp', 'go', 'other', 'rust'],
       'cpp': ['cpp'],
       'go': ['go'],
       'perf': ['perf'],
diff --git a/src/lib/BUILD.gn b/src/lib/BUILD.gn
index c9bca95..082dd03 100644
--- a/src/lib/BUILD.gn
+++ b/src/lib/BUILD.gn
@@ -6,6 +6,7 @@
   testonly = true
   deps = [
     "clearcut:tests",
+    "client:tests",
     "crypto_util:tests",
     "util:tests",
   ]
diff --git a/src/lib/client/BUILD.gn b/src/lib/client/BUILD.gn
new file mode 100644
index 0000000..55a5a0c
--- /dev/null
+++ b/src/lib/client/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+group("tests") {
+  testonly = true
+  deps = [
+    "rust:tests",
+  ]
+}
diff --git a/src/lib/client/rust/BUILD.gn b/src/lib/client/rust/BUILD.gn
new file mode 100644
index 0000000..db7c7e8
--- /dev/null
+++ b/src/lib/client/rust/BUILD.gn
@@ -0,0 +1,16 @@
+import("//build/rust/rustc_library.gni")
+
+rustc_library("cobalt-client") {
+  name = "cobalt_client"
+  edition = "2018"
+  version = "0.1.0"
+
+  with_unit_tests = true
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    ":cobalt-client_test_build",
+  ]
+}
diff --git a/src/lib/client/rust/src/lib.rs b/src/lib/client/rust/src/lib.rs
new file mode 100644
index 0000000..fcaa8c7
--- /dev/null
+++ b/src/lib/client/rust/src/lib.rs
@@ -0,0 +1,9 @@
+// Copyright 2019 Zachary Bush.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub mod traits;
diff --git a/src/lib/client/rust/src/traits.rs b/src/lib/client/rust/src/traits.rs
new file mode 100644
index 0000000..d41a990
--- /dev/null
+++ b/src/lib/client/rust/src/traits.rs
@@ -0,0 +1,137 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//! Traits for use with the rust client library for cobalt.
+
+/// `AsEventCode` is any type that can be converted into a `u32` for the purposes of reporting to
+/// cobalt.
+pub trait AsEventCode {
+    fn as_event_code(&self) -> u32;
+}
+
+impl AsEventCode for u32 {
+    fn as_event_code(&self) -> u32 {
+        *self
+    }
+}
+
+/// `AsEventCodes` is any type that can be converted into a `Vec<u32>` for the purposes of reporting
+/// to cobalt.
+pub trait AsEventCodes {
+    /// Converts the source type into a `Vec<u32>` of event codes.
+    fn as_event_codes(&self) -> Vec<u32>;
+}
+
+impl AsEventCodes for () {
+    fn as_event_codes(&self) -> Vec<u32> {
+        Vec::new()
+    }
+}
+
+impl<A: AsEventCode> AsEventCodes for A {
+    fn as_event_codes(&self) -> Vec<u32> {
+        vec![self.as_event_code()]
+    }
+}
+
+impl<A: AsEventCode> AsEventCodes for Vec<A> {
+    fn as_event_codes(&self) -> Vec<u32> {
+        self.iter().map(AsEventCode::as_event_code).collect()
+    }
+}
+
+impl<A: AsEventCode> AsEventCodes for [A] {
+    fn as_event_codes(&self) -> Vec<u32> {
+        self.iter().map(AsEventCode::as_event_code).collect()
+    }
+}
+
+macro_rules! array_impls {
+    ($($N:expr)+) => {
+        $(
+            impl<A: AsEventCode> AsEventCodes for [A; $N] {
+                fn as_event_codes(&self) -> Vec<u32> {
+                    self[..].as_event_codes()
+                }
+            }
+        )+
+    }
+}
+
+array_impls! {0 1 2 3 4 5 6}
+
+macro_rules! tuple_impls {
+    ($(($($N:ident => $i:tt),*)),*) => {
+        $(
+            impl <$($N: AsEventCode),*> AsEventCodes for ($($N),*,) {
+                fn as_event_codes(&self) -> Vec<u32> {
+                    vec![$(
+                        self.$i.as_event_code()
+                    ),*]
+                }
+            }
+        )*
+    }
+}
+
+tuple_impls! {
+    (A => 0),
+    (A => 0, B => 1),
+    (A => 0, B => 1, C => 2),
+    (A => 0, B => 1, C => 2, D => 3),
+    (A => 0, B => 1, C => 2, D => 3, E => 4),
+    (A => 0, B => 1, C => 2, D => 3, E => 4, F => 5)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[derive(Clone, Copy)]
+    enum EventCode1 {
+        A = 1,
+        B = 2,
+    }
+
+    impl AsEventCode for EventCode1 {
+        fn as_event_code(&self) -> u32 {
+            *self as u32
+        }
+    }
+
+    #[derive(Clone, Copy)]
+    enum EventCode2 {
+        C = 3,
+        D = 4,
+    }
+
+    impl AsEventCode for EventCode2 {
+        fn as_event_code(&self) -> u32 {
+            *self as u32
+        }
+    }
+
+    #[test]
+    fn test_as_event_codes() {
+        assert_eq!(().as_event_codes(), vec![]);
+        assert_eq!(([] as [u32; 0]).as_event_codes(), vec![]);
+        assert_eq!(1.as_event_codes(), vec![1]);
+        assert_eq!([1].as_event_codes(), vec![1]);
+        assert_eq!(vec![1].as_event_codes(), vec![1]);
+        assert_eq!([1, 2].as_event_codes(), vec![1, 2]);
+        assert_eq!(vec![1, 2].as_event_codes(), vec![1, 2]);
+        assert_eq!([1, 2, 3].as_event_codes(), vec![1, 2, 3]);
+        assert_eq!(vec![1, 2, 3].as_event_codes(), vec![1, 2, 3]);
+        assert_eq!([1, 2, 3, 4].as_event_codes(), vec![1, 2, 3, 4]);
+        assert_eq!(vec![1, 2, 3, 4].as_event_codes(), vec![1, 2, 3, 4]);
+        assert_eq!([1, 2, 3, 4, 5].as_event_codes(), vec![1, 2, 3, 4, 5]);
+        assert_eq!(vec![1, 2, 3, 4, 5].as_event_codes(), vec![1, 2, 3, 4, 5]);
+
+        assert_eq!((EventCode1::A, EventCode2::C).as_event_codes(), vec![1, 3]);
+        assert_eq!(
+            (0, EventCode1::B, 10, EventCode2::D).as_event_codes(),
+            vec![0, 2, 10, 4]
+        );
+    }
+}