Event Data Structure (#11)
* Started sketching the Event data structure * Implemented 1.0 Attributes * Fixed data read/write methods * Added extensions creators * Fixed doc example * Fixed github ci * Fixed Data creation from base64 * Updated meta Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
parent
e91622efdb
commit
5d280c4d33
|
@ -1,6 +1,12 @@
|
|||
name: Rust
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -10,12 +16,10 @@ jobs:
|
|||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all-features
|
||||
toolchain: stable
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all-features
|
||||
toolchain: stable
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
|
|
@ -1,5 +1,290 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "cloudevents-rust-sdk"
|
||||
version = "0.1.0"
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudevents"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"delegate",
|
||||
"hostname",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29017a50a5f59055b6edd22a8da6a764582edbe24fba9d953abf84be68dc2049"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winutil",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "winutil"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -1,9 +1,20 @@
|
|||
[package]
|
||||
name = "cloudevents-rust-sdk"
|
||||
version = "0.1.0"
|
||||
name = "cloudevents"
|
||||
version = "0.0.1"
|
||||
authors = ["Francesco Guardiani <francescoguard@gmail.com>"]
|
||||
license-file = "LICENSE"
|
||||
edition = "2018"
|
||||
description = "CloudEvents official Rust SDK"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/cloudevents/sdk-rust"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "^1.0", features = ["derive"] }
|
||||
serde_json = "^1.0"
|
||||
chrono = { version = "^0.4", features = ["serde"] }
|
||||
delegate = "^0.4"
|
||||
uuid = { version = "^0.8", features = ["serde", "v4"] }
|
||||
hostname = "^0.1"
|
||||
base64 = "^0.12"
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
use super::SpecVersion;
|
||||
use crate::event::{AttributesV10, ExtensionValue};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
|
||||
/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
|
||||
pub trait AttributesReader {
|
||||
/// Get the [id](https://github.com/cloudevents/spec/blob/master/spec.md#id).
|
||||
fn get_id(&self) -> &str;
|
||||
/// Get the [source](https://github.com/cloudevents/spec/blob/master/spec.md#source-1).
|
||||
fn get_source(&self) -> &str;
|
||||
/// Get the [specversion](https://github.com/cloudevents/spec/blob/master/spec.md#specversion).
|
||||
fn get_specversion(&self) -> SpecVersion;
|
||||
/// Get the [type](https://github.com/cloudevents/spec/blob/master/spec.md#type).
|
||||
fn get_type(&self) -> &str;
|
||||
/// Get the [datacontenttype](https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype).
|
||||
fn get_datacontenttype(&self) -> Option<&str>;
|
||||
/// Get the [dataschema](https://github.com/cloudevents/spec/blob/master/spec.md#dataschema).
|
||||
fn get_dataschema(&self) -> Option<&str>;
|
||||
/// Get the [subject](https://github.com/cloudevents/spec/blob/master/spec.md#subject).
|
||||
fn get_subject(&self) -> Option<&str>;
|
||||
/// Get the [time](https://github.com/cloudevents/spec/blob/master/spec.md#time).
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>>;
|
||||
/// Get the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name`
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue>;
|
||||
/// Get all the [extensions](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes)
|
||||
fn get_extensions(&self) -> Vec<(&str, &ExtensionValue)>;
|
||||
}
|
||||
|
||||
pub trait AttributesWriter {
|
||||
fn set_id(&mut self, id: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_type(&mut self, ty: impl Into<String>);
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>);
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
);
|
||||
fn remove_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
) -> Option<ExtensionValue>;
|
||||
}
|
||||
|
||||
pub(crate) trait DataAttributesWriter {
|
||||
fn set_datacontenttype(
|
||||
&mut self,
|
||||
datacontenttype: Option<impl Into<String>>,
|
||||
);
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>);
|
||||
}
|
||||
|
||||
pub enum Attributes {
|
||||
V10(AttributesV10),
|
||||
}
|
||||
|
||||
impl AttributesReader for Attributes {
|
||||
fn get_id(&self) -> &str {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_source(&self) -> &str {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_source(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_specversion(&self) -> SpecVersion {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_specversion(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self) -> &str {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_datacontenttype(&self) -> Option<&str> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_datacontenttype(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dataschema(&self) -> Option<&str> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_dataschema(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_subject(&self) -> Option<&str> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_subject(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_time(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_extension(extension_name),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_extensions(&self) -> Vec<(&str, &ExtensionValue)> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.get_extensions(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributesWriter for Attributes {
|
||||
fn set_id(&mut self, id: impl Into<String>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_id(id),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_source(&mut self, source: impl Into<String>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_source(source),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_type(&mut self, ty: impl Into<String>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_type(ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_subject(subject),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_time(time),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_extension(extension_name, extension_value),
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
) -> Option<ExtensionValue> {
|
||||
match self {
|
||||
Attributes::V10(a) => a.remove_extension(extension_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataAttributesWriter for Attributes {
|
||||
fn set_datacontenttype(
|
||||
&mut self,
|
||||
datacontenttype: Option<impl Into<String>>,
|
||||
) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_datacontenttype(datacontenttype),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
|
||||
match self {
|
||||
Attributes::V10(a) => a.set_dataschema(dataschema),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{Into, TryFrom};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
|
||||
/// Possible data values
|
||||
pub enum Data {
|
||||
String(String),
|
||||
Binary(Vec<u8>),
|
||||
Json(serde_json::Value),
|
||||
}
|
||||
|
||||
impl Data {
|
||||
/// Create a [`Data`] from a [`Into<Vec<u8>>`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use cloudevents::event::Data;
|
||||
///
|
||||
/// let value = Data::from_base64(b"dmFsdWU=").unwrap();
|
||||
/// assert_eq!(value, Data::Binary(base64::decode("dmFsdWU=").unwrap()));
|
||||
/// ```
|
||||
///
|
||||
/// [`AsRef<[u8]>`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
|
||||
/// [`Data`]: enum.Data.html
|
||||
pub fn from_base64<I>(i: I) -> Result<Self, base64::DecodeError>
|
||||
where
|
||||
I: AsRef<[u8]>,
|
||||
{
|
||||
Ok(base64::decode(&i)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Data> for serde_json::Value {
|
||||
fn into(self) -> Data {
|
||||
Data::Json(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Data> for Vec<u8> {
|
||||
fn into(self) -> Data {
|
||||
Data::Binary(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Data> for String {
|
||||
fn into(self) -> Data {
|
||||
Data::String(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Data> for serde_json::Value {
|
||||
type Error = serde_json::Error;
|
||||
|
||||
fn try_from(value: Data) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Data::String(s) => Ok(serde_json::from_str(&s)?),
|
||||
Data::Binary(v) => Ok(serde_json::from_slice(&v)?),
|
||||
Data::Json(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Data> for String {
|
||||
type Error = std::string::FromUtf8Error;
|
||||
|
||||
fn try_from(value: Data) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Data::String(s) => Ok(s),
|
||||
Data::Binary(v) => Ok(String::from_utf8(v)?),
|
||||
Data::Json(s) => Ok(s.to_string())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
use super::{Attributes, AttributesReader, AttributesWriter, Data, ExtensionValue, SpecVersion, AttributesV10};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use delegate::delegate;
|
||||
use std::convert::{TryFrom};
|
||||
use crate::event::attributes::DataAttributesWriter;
|
||||
|
||||
/// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md).
|
||||
/// It provides methods to get the attributes through [`AttributesReader`](cloudevents::event::AttributesReader)
|
||||
/// and write them through [`AttributesWriter`](cloudevents::event::AttributesWriter).
|
||||
/// It also provides methods to read and write the [event data](https://github.com/cloudevents/spec/blob/master/spec.md#event-data)
|
||||
/// ```
|
||||
/// use cloudevents::Event;
|
||||
/// use cloudevents::event::AttributesReader;
|
||||
///
|
||||
/// // Create an event using the Default trait
|
||||
/// let mut e = Event::default();
|
||||
/// e.write_data(
|
||||
/// "application/json",
|
||||
/// None,
|
||||
/// serde_json::json!({"hello": "world"})
|
||||
/// );
|
||||
///
|
||||
/// // Print the event id
|
||||
/// println!("Event id: {}", e.get_id());
|
||||
///
|
||||
/// // Get the event data
|
||||
/// let data: serde_json::Value = e.try_get_data().unwrap().unwrap();
|
||||
/// println!("Event data: {}", data)
|
||||
/// ```
|
||||
pub struct Event {
|
||||
pub attributes: Attributes,
|
||||
pub data: Option<Data>,
|
||||
}
|
||||
|
||||
impl AttributesReader for Event {
|
||||
delegate! {
|
||||
to self.attributes {
|
||||
fn get_id(&self) -> &str;
|
||||
fn get_source(&self) -> &str;
|
||||
fn get_specversion(&self) -> SpecVersion;
|
||||
fn get_type(&self) -> &str;
|
||||
fn get_datacontenttype(&self) -> Option<&str>;
|
||||
fn get_dataschema(&self) -> Option<&str>;
|
||||
fn get_subject(&self) -> Option<&str>;
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>>;
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue>;
|
||||
fn get_extensions(&self) -> Vec<(&str, &ExtensionValue)>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributesWriter for Event {
|
||||
delegate! {
|
||||
to self.attributes {
|
||||
fn set_id(&mut self, id: impl Into<String>);
|
||||
fn set_source(&mut self, source: impl Into<String>);
|
||||
fn set_type(&mut self, ty: impl Into<String>);
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>);
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>);
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
);
|
||||
fn remove_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
) -> Option<ExtensionValue>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Event {
|
||||
fn default() -> Self {
|
||||
Event {
|
||||
attributes: Attributes::V10(AttributesV10::default()),
|
||||
data: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub fn remove_data(&mut self) {
|
||||
self.data = None;
|
||||
}
|
||||
|
||||
/// Write data into the `Event`. You must provide a `content_type` and you can optionally provide a `schema`.
|
||||
///
|
||||
/// ```
|
||||
/// use cloudevents::Event;
|
||||
/// use serde_json::json;
|
||||
/// use std::convert::Into;
|
||||
///
|
||||
/// let mut e = Event::default();
|
||||
/// e.write_data("application/json", None, json!({}))
|
||||
/// ```
|
||||
pub fn write_data<S: Into<String>, D: Into<Data>>(&mut self, content_type: S, schema: Option<S>, value: D) {
|
||||
self.attributes.set_datacontenttype(Some(content_type));
|
||||
self.attributes.set_dataschema(schema);
|
||||
self.data = Some(value.into());
|
||||
}
|
||||
|
||||
pub fn get_data<T: Sized + From<Data>>(
|
||||
&self,
|
||||
) -> Option<T> {
|
||||
match self.data.as_ref() {
|
||||
Some(d) => Some(T::from(d.clone())),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_get_data<T: Sized + TryFrom<Data, Error = E>, E: std::error::Error>(
|
||||
&self,
|
||||
) -> Option<Result<T, E>> {
|
||||
match self.data.as_ref() {
|
||||
Some(d) => Some(T::try_from(d.clone())),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_data<T: Sized + TryFrom<Data, Error = E>, E: std::error::Error>(
|
||||
self,
|
||||
) -> Option<Result<T, E>> {
|
||||
match self.data {
|
||||
Some(d) => Some(T::try_from(d)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn try_get_data_json() {
|
||||
let expected_data = serde_json::json!({
|
||||
"hello": "world"
|
||||
});
|
||||
|
||||
let mut e = Event::default();
|
||||
e.write_data(
|
||||
"application/json",
|
||||
None,
|
||||
expected_data.clone()
|
||||
);
|
||||
|
||||
|
||||
let data: serde_json::Value = e.try_get_data().unwrap().unwrap();
|
||||
assert_eq!(expected_data, data);
|
||||
assert_eq!("application/json", e.get_datacontenttype().unwrap())
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::convert::From;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
|
||||
#[serde(untagged)]
|
||||
/// Represents all the possible [CloudEvents extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) values
|
||||
pub enum ExtensionValue {
|
||||
/// Represents a [`String`](std::string::String) value.
|
||||
String(String),
|
||||
/// Represents a [`bool`](bool) value.
|
||||
Boolean(bool),
|
||||
/// Represents an integer [`i64`](i64) value.
|
||||
Integer(i64),
|
||||
/// Represents a [Json `Value`](serde_json::value::Value).
|
||||
Json(Value),
|
||||
}
|
||||
|
||||
impl From<String> for ExtensionValue {
|
||||
fn from(s: String) -> Self {
|
||||
ExtensionValue::String(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ExtensionValue {
|
||||
fn from(s: bool) -> Self {
|
||||
ExtensionValue::Boolean(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for ExtensionValue {
|
||||
fn from(s: i64) -> Self {
|
||||
ExtensionValue::Integer(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for ExtensionValue {
|
||||
fn from(s: Value) -> Self {
|
||||
ExtensionValue::Json(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionValue {
|
||||
pub fn from_string<S>(s: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
ExtensionValue::from(s.into())
|
||||
}
|
||||
|
||||
pub fn from_i64<S>(s: S) -> Self
|
||||
where
|
||||
S: Into<i64>,
|
||||
{
|
||||
ExtensionValue::from(s.into())
|
||||
}
|
||||
|
||||
pub fn from_bool<S>(s: S) -> Self
|
||||
where
|
||||
S: Into<bool>,
|
||||
{
|
||||
ExtensionValue::from(s.into())
|
||||
}
|
||||
|
||||
pub fn from_json_value<S>(s: S) -> Self
|
||||
where
|
||||
S: Into<serde_json::Value>,
|
||||
{
|
||||
ExtensionValue::from(s.into())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
mod attributes;
|
||||
mod data;
|
||||
mod event;
|
||||
mod extensions;
|
||||
mod spec_version;
|
||||
|
||||
pub use attributes::Attributes;
|
||||
pub use attributes::{AttributesReader, AttributesWriter};
|
||||
pub use data::Data;
|
||||
pub use event::Event;
|
||||
pub use extensions::ExtensionValue;
|
||||
pub use spec_version::SpecVersion;
|
||||
|
||||
mod v10;
|
||||
|
||||
pub use v10::Attributes as AttributesV10;
|
|
@ -0,0 +1,32 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub enum SpecVersion {
|
||||
#[serde(rename = "0.3")]
|
||||
V03,
|
||||
#[serde(rename = "1.0")]
|
||||
V10,
|
||||
}
|
||||
|
||||
impl fmt::Display for SpecVersion {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SpecVersion::V03 => write!(f, "0.3"),
|
||||
SpecVersion::V10 => write!(f, "1.0"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for SpecVersion {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, String> {
|
||||
match value.as_str() {
|
||||
"0.3" => Ok(SpecVersion::V03),
|
||||
"1.0" => Ok(SpecVersion::V10),
|
||||
_ => Err(format!("Invalid specversion {}", value)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
use crate::event::{AttributesReader, AttributesWriter, ExtensionValue, SpecVersion};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use std::collections::HashMap;
|
||||
use crate::event::attributes::DataAttributesWriter;
|
||||
use uuid::Uuid;
|
||||
use hostname::get_hostname;
|
||||
|
||||
pub struct Attributes {
|
||||
id: String,
|
||||
ty: String,
|
||||
source: String,
|
||||
datacontenttype: Option<String>,
|
||||
dataschema: Option<String>,
|
||||
subject: Option<String>,
|
||||
time: Option<DateTime<FixedOffset>>,
|
||||
extensions: HashMap<String, ExtensionValue>,
|
||||
}
|
||||
|
||||
impl AttributesReader for Attributes {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn get_source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
fn get_specversion(&self) -> SpecVersion {
|
||||
SpecVersion::V10
|
||||
}
|
||||
|
||||
fn get_type(&self) -> &str {
|
||||
&self.ty
|
||||
}
|
||||
|
||||
fn get_datacontenttype(&self) -> Option<&str> {
|
||||
match self.datacontenttype.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dataschema(&self) -> Option<&str> {
|
||||
match self.dataschema.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_subject(&self) -> Option<&str> {
|
||||
match self.subject.as_ref() {
|
||||
Some(s) => Some(&s),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_time(&self) -> Option<&DateTime<FixedOffset>> {
|
||||
self.time.as_ref()
|
||||
}
|
||||
|
||||
fn get_extension(&self, extension_name: &str) -> Option<&ExtensionValue> {
|
||||
self.extensions.get(extension_name)
|
||||
}
|
||||
|
||||
fn get_extensions(&self) -> Vec<(&str, &ExtensionValue)> {
|
||||
self.extensions
|
||||
.iter()
|
||||
.map(|(k, v)| (k.as_str(), v))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl AttributesWriter for Attributes {
|
||||
fn set_id(&mut self, id: impl Into<String>) {
|
||||
self.id = id.into()
|
||||
}
|
||||
|
||||
fn set_source(&mut self, source: impl Into<String>) {
|
||||
self.source = source.into()
|
||||
}
|
||||
|
||||
fn set_type(&mut self, ty: impl Into<String>) {
|
||||
self.ty = ty.into()
|
||||
}
|
||||
|
||||
fn set_subject(&mut self, subject: Option<impl Into<String>>) {
|
||||
self.subject = subject.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_time(&mut self, time: Option<impl Into<DateTime<FixedOffset>>>) {
|
||||
self.time = time.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
extension_value: impl Into<ExtensionValue>,
|
||||
) {
|
||||
self.extensions
|
||||
.insert(extension_name.to_owned(), extension_value.into());
|
||||
}
|
||||
|
||||
fn remove_extension<'name, 'event: 'name>(
|
||||
&'event mut self,
|
||||
extension_name: &'name str,
|
||||
) -> Option<ExtensionValue> {
|
||||
self.extensions.remove(extension_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl DataAttributesWriter for Attributes {
|
||||
fn set_datacontenttype(
|
||||
&mut self,
|
||||
datacontenttype: Option<impl Into<String>>,
|
||||
) {
|
||||
self.datacontenttype = datacontenttype.map(Into::into)
|
||||
}
|
||||
|
||||
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
|
||||
self.dataschema = dataschema.map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Attributes {
|
||||
fn default() -> Self {
|
||||
Attributes {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
ty: "type".to_string(),
|
||||
source: get_hostname().unwrap_or("http://localhost/".to_string()),
|
||||
datacontenttype: None,
|
||||
dataschema: None,
|
||||
subject: None,
|
||||
time: None,
|
||||
extensions: HashMap::new()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
mod attributes;
|
||||
|
||||
pub use attributes::Attributes;
|
|
@ -0,0 +1,6 @@
|
|||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
pub mod event;
|
||||
|
||||
pub use event::Event;
|
|
@ -0,0 +1,2 @@
|
|||
#[test]
|
||||
fn use_event() {}
|
Loading…
Reference in New Issue