Move sehn to sehn-serde
This commit is contained in:
parent
9d4a63a405
commit
05ca67db3b
12
sehn-serde/Cargo.toml
Normal file
12
sehn-serde/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "sehn-serde"
|
||||
version = "0.1.0"
|
||||
authors = ["avitex <theavitex@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
itoa = "0.4"
|
||||
dtoa = "0.4"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
error-chain = "0.12"
|
19
sehn-serde/examples/basic.rs
Normal file
19
sehn-serde/examples/basic.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use serde_derive::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UserType<T>(T);
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Test<'a> {
|
||||
foo: &'a str,
|
||||
bar: UserType<&'a str>
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let val = Test {
|
||||
foo: "hello",
|
||||
bar: UserType("world")
|
||||
};
|
||||
|
||||
println!("{}", sehn_serde::to_string(&val).unwrap());
|
||||
}
|
32
sehn-serde/src/de/error.rs
Normal file
32
sehn-serde/src/de/error.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use error_chain::{
|
||||
error_chain,
|
||||
error_chain_processing,
|
||||
impl_error_chain_processed,
|
||||
impl_error_chain_kind,
|
||||
impl_extract_backtrace
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
errors {
|
||||
Eof {
|
||||
description("unexpected end of input")
|
||||
}
|
||||
NestingLimit {
|
||||
description("nesting limit reached")
|
||||
}
|
||||
Unexpected(c: char) {
|
||||
description("unexpected character")
|
||||
display("unexpected character: '{}'", c)
|
||||
}
|
||||
Message(t: String) {
|
||||
description(t)
|
||||
display("internal error: '{}'", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
ErrorKind::Message(msg.to_string()).into()
|
||||
}
|
||||
}
|
3
sehn-serde/src/de/mod.rs
Normal file
3
sehn-serde/src/de/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod error;
|
||||
|
||||
pub use self::error::*;
|
14
sehn-serde/src/error.rs
Normal file
14
sehn-serde/src/error.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use error_chain::{
|
||||
error_chain,
|
||||
error_chain_processing,
|
||||
impl_error_chain_processed,
|
||||
impl_error_chain_kind,
|
||||
impl_extract_backtrace
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
links {
|
||||
Serialization(crate::ser::Error, crate::ser::ErrorKind);
|
||||
Deserialization(crate::de::Error, crate::de::ErrorKind);
|
||||
}
|
||||
}
|
14
sehn-serde/src/lib.rs
Normal file
14
sehn-serde/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
mod error;
|
||||
|
||||
pub mod de;
|
||||
pub mod ser;
|
||||
pub mod syntax;
|
||||
pub mod value;
|
||||
|
||||
//pub use self::de::{from_str, Deserializer};
|
||||
pub use self::ser::{to_string, Serializer};
|
||||
pub use self::error::{Error, ErrorKind, Result};
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
33
sehn-serde/src/ser/config.rs
Normal file
33
sehn-serde/src/ser/config.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use super::{Format, StandardFormat};
|
||||
|
||||
pub trait Config {
|
||||
type Format: Format;
|
||||
|
||||
/// `struct $name { ..., ... }`
|
||||
const TAG_STRUCTS: bool = false;
|
||||
/// `struct $name ( ... )`
|
||||
const TAG_NEWTYPE_STRUCTS: bool = false;
|
||||
/// `struct $name ( ..., ... )`
|
||||
const TAG_TUPLE_STRUCTS: bool = false;
|
||||
/// `enum { $name ( ..., ... ) }`
|
||||
const TAG_TUPLE_VARIANTS: bool = false;
|
||||
/// `enum { $name { ..., ... } }`
|
||||
const TAG_STRUCT_VARIANTS: bool = false;
|
||||
/// `struct $name`
|
||||
const UNIT_STRUCT_TO_KIND: bool = false;
|
||||
/// The initial size of the stack used for
|
||||
/// checking if a value was tagged or not
|
||||
/// as it ascends nesting.
|
||||
const INITIAL_TAG_STACK_SIZE: usize = 265;
|
||||
/// Disable string escaping (DANGEROUS!)
|
||||
const DISABLE_STRING_ESCAPING: bool = false;
|
||||
}
|
||||
|
||||
pub struct DefaultConfig;
|
||||
|
||||
impl Config for DefaultConfig {
|
||||
type Format = StandardFormat;
|
||||
|
||||
const UNIT_STRUCT_TO_KIND: bool = true;
|
||||
const TAG_NEWTYPE_STRUCTS: bool = true;
|
||||
}
|
25
sehn-serde/src/ser/error.rs
Normal file
25
sehn-serde/src/ser/error.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use error_chain::{
|
||||
error_chain,
|
||||
error_chain_processing,
|
||||
impl_error_chain_processed,
|
||||
impl_error_chain_kind,
|
||||
impl_extract_backtrace
|
||||
};
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
Io(::std::io::Error);
|
||||
}
|
||||
errors {
|
||||
Message(t: String) {
|
||||
description(t)
|
||||
display("internal error: '{}'", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T: std::fmt::Display>(msg: T) -> Self {
|
||||
ErrorKind::Message(msg.to_string()).into()
|
||||
}
|
||||
}
|
12
sehn-serde/src/ser/format/deterministic.rs
Normal file
12
sehn-serde/src/ser/format/deterministic.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use super::{Format, RealFormat, WriteReal};
|
||||
|
||||
pub struct DeterministicRealFormat;
|
||||
|
||||
impl RealFormat for DeterministicRealFormat {}
|
||||
|
||||
pub struct DeterministicFormat;
|
||||
|
||||
impl Format for DeterministicFormat {
|
||||
type Engine = ();
|
||||
type RealFormat = DeterministicRealFormat;
|
||||
}
|
30
sehn-serde/src/ser/format/fast_real.rs
Normal file
30
sehn-serde/src/ser/format/fast_real.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::io::Write;
|
||||
use crate::ser::error::Result;
|
||||
use super::{RealFormat, WriteReal};
|
||||
|
||||
macro_rules! impl_write_real_for_fast_format {
|
||||
($($prim:ident with $xtoa:ident),*) => {
|
||||
$(impl WriteReal<$prim> for FastRealFormat {
|
||||
fn write_real<W: Write>(w: &mut W, r: $prim) -> Result<()> {
|
||||
$xtoa::write(w, r).map(|_| ()).map_err(|e| e.into())
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FastRealFormat;
|
||||
|
||||
impl_write_real_for_fast_format!(
|
||||
i8 with itoa,
|
||||
i16 with itoa,
|
||||
i32 with itoa,
|
||||
i64 with itoa,
|
||||
u8 with itoa,
|
||||
u16 with itoa,
|
||||
u32 with itoa,
|
||||
u64 with itoa,
|
||||
f32 with dtoa,
|
||||
f64 with dtoa
|
||||
);
|
||||
|
||||
impl RealFormat for FastRealFormat {}
|
40
sehn-serde/src/ser/format/mod.rs
Normal file
40
sehn-serde/src/ser/format/mod.rs
Normal file
@ -0,0 +1,40 @@
|
||||
mod fast_real;
|
||||
mod pretty;
|
||||
mod standard;
|
||||
//mod deterministic;
|
||||
|
||||
use super::{Write, Result};
|
||||
|
||||
pub use self::fast_real::*;
|
||||
pub use self::pretty::*;
|
||||
pub use self::standard::*;
|
||||
//pub use self::deterministic::*;
|
||||
|
||||
pub trait WriteReal<T> {
|
||||
fn write_real<W: Write>(w: &mut W, i: T) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait RealFormat:
|
||||
WriteReal<i8> + WriteReal<i16> +
|
||||
WriteReal<i32> + WriteReal<i64> +
|
||||
WriteReal<u8> + WriteReal<u16> +
|
||||
WriteReal<u32> + WriteReal<u64> +
|
||||
WriteReal<f32> + WriteReal<f64> {}
|
||||
|
||||
pub trait FormatEngine: Default {
|
||||
fn mark_delim(&mut self, _d: u8) {}
|
||||
}
|
||||
|
||||
impl FormatEngine for () {}
|
||||
|
||||
pub trait Format {
|
||||
type Engine: FormatEngine;
|
||||
type RealFormat: RealFormat;
|
||||
|
||||
#[inline]
|
||||
fn write<W: Write>(_e: &mut Self::Engine, w: &mut Write, bytes: &[u8]) -> Result<()> {
|
||||
// Passthrough
|
||||
w.write(bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
117
sehn-serde/src/ser/format/pretty.rs
Normal file
117
sehn-serde/src/ser/format/pretty.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use std::io::Write;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::syntax::*;
|
||||
|
||||
use crate::ser::error::Result;
|
||||
use super::{Format, FormatEngine, FastRealFormat};
|
||||
|
||||
pub type DefaultPrettyFormat = PrettyFormat<TwoSpaces>;
|
||||
|
||||
pub trait IndentStyle {
|
||||
const LEVEL: &'static [u8];
|
||||
}
|
||||
|
||||
pub struct OneTab;
|
||||
pub struct TwoSpaces;
|
||||
pub struct FourSpaces;
|
||||
|
||||
impl IndentStyle for OneTab {
|
||||
const LEVEL: &'static [u8] = &[
|
||||
WHITESPACE_TAB_CHAR
|
||||
];
|
||||
}
|
||||
|
||||
impl IndentStyle for TwoSpaces {
|
||||
const LEVEL: &'static [u8] = &[
|
||||
WHITESPACE_SPACE_CHAR,
|
||||
WHITESPACE_SPACE_CHAR
|
||||
];
|
||||
}
|
||||
|
||||
impl IndentStyle for FourSpaces {
|
||||
const LEVEL: &'static [u8] = &[
|
||||
WHITESPACE_SPACE_CHAR,
|
||||
WHITESPACE_SPACE_CHAR,
|
||||
WHITESPACE_SPACE_CHAR,
|
||||
WHITESPACE_SPACE_CHAR
|
||||
];
|
||||
}
|
||||
|
||||
pub struct PrettyFormat<I: IndentStyle> {
|
||||
indent_style: PhantomData<I>
|
||||
}
|
||||
|
||||
impl<I: IndentStyle> PrettyFormat<I> {
|
||||
fn style_before_write(e: &mut PrettyFormatEngine, w: &mut Write) -> Result<()> {
|
||||
match e.delim {
|
||||
DICT_END_CHAR | LIST_END_CHAR => {
|
||||
e.level -= 1;
|
||||
Self::write_newline(w)?;
|
||||
Self::write_indentation(e, w)?;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn style_after_write(e: &mut PrettyFormatEngine, w: &mut Write) -> Result<()> {
|
||||
match e.delim {
|
||||
DICT_START_CHAR | LIST_START_CHAR => {
|
||||
e.level += 1;
|
||||
Self::write_newline(w)?;
|
||||
Self::write_indentation(e, w)?;
|
||||
},
|
||||
DICT_KV_SEPARATOR_CHAR => {
|
||||
Self::write_space(w)?;
|
||||
},
|
||||
COMMA_CHAR => {
|
||||
Self::write_newline(w)?;
|
||||
Self::write_indentation(e, w)?;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
e.delim = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_space(w: &mut Write) -> Result<()> {
|
||||
w.write(&[WHITESPACE_SPACE_CHAR])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_newline(w: &mut Write) -> Result<()> {
|
||||
w.write(&[NEWLINE_LF_CHAR])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_indentation(e: &mut PrettyFormatEngine, w: &mut Write) -> Result<()> {
|
||||
for _ in 0..e.level {
|
||||
w.write(I::LEVEL)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PrettyFormatEngine {
|
||||
level: usize,
|
||||
delim: u8
|
||||
}
|
||||
|
||||
impl FormatEngine for PrettyFormatEngine {
|
||||
fn mark_delim(&mut self, delim: u8) {
|
||||
self.delim = delim;
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IndentStyle> Format for PrettyFormat<I> {
|
||||
type Engine = PrettyFormatEngine;
|
||||
type RealFormat = FastRealFormat;
|
||||
|
||||
fn write<W: Write>(e: &mut PrettyFormatEngine, w: &mut Write, bytes: &[u8]) -> Result<()> {
|
||||
Self::style_before_write(e, w)?;
|
||||
w.write(bytes)?;
|
||||
Self::style_after_write(e, w)
|
||||
}
|
||||
}
|
8
sehn-serde/src/ser/format/standard.rs
Normal file
8
sehn-serde/src/ser/format/standard.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use super::{Format, FastRealFormat};
|
||||
|
||||
pub struct StandardFormat;
|
||||
|
||||
impl Format for StandardFormat {
|
||||
type Engine = ();
|
||||
type RealFormat = FastRealFormat;
|
||||
}
|
35
sehn-serde/src/ser/macros.rs
Normal file
35
sehn-serde/src/ser/macros.rs
Normal file
@ -0,0 +1,35 @@
|
||||
macro_rules! impl_ser_real {
|
||||
($f:ident, $t:ident) => {
|
||||
fn $f(self, v: $t) -> Result<()> {
|
||||
self.write_real::<$t>(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tag_start {
|
||||
($s:expr, $opt_enabled:expr, $name:expr) => {
|
||||
if $opt_enabled {
|
||||
if $name.is_empty() {
|
||||
$s.tag_stack.push(false);
|
||||
} else {
|
||||
$s.write_bytes($name.as_bytes())?;
|
||||
$s.write_delim(syntax::TAG_START_CHAR)?;
|
||||
$s.tag_stack.push(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tag_end {
|
||||
($s:expr, $opt_enabled:expr) => {
|
||||
if $opt_enabled {
|
||||
if $s.tag_stack.pop().expect("stack tag value") {
|
||||
$s.write_delim(syntax::TAG_END_CHAR)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
491
sehn-serde/src/ser/mod.rs
Normal file
491
sehn-serde/src/ser/mod.rs
Normal file
@ -0,0 +1,491 @@
|
||||
mod error;
|
||||
mod string_writer;
|
||||
mod format;
|
||||
mod config;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
use std::io::Write;
|
||||
use std::marker::PhantomData;
|
||||
use serde::ser::{self, Serialize};
|
||||
use crate::syntax;
|
||||
|
||||
pub use self::format::*;
|
||||
pub use self::config::*;
|
||||
pub use self::string_writer::StringWriter;
|
||||
pub use self::error::{Error, ErrorKind, Result};
|
||||
|
||||
pub struct Serializer<W: Write, C: Config> {
|
||||
out: W,
|
||||
tag_stack: Vec<bool>,
|
||||
first_element: bool,
|
||||
config: PhantomData<C>,
|
||||
format_engine: <C::Format as Format>::Engine
|
||||
}
|
||||
|
||||
impl<W: Write, C: Config> Serializer<W, C> {
|
||||
#[inline]
|
||||
fn set_first_element(&mut self) {
|
||||
self.first_element = true;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_not_first_element(&mut self) -> bool {
|
||||
if self.first_element {
|
||||
self.first_element = false;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_delim(&mut self, delim: u8) -> Result<()> {
|
||||
self.format_engine.mark_delim(delim);
|
||||
self.write_bytes(&[delim])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
|
||||
<C::Format as Format>::write::<W>(&mut self.format_engine, &mut self.out, bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_real<R>(&mut self, r: R) -> Result<()> where <C::Format as Format>::RealFormat: WriteReal<R> {
|
||||
<C::Format as Format>::RealFormat::write_real::<W>(&mut self.out, r)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_with_config<T, C>(value: &T) -> Result<String>
|
||||
where
|
||||
T: Serialize,
|
||||
C: Config
|
||||
{
|
||||
let mut serializer = Serializer {
|
||||
out: StringWriter::new(),
|
||||
config: PhantomData::<C>,
|
||||
tag_stack: Vec::with_capacity(C::INITIAL_TAG_STACK_SIZE),
|
||||
first_element: false,
|
||||
format_engine: Default::default()
|
||||
};
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(serializer.out.to_string().expect("valid utf8"))
|
||||
}
|
||||
|
||||
pub fn to_string<T>(value: &T) -> Result<String>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
to_string_with_config::<T, DefaultConfig>(value)
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::Serializer for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Self;
|
||||
type SerializeTuple = Self;
|
||||
type SerializeTupleStruct = Self;
|
||||
type SerializeTupleVariant = Self;
|
||||
type SerializeMap = Self;
|
||||
type SerializeStruct = Self;
|
||||
type SerializeStructVariant = Self;
|
||||
|
||||
impl_ser_real!(serialize_i8, i8);
|
||||
impl_ser_real!(serialize_i16, i16);
|
||||
impl_ser_real!(serialize_i32, i32);
|
||||
impl_ser_real!(serialize_i64, i64);
|
||||
impl_ser_real!(serialize_u8, u8);
|
||||
impl_ser_real!(serialize_u16, u16);
|
||||
impl_ser_real!(serialize_u32, u32);
|
||||
impl_ser_real!(serialize_u64, u64);
|
||||
impl_ser_real!(serialize_f32, f32);
|
||||
impl_ser_real!(serialize_f64, f64);
|
||||
|
||||
fn serialize_char(self, v: char) -> Result<()> {
|
||||
let mut char_bytes: [u8; 4] = unsafe { std::mem::uninitialized() };
|
||||
let char_str = v.encode_utf8(&mut char_bytes[..]);
|
||||
self.serialize_str(&char_str)
|
||||
}
|
||||
|
||||
fn serialize_str(self, v: &str) -> Result<()> {
|
||||
self.write_delim(syntax::TEXT_CHAR)?;
|
||||
if C::DISABLE_STRING_ESCAPING {
|
||||
self.write_bytes(v.as_bytes())?;
|
||||
} else {
|
||||
// TODO: Escape characters
|
||||
self.write_bytes(v.as_bytes())?;
|
||||
}
|
||||
self.write_delim(syntax::TEXT_CHAR)
|
||||
}
|
||||
|
||||
fn serialize_bool(self, v: bool) -> Result<()> {
|
||||
self.write_bytes(
|
||||
if v {
|
||||
syntax::BOOLEAN_TRUE_KEYWORD
|
||||
} else {
|
||||
syntax::BOOLEAN_FALSE_KEYWORD
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
|
||||
use serde::ser::SerializeSeq;
|
||||
let mut seq = self.serialize_seq(Some(v.len()))?;
|
||||
for byte in v {
|
||||
seq.serialize_element(byte)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
|
||||
fn serialize_some<T>(self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<()> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<()> {
|
||||
self.write_bytes(b"none")
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, name: &'static str) -> Result<()> {
|
||||
if C::UNIT_STRUCT_TO_KIND {
|
||||
self.write_bytes(name.as_bytes())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<()> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T>(
|
||||
self,
|
||||
name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
tag_start!(self, C::TAG_NEWTYPE_STRUCTS, name);
|
||||
value.serialize(&mut *self)?;
|
||||
tag_end!(self, C::TAG_NEWTYPE_STRUCTS)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.write_delim(syntax::DICT_START_CHAR)?;
|
||||
variant.serialize(&mut *self)?;
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
value.serialize(&mut *self)?;
|
||||
self.write_delim(syntax::DICT_END_CHAR)
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||
self.write_delim(syntax::LIST_START_CHAR)?;
|
||||
self.set_first_element();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
tag_start!(self, C::TAG_TUPLE_STRUCTS, name);
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
tag_start!(self, C::TAG_TUPLE_VARIANTS, variant);
|
||||
self.write_delim(syntax::DICT_START_CHAR)?;
|
||||
variant.serialize(&mut *self)?;
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
self.write_delim(syntax::LIST_START_CHAR)?;
|
||||
self.set_first_element();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
self.write_delim(syntax::DICT_START_CHAR)?;
|
||||
self.set_first_element();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct> {
|
||||
tag_start!(self, C::TAG_STRUCTS, name);
|
||||
self.serialize_map(Some(len))?;
|
||||
self.set_first_element();
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
tag_start!(self, C::TAG_STRUCT_VARIANTS, variant);
|
||||
self.write_delim(syntax::DICT_START_CHAR)?;
|
||||
variant.serialize(&mut *self)?;
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
self.write_delim(syntax::DICT_START_CHAR)?;
|
||||
self.set_first_element();
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeSeq for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::LIST_SEPARATOR_CHAR)?;
|
||||
}
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::LIST_END_CHAR)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeTuple for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::LIST_SEPARATOR_CHAR)?;
|
||||
}
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::LIST_END_CHAR)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeTupleStruct for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::LIST_SEPARATOR_CHAR)?;
|
||||
}
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::LIST_END_CHAR)?;
|
||||
tag_end!(self, C::TAG_TUPLE_STRUCTS)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeTupleVariant for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::LIST_SEPARATOR_CHAR)?;
|
||||
}
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::LIST_END_CHAR)?;
|
||||
self.write_delim(syntax::DICT_END_CHAR)?;
|
||||
tag_end!(self, C::TAG_TUPLE_VARIANTS)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeMap for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::DICT_PAIR_SEPARATOR_CHAR)?;
|
||||
}
|
||||
key.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::DICT_END_CHAR)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeStruct for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::DICT_PAIR_SEPARATOR_CHAR)?;
|
||||
}
|
||||
key.serialize(&mut **self)?;
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::DICT_END_CHAR)?;
|
||||
tag_end!(self, C::TAG_STRUCTS)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, W: Write, C: Config> ser::SerializeStructVariant for &'a mut Serializer<W, C> {
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
if self.check_not_first_element() {
|
||||
self.write_delim(syntax::DICT_PAIR_SEPARATOR_CHAR)?;
|
||||
}
|
||||
key.serialize(&mut **self)?;
|
||||
self.write_delim(syntax::DICT_KV_SEPARATOR_CHAR)?;
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<()> {
|
||||
self.write_delim(syntax::DICT_END_CHAR)?;
|
||||
self.write_delim(syntax::DICT_END_CHAR)?;
|
||||
tag_end!(self, C::TAG_STRUCT_VARIANTS)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[test]
|
||||
fn test_struct() {
|
||||
#[derive(Serialize)]
|
||||
struct Test {
|
||||
int: u32,
|
||||
seq: Vec<&'static str>,
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
int: 1,
|
||||
seq: vec!["a", "b"],
|
||||
};
|
||||
let expected = r#"{"int":1,"seq":["a","b"]}"#;
|
||||
assert_eq!(to_string(&test).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum() {
|
||||
#[derive(Serialize)]
|
||||
enum E {
|
||||
Unit,
|
||||
Newtype(u32),
|
||||
Tuple(u32, u32),
|
||||
Struct { a: u32 },
|
||||
}
|
||||
|
||||
let u = E::Unit;
|
||||
let expected = r#""Unit""#;
|
||||
assert_eq!(to_string(&u).unwrap(), expected);
|
||||
|
||||
let n = E::Newtype(1);
|
||||
let expected = r#"{"Newtype":1}"#;
|
||||
assert_eq!(to_string(&n).unwrap(), expected);
|
||||
|
||||
let t = E::Tuple(1, 2);
|
||||
let expected = r#"{"Tuple":[1,2]}"#;
|
||||
assert_eq!(to_string(&t).unwrap(), expected);
|
||||
|
||||
let s = E::Struct { a: 1 };
|
||||
let expected = r#"{"Struct":{"a":1}}"#;
|
||||
assert_eq!(to_string(&s).unwrap(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple() {
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename = "Test/Unit")]
|
||||
struct TestUnitStruct;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename = "Test/NewtypeStruct")]
|
||||
struct TestNewtypeStruct(i8);
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename = "")]
|
||||
struct TestTransparentUnitStruct;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
struct TestTransparentNewtypeStruct(i8);
|
||||
|
||||
assert_eq!(to_string(&TestUnitStruct).unwrap(), "Test/Unit");
|
||||
assert_eq!(to_string(&TestNewtypeStruct(1)).unwrap(), "Test/NewtypeStruct(1)");
|
||||
assert_eq!(to_string(&TestTransparentUnitStruct).unwrap(), "");
|
||||
assert_eq!(to_string(&TestTransparentNewtypeStruct(1)).unwrap(), "1");
|
||||
}
|
29
sehn-serde/src/ser/string_writer.rs
Normal file
29
sehn-serde/src/ser/string_writer.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use std::io;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
pub struct StringWriter {
|
||||
buf: Vec<u8>
|
||||
}
|
||||
|
||||
impl StringWriter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buf: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(self) -> Result<String, FromUtf8Error> {
|
||||
String::from_utf8(self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for StringWriter {
|
||||
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||
self.buf.extend_from_slice(bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
115
sehn-serde/src/syntax/mod.rs
Normal file
115
sehn-serde/src/syntax/mod.rs
Normal file
@ -0,0 +1,115 @@
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
// Shared
|
||||
pub const COMMA_CHAR: u8 = b',';
|
||||
|
||||
// Newline
|
||||
pub const NEWLINE_LF_CHAR: u8 = b'\n';
|
||||
pub const NEWLINE_CR_CHAR: u8 = b'\r';
|
||||
|
||||
// Whitespace
|
||||
pub const WHITESPACE_TAB_CHAR: u8 = b'\t';
|
||||
pub const WHITESPACE_SPACE_CHAR: u8 = b' ';
|
||||
|
||||
// Real
|
||||
pub const REAL_ZERO_CHAR: u8 = b'0';
|
||||
pub const REAL_NEG_CHAR: u8 = b'-';
|
||||
pub const REAL_SIGEXP_SEPARATOR_CHAR: u8 = b'e';
|
||||
|
||||
// Text
|
||||
pub const TEXT_CHAR: u8 = b'"';
|
||||
pub const MULTI_TEXT_CHAR: u8 = b'`';
|
||||
|
||||
// Kind
|
||||
pub const KIND_NS_SEPARATOR_CHAR: u8 = b'/';
|
||||
|
||||
// Tag
|
||||
pub const TAG_START_CHAR: u8 = b'(';
|
||||
pub const TAG_END_CHAR: u8 = b')';
|
||||
|
||||
// Dict
|
||||
pub const DICT_START_CHAR: u8 = b'{';
|
||||
pub const DICT_END_CHAR: u8 = b'}';
|
||||
pub const DICT_KV_SEPARATOR_CHAR: u8 = b':';
|
||||
pub const DICT_PAIR_SEPARATOR_CHAR: u8 = COMMA_CHAR;
|
||||
|
||||
// List
|
||||
pub const LIST_START_CHAR: u8 = b'[';
|
||||
pub const LIST_END_CHAR: u8 = b']';
|
||||
pub const LIST_SEPARATOR_CHAR: u8 = COMMA_CHAR;
|
||||
|
||||
// Comment
|
||||
pub const COMMENT_CHAR: u8 = b'#';
|
||||
|
||||
// Boolean
|
||||
pub const BOOLEAN_TRUE_KEYWORD: &[u8] = b"true";
|
||||
pub const BOOLEAN_FALSE_KEYWORD: &[u8] = b"false";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Token {
|
||||
// Non-value types
|
||||
Newline,
|
||||
Whitespace,
|
||||
Comment,
|
||||
AlphaLower,
|
||||
AlphaUpper,
|
||||
// Kinds
|
||||
KindNsSep,
|
||||
// Real
|
||||
RealMinus,
|
||||
RealZero,
|
||||
RealNumeric,
|
||||
RealSigExpSep,
|
||||
// Text
|
||||
TextStart,
|
||||
TextEnd,
|
||||
MultiTextStart,
|
||||
MultiTextEnd,
|
||||
// Structures
|
||||
TagStart,
|
||||
TagEnd,
|
||||
DictStart,
|
||||
DictEnd,
|
||||
DictKvSep,
|
||||
DictPairSep,
|
||||
ListStart,
|
||||
ListEnd,
|
||||
ListSep
|
||||
}
|
||||
|
||||
|
||||
impl Token {
|
||||
pub fn description(&self) -> &'static str {
|
||||
match self {
|
||||
Token::Newline => "newline '<lf/<crlf>'",
|
||||
Token::Whitespace => "whitespace '<tab/space>'",
|
||||
Token::Comment => "comment '#'",
|
||||
Token::AlphaLower => "alpha-lower '<a-z>'",
|
||||
Token::AlphaUpper => "alpha-upper '<A-Z>'",
|
||||
Token::KindNsSep => "kind ns separator '/'",
|
||||
Token::RealMinus => "minus '-'",
|
||||
Token::RealZero => "zero '0'",
|
||||
Token::RealNumeric => "number '<0-9>'",
|
||||
Token::RealSigExpSep => "sig-exp separator 'e'",
|
||||
Token::TextStart => "text '\"'",
|
||||
Token::TextEnd => "text end '\"'",
|
||||
Token::MultiTextStart => "multi text '`'",
|
||||
Token::MultiTextEnd => "multi text end '`'",
|
||||
Token::TagStart => "tag '('",
|
||||
Token::TagEnd => "tag end ')'",
|
||||
Token::DictStart => "dict '{'",
|
||||
Token::DictEnd => "dict end '}'",
|
||||
Token::DictKvSep => "dict kv separator ':'",
|
||||
Token::DictPairSep => "dict pair separator ','",
|
||||
Token::ListStart => "list '['",
|
||||
Token::ListEnd => "list end ']'",
|
||||
Token::ListSep => "list separator ','",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "{}", self.description())
|
||||
}
|
||||
}
|
156
sehn-serde/src/syntax/parser.rs
Normal file
156
sehn-serde/src/syntax/parser.rs
Normal file
@ -0,0 +1,156 @@
|
||||
pub struct Mark {
|
||||
i: usize
|
||||
}
|
||||
|
||||
pub trait Read<'a> {
|
||||
// Per char reading.
|
||||
fn next_char(&mut self) -> Result<char>;
|
||||
fn peek_char(&mut self) -> Result<char>;
|
||||
|
||||
// Sectioning off data.
|
||||
fn mark(&self) -> Mark;
|
||||
/// Returns reference to the section.
|
||||
fn from_mark(&mut self, m: Mark) -> &'a str;
|
||||
|
||||
/// Attempt to enter a new level of nesting.
|
||||
fn enter_nest(&mut self) -> Result<()>;
|
||||
/// Leave a nest.
|
||||
fn leave_nest(&mut self);
|
||||
|
||||
/// Called when the parser passes a line.
|
||||
fn passed_line(&mut self);
|
||||
}
|
||||
|
||||
//pub trait Visitor {}
|
||||
|
||||
pub struct Parser<'a, R: Read + 'a> {
|
||||
r: &'a mut R
|
||||
}
|
||||
|
||||
impl<'a, R: Read> Parser<'a, R> {
|
||||
fn parse_value(&mut self) -> Result<()> {
|
||||
loop {
|
||||
match self.r.next() {
|
||||
Some(c) => match c {
|
||||
COMMENT_CHAR => if !self.skip_to_next_line() {
|
||||
return Ok(())
|
||||
},
|
||||
// Whitespace
|
||||
LF_CHAR => self.r.mark_line(),
|
||||
CR_CHAR => {
|
||||
self.skip_char(LF_CHAR);
|
||||
self.r.mark_line();
|
||||
},
|
||||
SPACE_CHAR | TAB_CHAR => {
|
||||
self.skip_one();
|
||||
},
|
||||
// Values
|
||||
DICT_START_CHAR => return self.parse_dict(),
|
||||
LIST_START_CHAR => return self.parse_list(),
|
||||
TEXT_DELIM_CHAR => return self.parse_text(),
|
||||
MULTI_TEXT_DELIM_CHAR => return self.parse_multi_text(),
|
||||
REAL_NEG_CHAR => return self.parse_real_negative(),
|
||||
REAL_ZERO_CHAR => return self.parse_real_positive_decimal(),
|
||||
'1'...'9' => return self.parse_real_positive_int(),
|
||||
'a'...'z' => return self.parse_initial_lowercase(c),
|
||||
'A'...'Z' => return self.parse_initial_uppercase(c),
|
||||
// Unexpected
|
||||
c => return Some(ParseError::Unexpected(c))
|
||||
},
|
||||
None => return Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_one(&mut self) -> bool {
|
||||
match self.r.next() {
|
||||
Some(_) => true,
|
||||
None => false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_char(&mut self, c: char) -> bool {
|
||||
match self.r.peek() {
|
||||
Some(peeked_c) if c == peeked_c => self.skip_one(),
|
||||
Some(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_to_next_line(&mut self) -> bool {
|
||||
loop {
|
||||
match self.r.next() {
|
||||
Some(LF_CHAR) => {
|
||||
self.r.mark_line();
|
||||
return true
|
||||
},
|
||||
Some(CR_CHAR) => {
|
||||
if self.skip_char(LF_CHAR) {
|
||||
self.r.mark_line();
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
Some(_) => return true,
|
||||
None => return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_dict(&mut self) -> Option<ParseError> {
|
||||
if self.r.enter_ctx() {
|
||||
None
|
||||
} else {
|
||||
Some(ParseError::NestingLimit)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_list(&mut self) -> Option<ParseError> {
|
||||
if self.r.enter_ctx() {
|
||||
None
|
||||
} else {
|
||||
Some(ParseError::NestingLimit)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_text(&mut self) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_multi_text(&mut self) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_real_negative(&mut self) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_real_positive_int(&mut self) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_real_positive_decimal(&mut self) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_initial_lowercase(&mut self, c: char) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_initial_uppercase(&mut self, c: char) -> Option<ParseError> {
|
||||
None
|
||||
}
|
||||
}
|
0
sehn-serde/src/value/mod.rs
Normal file
0
sehn-serde/src/value/mod.rs
Normal file
24
sehn-serde/src/value/real/mod.rs
Normal file
24
sehn-serde/src/value/real/mod.rs
Normal file
@ -0,0 +1,24 @@
|
||||
//mod sig_exp;
|
||||
//mod str_int;
|
||||
|
||||
// //pub use self::sig_exp::*;
|
||||
// pub use self::str_int::*;
|
||||
|
||||
// pub type PrimaryInt = i64;
|
||||
// pub type PrimaryNat = u64;
|
||||
|
||||
// #[derive(Debug, PartialEq)]
|
||||
// pub enum ToPrimitiveError {
|
||||
// Overflow,
|
||||
// Underflow
|
||||
//}
|
||||
|
||||
|
||||
// pub enum Real {
|
||||
// /// Zero to the natural primitive max.
|
||||
// Nat(PrimitiveNatValue),
|
||||
// /// Negative to the integer primitive min.
|
||||
// Int(PrimitiveIntValue),
|
||||
// /// SigExp
|
||||
// SigExp(SigExp)
|
||||
// }
|
125
sehn-serde/src/value/real/sig_exp.rs
Normal file
125
sehn-serde/src/value/real/sig_exp.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use super::{
|
||||
StrInt,
|
||||
StringInt,
|
||||
PrimitiveInt,
|
||||
PrimitiveIntValue
|
||||
};
|
||||
|
||||
pub struct StrSigExp<'a> {
|
||||
sig: StrInt<'a>,
|
||||
exp: StrInt<'a>
|
||||
}
|
||||
|
||||
impl<'a> StrSigExp<'a> {
|
||||
#[inline]
|
||||
pub fn from_parts(sig: StrInt<'a>, exp: StrInt<'a>) -> Self {
|
||||
Self {
|
||||
sig,
|
||||
exp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<StrSigExp<'a>> for SigExp {
|
||||
fn from(raw: StrSigExp<'a>) -> SigExp {
|
||||
let raw_exp_digits = raw.exp.digits();
|
||||
|
||||
if raw_exp_digits.len() <= I16_MAX_DIGITS {
|
||||
raw_exp_digits
|
||||
}
|
||||
|
||||
|
||||
if let PrimitiveInt(sig) = PrimitiveInt::from_str_int(raw.sig) {
|
||||
let raw_exp_digits = raw.exp.digits();
|
||||
|
||||
if raw_exp_digits.len() <= I16_MAX_DIGITS {
|
||||
raw_exp_digits
|
||||
}
|
||||
|
||||
if let PrimitiveInt(exp) = PrimitiveInt::from_str_int() {
|
||||
SigExp::Fit {
|
||||
sig,
|
||||
exp
|
||||
}
|
||||
} else {
|
||||
SigExp::Fat {
|
||||
sig: raw.sig.to_string_int(),
|
||||
exp: raw.sig.to_string_int()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SigExp::Massive {
|
||||
sig: raw.sig.to_string_int(),
|
||||
exp: raw.sig.to_string_int()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SigExp {
|
||||
/// When both `sig` AND `exp` can be
|
||||
/// represented by fast primitive
|
||||
/// types without loss of precision.
|
||||
Fit {
|
||||
sig: PrimitiveIntValue,
|
||||
exp: i16
|
||||
},
|
||||
/// When `sig` can NOT be represented
|
||||
/// by a fast primitive type
|
||||
/// without loss of precision.
|
||||
Fat {
|
||||
sig: StringInt,
|
||||
exp: i16
|
||||
},
|
||||
/// When both `sig` OR `exp` can NOT be
|
||||
/// represented by fast primitive types
|
||||
/// without loss of precision.
|
||||
// #[feature(massive_sig_exp)]
|
||||
Massive {
|
||||
sig: StringInt,
|
||||
exp: StringInt
|
||||
}
|
||||
}
|
||||
|
||||
impl SigExp {
|
||||
// pub fn is_fit(&self) -> f64 {
|
||||
|
||||
// }
|
||||
// pub fn map_approx_f64(&self) -> f64 {
|
||||
// match self {
|
||||
// SigExp::Fit { sig, exp } => {
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_str_sig_exp_to_sig_exp_fit() {
|
||||
let sig_str_int = StrInt::new("+1").unwrap();
|
||||
let exp_str_int = StrInt::new("+1").unwrap();
|
||||
let sig_exp = SigExp::from(StrSigExp::from_parts(sig_str_int, exp_str_int));
|
||||
assert_eq!(sig_exp, SigExp::Fit{sig: 1, exp: 1});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_sig_exp_to_sig_exp_fat() {
|
||||
let sig_str_int = StrInt::new("+10000000000000000000").unwrap();
|
||||
let exp_str_int = StrInt::new("+1").unwrap();
|
||||
let sig_exp = SigExp::from(StrSigExp::from_parts(sig_str_int, exp_str_int));
|
||||
assert_eq!(sig_exp, SigExp::Fat{sig: sig_str_int.to_string_int(), exp: 1});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_sig_exp_to_sig_exp_massive() {
|
||||
let sig_str_int = StrInt::new("+10000000000000000000").unwrap();
|
||||
let exp_str_int = StrInt::new("+10000000000000000000").unwrap();
|
||||
let sig_exp = SigExp::from(StrSigExp::from_parts(sig_str_int, exp_str_int));
|
||||
assert_eq!(sig_exp, SigExp::Massive{sig: sig_str_int.to_string_int(), exp: exp_str_int.to_string_int()});
|
||||
}
|
||||
}
|
295
sehn-serde/src/value/real/str_int.rs
Normal file
295
sehn-serde/src/value/real/str_int.rs
Normal file
@ -0,0 +1,295 @@
|
||||
use super::ToPrimitiveError;
|
||||
|
||||
const EMPTY_STR: &str = "";
|
||||
const ZERO_STR: &str = "0";
|
||||
const RADIX_10: u8 = 10;
|
||||
|
||||
macro_rules! max_digits (
|
||||
($x:ident) => (($x::max_value() as f64).log10().floor() as usize + 1)
|
||||
);
|
||||
|
||||
macro_rules! unchecked_str_to_primitive {
|
||||
($n:ident) => {
|
||||
for &c in digits {
|
||||
let x = match (c as char).to_digit(RADIX_10) {
|
||||
Some(x) => x,
|
||||
None => panic!("invalid char")
|
||||
};
|
||||
result = match result.checked_mul(RADIX_10) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
result = match result.checked_add(x) {
|
||||
Some(result) => result,
|
||||
None => return Err(PIE { kind: Overflow }),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_str_int_to_primitive_int {
|
||||
($n:ident, $p:ident) => {
|
||||
pub fn $n(self: &Self) -> Result<$p, ToPrimitiveError> {
|
||||
if self.is_zero() {
|
||||
return Ok(0)
|
||||
}
|
||||
if self.digits().len() <= max_digits!($p) {
|
||||
for digit.
|
||||
if let Ok(v) = $p::from_str_radix(self.digits(), 10) {
|
||||
return Ok(v * self.signum() as $p)
|
||||
}
|
||||
}
|
||||
if self.is_positive() {
|
||||
Err(ToPrimitiveError::Overflow)
|
||||
} else {
|
||||
Err(ToPrimitiveError::Underflow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_str_int_to_primitive_nat {
|
||||
($n:ident, $p:ident) => {
|
||||
pub fn $n(self: &Self) -> Result<$p, ToPrimitiveError> {
|
||||
if self.is_zero() {
|
||||
return Ok(0)
|
||||
}
|
||||
if self.digits().len() <= max_digits!($p) {
|
||||
if let Ok(v) = $p::from_str_radix(self.digits(), 10) {
|
||||
return Ok(v)
|
||||
}
|
||||
}
|
||||
Err(ToPrimitiveError::Overflow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StrIntParseError {
|
||||
InvalidChar,
|
||||
NonZeroEmpty,
|
||||
ZeroWithTrailing
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct StrInt<'a> {
|
||||
signum: i8,
|
||||
digits: &'a str
|
||||
}
|
||||
|
||||
impl<'a> StrInt<'a> {
|
||||
pub fn new(str_int: &'a str) -> Result<Self, StrIntParseError> {
|
||||
match str_int.chars().next() {
|
||||
Some('0') => Self::from_parts(0, &str_int[1..]),
|
||||
Some('-') => Self::from_parts(-1, &str_int[1..]),
|
||||
Some('+') => Self::from_parts(1, &str_int[1..]),
|
||||
_ => Self::from_parts(1, str_int)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_zero() -> Self {
|
||||
Self {
|
||||
signum: 0,
|
||||
digits: EMPTY_STR
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_parts(signum: i8, digits: &'a str) -> Result<Self, StrIntParseError> {
|
||||
if signum == 0 {
|
||||
if digits.len() > 0 {
|
||||
return Err(StrIntParseError::ZeroWithTrailing)
|
||||
}
|
||||
} else {
|
||||
if digits.len() == 0 {
|
||||
return Err(StrIntParseError::NonZeroEmpty)
|
||||
}
|
||||
let maybe_invalid_char = digits.chars().find(|c| match c {
|
||||
'0'...'9' => false,
|
||||
_ => true
|
||||
});
|
||||
if let Some(_) = maybe_invalid_char {
|
||||
return Err(StrIntParseError::InvalidChar)
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
signum,
|
||||
digits
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_parts_unchecked(signum: i8, digits: &'a str) -> Self {
|
||||
Self {
|
||||
signum,
|
||||
digits
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_string_int(&self) -> StringInt {
|
||||
StringInt {
|
||||
signum: self.signum,
|
||||
digits: self.digits.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_positive(&self) -> bool {
|
||||
self.signum > -1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.signum == -1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.signum == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn digits(&self) -> &str {
|
||||
if self.is_zero() {
|
||||
ZERO_STR
|
||||
} else {
|
||||
self.digits
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn signum(&self) -> i8 {
|
||||
self.signum
|
||||
}
|
||||
|
||||
impl_str_int_to_primitive_int!(to_i8, i8);
|
||||
impl_str_int_to_primitive_int!(to_i16, i16);
|
||||
impl_str_int_to_primitive_int!(to_i32, i32);
|
||||
impl_str_int_to_primitive_int!(to_i64, i64);
|
||||
|
||||
impl_str_int_to_primitive_nat!(to_u8, u8);
|
||||
impl_str_int_to_primitive_nat!(to_u16, u16);
|
||||
impl_str_int_to_primitive_nat!(to_u32, u32);
|
||||
impl_str_int_to_primitive_nat!(to_u64, u64);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct StringInt {
|
||||
signum: i8,
|
||||
digits: String
|
||||
}
|
||||
|
||||
impl StringInt {
|
||||
#[inline]
|
||||
pub fn is_positive(&self) -> bool {
|
||||
self.signum > -1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_negative(&self) -> bool {
|
||||
self.signum == -1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.signum == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn digits(&self) -> &str {
|
||||
if self.is_zero() {
|
||||
ZERO_STR
|
||||
} else {
|
||||
self.digits.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_str_int(&self) -> StrInt {
|
||||
StrInt {
|
||||
signum: self.signum,
|
||||
digits: self.digits.as_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const GOOD_INT_STR_CASES: [(&str, StrInt<'static>); 6] = [
|
||||
("0", StrInt {signum: 0, digits: ""}),
|
||||
("1", StrInt {signum: 1, digits: "1"}),
|
||||
("-1", StrInt {signum: -1, digits: "1"}),
|
||||
("123", StrInt {signum: 1, digits: "123"}),
|
||||
("+123", StrInt {signum: 1, digits: "123"}),
|
||||
("-123", StrInt {signum: -1, digits: "123"})
|
||||
];
|
||||
|
||||
const BAD_INT_STR_CASES: [&str; 5] = [
|
||||
"01",
|
||||
"0a",
|
||||
"--",
|
||||
"++",
|
||||
"00"
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_good_str_int_cases() {
|
||||
for (input, expected) in GOOD_INT_STR_CASES.iter() {
|
||||
if let Ok(output) = StrInt::new(input) {
|
||||
assert_eq!(output, *expected);
|
||||
} else {
|
||||
panic!("input {:?}", input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bad_str_int_cases() {
|
||||
for input in BAD_INT_STR_CASES.iter() {
|
||||
assert!(StrInt::new(input).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_int_to_int_primitives() {
|
||||
// Pos
|
||||
assert_eq!(StrInt::new("+128").unwrap().to_i8(), Ok(i8::max_value()));
|
||||
assert_eq!(StrInt::new("+32767").unwrap().to_i16(), Ok(i16::max_value()));
|
||||
assert_eq!(StrInt::new("+2147483647").unwrap().to_i32(), Ok(i32::max_value()));
|
||||
assert_eq!(StrInt::new("+9223372036854775807").unwrap().to_i64(), Ok(i64::max_value()));
|
||||
// Neg
|
||||
assert_eq!(StrInt::new("-127").unwrap().to_i8(), Ok(i8::min_value()));
|
||||
assert_eq!(StrInt::new("-32767").unwrap().to_i16(), Ok(i16::min_value()));
|
||||
assert_eq!(StrInt::new("-2147483647").unwrap().to_i32(), Ok(i32::min_value()));
|
||||
assert_eq!(StrInt::new("-9223372036854775807").unwrap().to_i64(), Ok(i64::min_value()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_int_to_nat_primitives() {
|
||||
assert_eq!(StrInt::new("+255").unwrap().to_u8(), Ok(u8::max_value()));
|
||||
assert_eq!(StrInt::new("+65535").unwrap().to_u16(), Ok(u16::max_value()));
|
||||
assert_eq!(StrInt::new("+4294967295").unwrap().to_u32(), Ok(u32::max_value()));
|
||||
assert_eq!(StrInt::new("+18446744073709551615").unwrap().to_u64(), Ok(u64::max_value()));
|
||||
assert_eq!(StrInt::new("-255").unwrap().to_u8(), Ok(u8::max_value()));
|
||||
assert_eq!(StrInt::new("-65535").unwrap().to_u16(), Ok(u16::max_value()));
|
||||
assert_eq!(StrInt::new("-4294967295").unwrap().to_u32(), Ok(u32::max_value()));
|
||||
assert_eq!(StrInt::new("-18446744073709551615").unwrap().to_u64(), Ok(u64::max_value()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_parse_overflow_underflow() {
|
||||
let definite_overflow = "+256";
|
||||
let definite_underflow = "-256";
|
||||
assert_eq!(StrInt::new(definite_overflow).unwrap().to_i8(), Err(ToPrimitiveError::Overflow));
|
||||
assert_eq!(StrInt::new(definite_underflow).unwrap().to_i8(), Err(ToPrimitiveError::Underflow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nat_parse_overflow() {
|
||||
let definite_overflow_1 = "+256";
|
||||
let definite_overflow_2 = "-256";
|
||||
assert_eq!(StrInt::new(definite_overflow_1).unwrap().to_u8(), Err(ToPrimitiveError::Overflow));
|
||||
assert_eq!(StrInt::new(definite_overflow_2).unwrap().to_u8(), Err(ToPrimitiveError::Overflow));
|
||||
}
|
||||
}
|
3
sehn-serde/src/value/text.rs
Normal file
3
sehn-serde/src/value/text.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub struct Multiline {
|
||||
lines: Vec<String>
|
||||
}
|
Loading…
Reference in New Issue
Block a user