use byteorder::{ReadBytesExt, WriteBytesExt};
use heck::*;
use once_cell::sync::Lazy;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
pub use trans_derive::*;
pub mod prelude {
pub use super::Trans;
pub use trans_derive::*;
}
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub trait Trans: Sized + 'static {
fn create_schema() -> Schema;
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()>;
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self>;
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Name(String);
impl Name {
pub fn new(name: String) -> Self {
Self(name.to_camel_case())
}
pub fn raw(&self) -> String {
self.0.clone()
}
pub fn snake_case(&self, conv: impl FnOnce(&str) -> String) -> String {
conv(&self.0).to_snake_case()
}
pub fn camel_case(&self, conv: impl FnOnce(&str) -> String) -> String {
conv(&self.0).to_camel_case()
}
pub fn shouty_snake_case(&self, conv: impl FnOnce(&str) -> String) -> String {
conv(&self.0).to_shouty_snake_case()
}
pub fn mixed_case(&self, conv: impl FnOnce(&str) -> String) -> String {
conv(&self.0).to_mixed_case()
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Field {
pub name: Name,
pub schema: Arc<Schema>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Struct {
pub magic: Option<i32>,
pub name: Name,
pub fields: Vec<Field>,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum Schema {
Bool,
Int32,
Int64,
Float32,
Float64,
String,
Struct(Struct),
OneOf {
base_name: Name,
variants: Vec<Struct>,
},
Option(Arc<Schema>),
Vec(Arc<Schema>),
Map(Arc<Schema>, Arc<Schema>),
Enum {
base_name: Name,
variants: Vec<Name>,
},
}
impl Schema {
pub fn full_name(&self) -> Name {
match self {
Schema::Bool => Name("Bool".to_owned()),
Schema::Int32 => Name("Int32".to_owned()),
Schema::Int64 => Name("Int64".to_owned()),
Schema::Float32 => Name("Float32".to_owned()),
Schema::Float64 => Name("Float64".to_owned()),
Schema::String => Name("String".to_owned()),
Schema::Struct(Struct { name, .. }) => name.clone(),
Schema::OneOf { base_name, .. } => base_name.to_owned(),
Schema::Option(inner) => Name(format!("Opt{}", inner.full_name().0)),
Schema::Vec(inner) => Name(format!("Vec{}", inner.full_name().0)),
Schema::Map(key, value) => {
Name(format!("Map{}{}", key.full_name().0, value.full_name().0))
}
Schema::Enum { base_name, .. } => base_name.clone(),
}
}
pub fn hashable(&self) -> bool {
match self {
Self::Bool | Self::Int32 | Self::Int64 | Self::String => true,
Self::Float32 | Self::Float64 => false,
Self::Option(_) => false,
Self::Struct(Struct { fields, .. }) => {
fields.iter().all(|field| field.schema.hashable())
}
Self::OneOf { .. } => false,
Self::Vec(_) => false,
Self::Map(_, _) => false,
Self::Enum { .. } => true,
}
}
pub fn of<T: Trans>() -> Arc<Schema> {
static MAP: Lazy<Mutex<HashSet<Arc<Schema>>>> = Lazy::new(|| Mutex::new(HashSet::new()));
let schema = T::create_schema();
if !MAP.lock().unwrap().contains(&schema) {
let schema = Arc::new(T::create_schema());
MAP.lock().unwrap().insert(schema);
}
MAP.lock().unwrap().get(&schema).unwrap().clone()
}
}
macro_rules! impl_for_tuple {
($($name:ident),*) => {
#[allow(non_snake_case, unused_variables)]
impl<$($name: Trans),*> Trans for ($($name,)*) {
fn create_schema() -> Schema {
todo!()
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
Ok(($(<$name as Trans>::read_from(reader)?,)*))
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
let ($($name,)*) = self;
$($name.write_to(writer)?;)*
Ok(())
}
}
};
}
impl_for_tuple!();
impl_for_tuple!(A);
impl_for_tuple!(A, B);
impl_for_tuple!(A, B, C);
impl_for_tuple!(A, B, C, D);
impl Trans for bool {
fn create_schema() -> Schema {
Schema::Bool
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
let value = reader.read_u8()?;
match value {
0 => Ok(false),
1 => Ok(true),
_ => Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Bool value should be 0 or 1",
)),
}
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_u8(if *self { 1 } else { 0 })
}
}
impl Trans for usize {
fn create_schema() -> Schema {
Schema::Int32
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
Ok(i32::read_from(reader)? as usize)
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
(*self as i32).write_to(writer)
}
}
impl Trans for i32 {
fn create_schema() -> Schema {
Schema::Int32
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
reader.read_i32::<byteorder::LittleEndian>()
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_i32::<byteorder::LittleEndian>(*self)
}
}
impl Trans for i64 {
fn create_schema() -> Schema {
Schema::Int64
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
reader.read_i64::<byteorder::LittleEndian>()
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_i64::<byteorder::LittleEndian>(*self)
}
}
impl Trans for f32 {
fn create_schema() -> Schema {
Schema::Float32
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
reader.read_f32::<byteorder::LittleEndian>()
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_f32::<byteorder::LittleEndian>(*self)
}
}
impl Trans for f64 {
fn create_schema() -> Schema {
Schema::Float64
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
reader.read_f64::<byteorder::LittleEndian>()
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
writer.write_f64::<byteorder::LittleEndian>(*self)
}
}
impl Trans for String {
fn create_schema() -> Schema {
Schema::String
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
let len = i32::read_from(reader)? as usize;
let mut buf = vec![0; len];
reader.read_exact(&mut buf)?;
String::from_utf8(buf).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
(self.len() as i32).write_to(writer)?;
writer.write_all(self.as_bytes())
}
}
impl<T: Trans> Trans for Option<T> {
fn create_schema() -> Schema {
Schema::Option(Schema::of::<T>())
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
let is_some = bool::read_from(reader)?;
Ok(if is_some {
Some(T::read_from(reader)?)
} else {
None
})
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
self.is_some().write_to(writer)?;
if let Some(value) = self {
value.write_to(writer)?;
}
Ok(())
}
}
impl<T: Trans> Trans for Vec<T> {
fn create_schema() -> Schema {
Schema::Vec(Schema::of::<T>())
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
let len = usize::read_from(reader)?;
let mut result = Vec::with_capacity(len);
for _ in 0..len {
result.push(T::read_from(reader)?);
}
Ok(result)
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
self.len().write_to(writer)?;
for item in self {
item.write_to(writer)?;
}
Ok(())
}
}
impl<K: Trans + Eq + std::hash::Hash, V: Trans> Trans for HashMap<K, V> {
fn create_schema() -> Schema {
Schema::Map(Schema::of::<K>(), Schema::of::<V>())
}
fn read_from(reader: &mut dyn std::io::Read) -> std::io::Result<Self> {
let len = usize::read_from(reader)?;
let mut result = Self::with_capacity(len);
for _ in 0..len {
result.insert(K::read_from(reader)?, V::read_from(reader)?);
}
Ok(result)
}
fn write_to(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
self.len().write_to(writer)?;
for (key, value) in self {
key.write_to(writer)?;
value.write_to(writer)?;
}
Ok(())
}
}
#[test]
fn test() {
assert_eq!(*Schema::of::<i32>(), Schema::Int32);
assert_eq!(*Schema::of::<i64>(), Schema::Int64);
assert_eq!(*Schema::of::<f32>(), Schema::Float32);
assert_eq!(*Schema::of::<f64>(), Schema::Float64);
assert_eq!(*Schema::of::<String>(), Schema::String);
}