use core::marker;
#[cfg(feature = "alloc")]
use rust_alloc::string::String;
#[cfg(feature = "alloc")]
use rust_alloc::vec::Vec;
#[cfg(feature = "alloc")]
use crate::alloc::Global;
use crate::mode::Text;
use crate::{Allocator, Context, Decode, Encode, IntoWriter, Writer};
use super::de::CsvDecoder;
use super::en::CsvEncoder;
#[cfg(feature = "alloc")]
use super::error::Error;
use super::parser::{IntoParser, Parser};
use super::reader::Reader;
use super::writer::Writer as CsvWriter;
#[allow(unused)]
const DEFAULT: Encoding = Encoding::new();
crate::macros::bare_encoding!(Text, DEFAULT, csv, IntoParser, IntoWriter);
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn to_string<T>(value: &T) -> Result<String, Error>
where
T: ?Sized + Encode<Text>,
{
DEFAULT.to_string(value)
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn from_str<'de, T>(string: &'de str) -> Result<T, Error>
where
T: Decode<'de, Text, Global>,
{
DEFAULT.from_str(string)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Terminator {
CRLF,
Any(u8),
}
impl Terminator {
#[inline]
pub(crate) const fn read_byte(self) -> Option<u8> {
match self {
Terminator::CRLF | Terminator::Any(b'\n') => None,
Terminator::Any(b) => Some(b),
}
}
#[inline]
pub(crate) const fn write_bytes(&self) -> &[u8] {
match self {
Terminator::CRLF => b"\r\n",
Terminator::Any(b) => core::slice::from_ref(b),
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct Config {
pub(crate) delimiter: u8,
pub(crate) quote: u8,
pub(crate) terminator: Terminator,
pub(crate) has_headers: bool,
pub(crate) flexible: bool,
}
pub struct Encoding<M = Text>
where
M: 'static,
{
config: Config,
_marker: marker::PhantomData<M>,
}
impl Default for Encoding<Text> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Encoding<Text> {
#[inline]
pub const fn new() -> Self {
Encoding {
config: Config {
delimiter: b',',
quote: b'"',
terminator: Terminator::Any(b'\n'),
has_headers: true,
flexible: false,
},
_marker: marker::PhantomData,
}
}
}
impl<M> Encoding<M>
where
M: 'static,
{
#[inline]
pub const fn with_mode<T>(self) -> Encoding<T>
where
T: 'static,
{
Encoding {
config: self.config,
_marker: marker::PhantomData,
}
}
#[inline]
pub const fn with_delimiter(self, delimiter: u8) -> Self {
Self {
config: Config {
delimiter,
..self.config
},
..self
}
}
#[inline]
pub const fn with_quote(self, quote: u8) -> Self {
Self {
config: Config {
quote,
..self.config
},
..self
}
}
#[inline]
pub const fn with_terminator(self, terminator: Terminator) -> Self {
Self {
config: Config {
terminator,
..self.config
},
..self
}
}
#[inline]
pub const fn with_headers(self, has_headers: bool) -> Self {
Self {
config: Config {
has_headers,
..self.config
},
..self
}
}
#[inline]
pub const fn with_flexible(self, flexible: bool) -> Self {
Self {
config: Config {
flexible,
..self.config
},
..self
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn reader<'de, B>(self, input: &'de B) -> Reader<'de, M, Global>
where
B: ?Sized + AsRef<[u8]>,
{
Reader::new(self.config, input.as_ref(), Global::new())
}
#[inline]
pub fn reader_in<'de, B, A>(self, input: &'de B, alloc: A) -> Reader<'de, M, A>
where
B: ?Sized + AsRef<[u8]>,
A: Allocator,
{
Reader::new(self.config, input.as_ref(), alloc)
}
#[inline]
pub fn writer<W>(self, writer: W) -> CsvWriter<W::Writer, M>
where
W: IntoWriter,
{
CsvWriter::new(self.config, writer.into_writer())
}
#[inline]
fn decoder<'de, C, P>(self, cx: C, parser: P) -> CsvDecoder<P, C, M>
where
C: Context,
P: Parser<'de>,
{
CsvDecoder::new(cx, parser, self.config)
}
#[inline]
fn encode_into<C, W, T>(self, cx: C, writer: W, value: &T) -> Result<(), C::Error>
where
C: Context,
W: Writer,
T: ?Sized + Encode<M>,
{
T::encode(value, CsvEncoder::new(cx, writer, self.config, value))
}
crate::macros::encoding_impls!(
custom,
M,
csv,
self.decoder,
IntoParser::into_parser,
crate::csv::parser::Parser,
IntoWriter::into_writer,
);
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn to_string<T>(self, value: &T) -> Result<String, Error>
where
T: ?Sized + Encode<M>,
{
let cx = crate::context::new().with_error();
self.to_string_with(&cx, value)
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[inline]
pub fn to_string_with<T, C>(self, cx: C, value: &T) -> Result<String, C::Error>
where
C: Context,
T: ?Sized + Encode<M>,
{
cx.clear();
let mut data = Vec::with_capacity(128);
self.encode_into(cx, &mut data, value)?;
match String::from_utf8(data) {
Ok(string) => Ok(string),
Err(..) => Err(cx.message("Encoded CSV is not valid UTF-8")),
}
}
}
impl<M> Clone for Encoding<M> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<M> Copy for Encoding<M> {}