Move sehn to sehn-serde part 2
This commit is contained in:
		
							parent
							
								
									05ca67db3b
								
							
						
					
					
						commit
						e49e427e5a
					
				@ -1,5 +1,5 @@
 | 
			
		||||
[workspace]
 | 
			
		||||
members = [
 | 
			
		||||
	"sehn",
 | 
			
		||||
	"sehn-serde",
 | 
			
		||||
	"sehn-std"
 | 
			
		||||
]
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "sehn"
 | 
			
		||||
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"
 | 
			
		||||
@ -1,19 +0,0 @@
 | 
			
		||||
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::to_string(&val).unwrap());
 | 
			
		||||
}
 | 
			
		||||
@ -1,32 +0,0 @@
 | 
			
		||||
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()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
mod error;
 | 
			
		||||
 | 
			
		||||
pub use self::error::*;
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +0,0 @@
 | 
			
		||||
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 {}
 | 
			
		||||
}
 | 
			
		||||
@ -1,33 +0,0 @@
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
@ -1,25 +0,0 @@
 | 
			
		||||
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()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
use super::{Format, RealFormat, WriteReal};
 | 
			
		||||
 | 
			
		||||
pub struct DeterministicRealFormat;
 | 
			
		||||
 | 
			
		||||
impl RealFormat for DeterministicRealFormat {}
 | 
			
		||||
 | 
			
		||||
pub struct DeterministicFormat;
 | 
			
		||||
 | 
			
		||||
impl Format for DeterministicFormat {
 | 
			
		||||
	type Engine = ();
 | 
			
		||||
	type RealFormat = DeterministicRealFormat;
 | 
			
		||||
}
 | 
			
		||||
@ -1,30 +0,0 @@
 | 
			
		||||
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 {}
 | 
			
		||||
@ -1,40 +0,0 @@
 | 
			
		||||
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(())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,117 +0,0 @@
 | 
			
		||||
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)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,8 +0,0 @@
 | 
			
		||||
use super::{Format, FastRealFormat};
 | 
			
		||||
 | 
			
		||||
pub struct StandardFormat;
 | 
			
		||||
 | 
			
		||||
impl Format for StandardFormat {
 | 
			
		||||
	type Engine = ();
 | 
			
		||||
	type RealFormat = FastRealFormat;
 | 
			
		||||
}
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
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(())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,491 +0,0 @@
 | 
			
		||||
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");
 | 
			
		||||
}
 | 
			
		||||
@ -1,29 +0,0 @@
 | 
			
		||||
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(())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,115 +0,0 @@
 | 
			
		||||
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())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,156 +0,0 @@
 | 
			
		||||
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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
//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)
 | 
			
		||||
// }
 | 
			
		||||
@ -1,125 +0,0 @@
 | 
			
		||||
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()});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,295 +0,0 @@
 | 
			
		||||
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));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,3 +0,0 @@
 | 
			
		||||
pub struct Multiline {
 | 
			
		||||
	lines: Vec<String>
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user