use std::{
collections::HashMap,
fmt::Display,
hash::{Hash, Hasher},
};
use serde::{Deserialize, Serialize};
#[cfg(test)]
mod test;
mod trait_impls;
mod traits;
pub type FrcTimestamp = u64;
type Bytes = Vec<u8>;
pub fn now() -> FrcTimestamp {
let now = std::time::SystemTime::now();
let since_epoch = now.duration_since(std::time::UNIX_EPOCH).unwrap();
since_epoch.as_micros() as FrcTimestamp
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FrcType {
Boolean,
Int,
Double,
Float,
String,
BoolArray,
IntArray,
FloatArray,
DoubleArray,
StringArray,
Binary,
}
impl Display for FrcType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrcType::Boolean => write!(f, "Boolean"),
FrcType::Int => write!(f, "Int"),
FrcType::Double => write!(f, "Double"),
FrcType::Float => write!(f, "Float"),
FrcType::String => write!(f, "String"),
FrcType::BoolArray => write!(f, "BoolArray"),
FrcType::IntArray => write!(f, "IntArray"),
FrcType::FloatArray => write!(f, "FloatArray"),
FrcType::DoubleArray => write!(f, "DoubleArray"),
FrcType::StringArray => write!(f, "StringArray"),
FrcType::Binary => write!(f, "Binary"),
}
}
}
impl Serialize for FrcType {
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as serde::Serializer>::Ok, <S as serde::Serializer>::Error>
where
S: serde::Serializer,
{
if let Self::Binary = self {
return serializer.serialize_str("raw");
}
serializer.serialize_str(&self.to_string().to_lowercase().replace("array", "[]"))
}
}
impl<'a> Deserialize<'a> for FrcType {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as serde::Deserializer<'a>>::Error>
where
D: serde::Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"boolean" => Ok(FrcType::Boolean),
"int" => Ok(FrcType::Int),
"double" => Ok(FrcType::Double),
"float" => Ok(FrcType::Float),
"string" => Ok(FrcType::String),
"json" => Ok(FrcType::String),
"bool[]" => Ok(FrcType::BoolArray),
"int[]" => Ok(FrcType::IntArray),
"float[]" => Ok(FrcType::FloatArray),
"double[]" => Ok(FrcType::DoubleArray),
"string[]" => Ok(FrcType::StringArray),
"raw" => Ok(FrcType::Binary),
"rpc" => Ok(FrcType::Binary),
"msgpack" => Ok(FrcType::Binary),
"protobuf" => Ok(FrcType::Binary),
_ => Err(serde::de::Error::custom(format!("Invalid FrcType: {}", s))),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash)]
#[serde(untagged)]
pub enum FrcBinaryFormats {
Raw(Bytes),
MsgPack(Bytes),
Protobuf(Bytes),
}
impl FrcBinaryFormats {
pub fn get_bytes(&self) -> &Bytes {
match self {
FrcBinaryFormats::Raw(v) => v,
FrcBinaryFormats::MsgPack(v) => v,
FrcBinaryFormats::Protobuf(v) => v,
}
}
pub fn to_bytes(self) -> Bytes {
match self {
FrcBinaryFormats::Raw(v) => v,
FrcBinaryFormats::MsgPack(v) => v,
FrcBinaryFormats::Protobuf(v) => v,
}
}
pub fn len(&self) -> usize {
match self {
FrcBinaryFormats::Raw(v) => v.len(),
FrcBinaryFormats::MsgPack(v) => v.len(),
FrcBinaryFormats::Protobuf(v) => v.len(),
}
}
pub fn format_str(&self) -> &str {
match self {
FrcBinaryFormats::Raw(_) => "Raw",
FrcBinaryFormats::MsgPack(_) => "MsgPack",
FrcBinaryFormats::Protobuf(_) => "Protobuf",
}
}
}
impl Display for FrcBinaryFormats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}: {} Bytes]", self.format_str(), self.len())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum FrcValue {
Boolean(bool),
Int(i64),
Double(f64),
Float(f32),
String(String),
BooleanArray(Vec<bool>),
IntArray(Vec<i64>),
FloatArray(Vec<f32>),
DoubleArray(Vec<f64>),
StringArray(Vec<String>),
Binary(FrcBinaryFormats),
}
impl Display for FrcValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FrcValue::Boolean(v) => write!(f, "{}", v),
FrcValue::Int(v) => write!(f, "{}", v),
FrcValue::Double(v) => write!(f, "{}", v),
FrcValue::Float(v) => write!(f, "{}", v),
FrcValue::String(v) => write!(f, "{}", v),
FrcValue::BooleanArray(v) => write!(f, "{:?}", v),
FrcValue::IntArray(v) => write!(f, "{:?}", v),
FrcValue::FloatArray(v) => write!(f, "{:?}", v),
FrcValue::DoubleArray(v) => write!(f, "{:?}", v),
FrcValue::StringArray(v) => write!(f, "{:?}", v),
FrcValue::Binary(v) => write!(f, "{}", v),
}
}
}
impl Hash for FrcValue {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
FrcValue::Boolean(v) => v.hash(state),
FrcValue::Int(v) => v.hash(state),
FrcValue::Double(v) => v.to_bits().hash(state),
FrcValue::Float(v) => v.to_bits().hash(state),
FrcValue::String(v) => v.hash(state),
FrcValue::BooleanArray(v) => v.hash(state),
FrcValue::IntArray(v) => v.hash(state),
FrcValue::FloatArray(v) => v.iter().for_each(|v| v.to_bits().hash(state)),
FrcValue::DoubleArray(v) => v.iter().for_each(|v| v.to_bits().hash(state)),
FrcValue::StringArray(v) => v.hash(state),
FrcValue::Binary(v) => v.hash(state),
}
}
}
impl FrcValue {
pub fn get_type(&self) -> FrcType {
match self {
FrcValue::Boolean(_) => FrcType::Boolean,
FrcValue::Int(_) => FrcType::Int,
FrcValue::Double(_) => FrcType::Double,
FrcValue::Float(_) => FrcType::Float,
FrcValue::String(_) => FrcType::String,
FrcValue::BooleanArray(_) => FrcType::BoolArray,
FrcValue::IntArray(_) => FrcType::IntArray,
FrcValue::FloatArray(_) => FrcType::FloatArray,
FrcValue::DoubleArray(_) => FrcType::DoubleArray,
FrcValue::StringArray(_) => FrcType::StringArray,
FrcValue::Binary(_) => FrcType::Binary,
}
}
pub fn empty() -> Self {
Self::Binary(FrcBinaryFormats::Raw(Vec::new()))
}
pub fn is_empty(&self) -> bool {
match self {
FrcValue::String(v) => v.is_empty(),
FrcValue::BooleanArray(v) => v.is_empty(),
FrcValue::IntArray(v) => v.is_empty(),
FrcValue::DoubleArray(v) => v.is_empty(),
FrcValue::FloatArray(v) => v.is_empty(),
FrcValue::StringArray(v) => v.is_empty(),
FrcValue::Binary(v) => v.len() == 0,
_ => false,
}
}
pub fn is_array(&self) -> bool {
match self {
FrcValue::BooleanArray(_) => true,
FrcValue::IntArray(_) => true,
FrcValue::DoubleArray(_) => true,
FrcValue::FloatArray(_) => true,
FrcValue::StringArray(_) => true,
_ => false,
}
}
pub fn to_timestamped_now(self) -> FrcTimestampedValue {
FrcTimestampedValue::new(now(), self)
}
pub fn to_timestamped(self, timestamp: FrcTimestamp) -> FrcTimestampedValue {
FrcTimestampedValue::new(timestamp, self)
}
pub fn as_timestamped_now(&self) -> FrcTimestampedValue {
FrcTimestampedValue::new(now(), self.clone())
}
pub fn as_timestamped(&self, timestamp: FrcTimestamp) -> FrcTimestampedValue {
FrcTimestampedValue::new(timestamp, self.clone())
}
pub fn to_tagged(self) -> TaggedValue {
TaggedValue {
r#type: self.get_type(),
value: self,
}
}
pub fn default_value(r#type: FrcType) -> Self {
match r#type {
FrcType::Boolean => FrcValue::Boolean(false),
FrcType::Int => FrcValue::Int(0),
FrcType::Double => FrcValue::Double(0.0),
FrcType::Float => FrcValue::Float(0.0),
FrcType::String => FrcValue::String(String::new()),
FrcType::BoolArray => FrcValue::BooleanArray(Vec::new()),
FrcType::IntArray => FrcValue::IntArray(Vec::new()),
FrcType::FloatArray => FrcValue::FloatArray(Vec::new()),
FrcType::DoubleArray => FrcValue::DoubleArray(Vec::new()),
FrcType::StringArray => FrcValue::StringArray(Vec::new()),
FrcType::Binary => FrcValue::Binary(FrcBinaryFormats::Raw(Vec::new())),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash)]
pub struct TaggedValue {
#[serde(rename = "type")]
pub r#type: FrcType,
pub value: FrcValue,
}
impl Display for TaggedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({})", self.r#type, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Hash)]
pub struct FrcTimestampedValue {
pub timestamp: FrcTimestamp,
pub value: FrcValue,
}
impl Display for FrcTimestampedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} at {}", self.value, self.timestamp)
}
}
impl FrcTimestampedValue {
pub fn new(timestamp: FrcTimestamp, value: FrcValue) -> Self {
Self { timestamp, value }
}
pub fn get_type(&self) -> FrcType {
self.value.get_type()
}
pub fn is_empty(&self) -> bool {
self.value.is_empty()
}
pub fn is_array(&self) -> bool {
self.value.is_array()
}
pub fn is_after_timestamp(&self, timestamp: FrcTimestamp) -> bool {
self.timestamp > timestamp
}
pub fn is_after_other(&self, other: &Self) -> bool {
self.timestamp > other.timestamp
}
pub fn is_before_timestamp(&self, timestamp: FrcTimestamp) -> bool {
self.timestamp < timestamp
}
pub fn is_before_other(&self, other: &Self) -> bool {
self.timestamp < other.timestamp
}
pub fn replace_timestamp(&mut self, timestamp: FrcTimestamp) {
self.timestamp = timestamp;
}
pub fn replace_value(&mut self, value: FrcValue) {
self.value = value;
}
pub fn replace(&mut self, other: Self) {
self.timestamp = other.timestamp;
self.value = other.value;
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrcTimeline(Vec<FrcTimestampedValue>);
impl IntoIterator for FrcTimeline {
type Item = FrcTimestampedValue;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl FrcTimeline {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn from_vec_sorted(vec: Vec<FrcTimestampedValue>) -> Self {
Self(vec)
}
pub fn from_vec(mut vec: Vec<FrcTimestampedValue>) -> Self {
vec.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
Self(vec)
}
pub fn to_vec(self) -> Vec<FrcTimestampedValue> {
self.0
}
pub fn is_all_same_type(&self) -> bool {
if self.0.is_empty() {
return true;
}
let first_type = self.0[0].get_type();
self.0.iter().all(|v| v.get_type() == first_type)
}
pub fn is_all_same_type_as(&self, other: &Self) -> bool {
if self.0.is_empty() || other.0.is_empty() {
return true;
}
let first_type = self.0[0].get_type();
let other_first_type = other.0[0].get_type();
first_type == other_first_type && self.0.iter().all(|v| v.get_type() == first_type)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn get_by_timestamp(
&self,
timestamp: u64,
closest_after: bool,
) -> Option<&FrcTimestampedValue> {
if closest_after {
if timestamp < self.0[0].timestamp {
return None;
}
} else {
if timestamp > self.0[self.0.len() - 1].timestamp {
return None;
}
}
let mut low = 0;
let mut high = self.0.len() - 1;
while low <= high {
let mid = (low + high) / 2;
if self.0[mid].timestamp < timestamp {
low = mid + 1;
} else if self.0[mid].timestamp > timestamp {
high = mid - 1;
} else {
return Some(&self.0[mid]);
}
}
if low == self.0.len() {
return None;
}
if closest_after {
Some(&self.0[low])
} else {
Some(&self.0[low - 1])
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrcTableInstant {
#[serde(flatten)]
pub values: HashMap<String, FrcTimestampedValue>, }
impl Display for FrcTableInstant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
for (i, (k, v)) in self.values.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v)?;
}
write!(f, "}}")
}
}
impl FrcTableInstant {
pub fn new_slim() -> Self {
Self {
values: HashMap::new(),
}
}
pub fn new() -> Self {
Self {
values: HashMap::new(),
}
}
pub fn from_tuples(mut tuples: Vec<(impl ToString, FrcTimestampedValue)>) -> Self {
let mut values = HashMap::new();
tuples.reverse();
for (k, v) in tuples {
values.insert(k.to_string(), v);
}
Self { values }
}
pub fn set_field(&mut self, name: impl ToString, value: FrcTimestampedValue) {
self.values.insert(name.to_string(), value);
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FrcTableHistory {
#[serde(flatten)]
pub values: HashMap<String, FrcTimeline>, }
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", content = "table", rename_all = "lowercase")]
pub enum FrcTable {
Snapshot(FrcTableInstant),
History(FrcTableHistory),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrcObject {
pub table: FrcTableInstant,
#[serde(rename = "type")]
pub r#type: String,
pub schema: FrcObjectSchema,
panic_on_type_mismatch: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FrcObjectSchema {
#[serde(flatten)]
pub values: HashMap<String, FrcType>,
}
impl FrcObjectSchema {
pub fn from_tuples(tuples: Vec<(impl ToString, FrcType)>) -> Self {
let mut values = HashMap::new();
for (k, v) in tuples {
values.insert(k.to_string(), v);
}
Self { values }
}
}