Better separation between puiblic/private types in exported API #43

Merged
bazaah merged 5 commits from feature/event/hide-scalar-internals into master 2022-03-27 10:51:02 +00:00
12 changed files with 287 additions and 137 deletions

View File

@ -255,11 +255,11 @@ where
/// for e in events.iter().filter_map(Result::ok)
/// {
/// if let EventData::Scalar(Node {
/// content: Scalar::Eager { data, .. },
/// content: ScalarLike::Eager(scalar),
/// ..
/// }) = e.data()
/// {
/// if let Ok(digit) = data.parse::<i32>()
/// if let Ok(digit) = scalar.parse::<i32>()
/// {
/// println!("Got digit: {}", digit);
/// }

View File

@ -111,7 +111,7 @@ macro_rules! state {
macro_rules! consume {
($queue:expr, $kind:tt) => {{
#[allow(unused_imports)]
use $crate::{token::Token::*, scanner::entry::MaybeToken, event::types::{Event, EventData, VersionDirective, Scalar}};
use $crate::{token::Token::*, scanner::entry::MaybeToken, event::types::{Event, EventData, VersionDirective, ScalarLike}};
#[allow(clippy::collapsible_match)]
pop!($queue).map(|entry| consume!(@wrap entry, $kind))
@ -122,10 +122,10 @@ macro_rules! consume {
match $entry.wrap {
MaybeToken::Token(token) => match token {
Scalar(data, style) => (end, end, Scalar::Eager { data, style }),
Scalar(data, style) => (end, end, ScalarLike::eager(data, style)),
_ => unreachable!(),
},
MaybeToken::Deferred(lazy) => (end, end, Scalar::Lazy { data: Box::new(lazy) })
MaybeToken::Deferred(lazy) => (end, end, ScalarLike::lazy(lazy))
}
}};
(@wrap $entry:expr, $kind:tt) => {{

View File

@ -200,10 +200,9 @@ macro_rules! scalar {
}
};
($content:expr, $style:expr) => {
types::Scalar::Eager {
data: $crate::token::Slice::from($content),
style: $style,
}
types::ScalarLike::eager(
$crate::token::Slice::from($content), $style
)
};
}

View File

@ -11,15 +11,17 @@ use std::{array::IntoIter as ArrayIter, borrow::Cow, collections::HashMap};
use crate::{
scanner::{entry::Lazy, error::ScanResult},
token::{ScalarStyle, Slice, StreamEncoding, Token},
token::Token,
};
pub type Slice<'a> = std::borrow::Cow<'a, str>;
pub const DEFAULT_TAGS: [(Slice<'static>, Slice<'static>); 2] = [
(Cow::Borrowed("!"), Cow::Borrowed("!")),
(Cow::Borrowed("!!"), Cow::Borrowed("tag:yaml.org,2002:")),
];
pub const DEFAULT_VERSION: VersionDirective = VersionDirective { major: 1, minor: 2 };
pub const EMPTY_SCALAR: Scalar<'static> = Scalar::empty();
pub const EMPTY_SCALAR: ScalarLike<'static> = ScalarLike::empty();
/// Specific YAML productions found in the YAML stream. Each
/// Event has a start and end mark indicating an approximate
@ -97,7 +99,7 @@ pub enum EventData<'de>
Alias(Alias<'de>),
/// A scalar leaf node, containing (perhaps lazy)
/// unicode slice content
Scalar(Node<'de, Scalar<'de>>),
Scalar(Node<'de, ScalarLike<'de>>),
/// Start of a YAML key value production, followed by
/// zero or more of:
@ -145,85 +147,168 @@ pub struct Node<'de, T: 'de>
/// evaluated, in which case a caller may trigger a fallible
/// evaluation on demand.
#[derive(Debug, Clone)]
pub enum Scalar<'de>
pub enum ScalarLike<'de>
{
Eager
{
data: Slice<'de>,
style: ScalarStyle,
},
Lazy
{
data: Box<Lazy<'de>>
},
Eager(Scalar<'de>),
Lazy(ScalarLazy<'de>),
}
impl<'de> Scalar<'de>
impl<'de> ScalarLike<'de>
{
pub fn evaluate(self) -> ScanResult<Self>
pub fn evaluate(self) -> Result<Scalar<'de>, crate::Error>
{
match self
{
eager @ Scalar::Eager { .. } => Ok(eager),
Scalar::Lazy { data } => data.into_token().map(|token| match token
{
Token::Scalar(data, style) => Self::Eager { data, style },
// Only scalars can be deferred
_ => unreachable!(),
}),
}
self.evaluate_scalar().map_err(Into::into)
}
pub fn ref_evaluate(&mut self) -> ScanResult<()>
pub fn evaluate_by_ref(&mut self) -> Result<&mut Scalar<'de>, crate::Error>
{
self.evaluate_scalar_by_ref().map_err(Into::into)
}
pub fn is_evaluated(&self) -> bool
{
!self.is_lazy()
}
pub fn is_unevaluated(&self) -> bool
{
self.is_lazy()
}
pub(crate) fn evaluate_scalar_by_ref(&mut self) -> ScanResult<&mut Scalar<'de>>
{
let this = std::mem::take(self);
*self = this.evaluate()?;
*self = Self::Eager(this.evaluate_scalar()?);
Ok(())
match self
{
ScalarLike::Eager(scalar) => Ok(scalar),
_ => unreachable!(),
}
}
pub(crate) fn evaluate_scalar(self) -> ScanResult<Scalar<'de>>
{
match self
{
Self::Eager(scalar) => Ok(scalar),
Self::Lazy(lazy) => lazy.evaluate_scalar(),
}
}
pub(crate) fn eager(data: Slice<'de>, style: ScalarStyle) -> Self
{
Self::Eager(Scalar { data, style })
}
pub(crate) fn lazy(lazy: Lazy<'de>) -> Self
{
Self::Lazy(ScalarLazy { inner: lazy })
}
const fn is_lazy(&self) -> bool
{
matches!(self, Self::Lazy(_))
}
}
impl Scalar<'static>
impl ScalarLike<'static>
{
pub const fn empty() -> Self
{
Self::Eager {
Self::Eager(Scalar {
data: Slice::Borrowed(""),
style: ScalarStyle::Plain,
}
})
}
}
impl<'de> PartialEq for Scalar<'de>
impl Default for ScalarLike<'_>
{
fn default() -> Self
{
ScalarLike::empty()
}
}
impl<'de> PartialEq for ScalarLike<'de>
{
fn eq(&self, other: &Self) -> bool
{
match (self, other)
{
(
Self::Eager {
data: l_data,
style: l_style,
},
Self::Eager {
data: r_data,
style: r_style,
},
) => l_data == r_data && l_style == r_style,
(Self::Eager(s), Self::Eager(o)) => s == o,
// No ordering is established between yet-to-be-evaluated scalars
_ => false,
}
}
}
impl Default for Scalar<'_>
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Scalar<'de>
{
fn default() -> Self
data: Slice<'de>,
style: ScalarStyle,
}
impl<'de> Scalar<'de>
{
pub fn data(&self) -> &Slice
{
Self::Eager {
data: "".into(),
style: ScalarStyle::Plain,
}
&self.data
}
pub fn data_mut(&mut self) -> &mut Slice<'de>
{
&mut self.data
}
pub fn style(&self) -> ScalarStyle
{
self.style
}
}
impl<'de> AsRef<str> for Scalar<'de>
{
fn as_ref(&self) -> &str
{
&*self.data
}
}
impl<'de> std::ops::Deref for Scalar<'de>
{
type Target = str;
fn deref(&self) -> &Self::Target
{
&*self.data
}
}
#[derive(Debug, Clone)]
pub struct ScalarLazy<'de>
{
inner: Lazy<'de>,
}
impl<'de> ScalarLazy<'de>
{
pub fn evaluate(self) -> Result<Scalar<'de>, crate::Error>
{
self.evaluate_scalar().map_err(Into::into)
}
pub(crate) fn evaluate_scalar(self) -> ScanResult<Scalar<'de>>
{
self.inner.into_token().map(|t| match t
{
Token::Scalar(data, style) => Scalar { data, style },
// Only scalars can be deferred
_ => unreachable!(),
})
}
}
@ -352,3 +437,19 @@ pub struct VersionDirective
/// Typedef map of tag directives present in the current
/// document
pub type TagDirectives<'de> = HashMap<Slice<'de>, Slice<'de>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StreamEncoding
{
UTF8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ScalarStyle
{
Plain,
SingleQuote,
DoubleQuote,
Literal,
Folded,
}

View File

@ -4,15 +4,11 @@
* was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
use super::{error::ReaderResult as Result, private, Read, Reader};
use crate::{
queue::Queue,
scanner::{
entry::TokenEntry,
flag::{Flags, O_EXTENDABLE},
Scanner,
},
use super::{
error::{ReadError, ReaderResult},
private, Read, ReadContext, Reader,
};
use crate::scanner::flag::{Flags, O_EXTENDABLE};
#[derive(Debug, Clone)]
pub struct BorrowReader<'de>
@ -27,7 +23,7 @@ impl<'de> BorrowReader<'de>
Self { data }
}
pub(crate) fn try_from_bytes(data: &'de [u8]) -> Result<Self>
pub(crate) fn try_from_bytes(data: &'de [u8]) -> ReaderResult<Self>
{
let this = std::str::from_utf8(data).map(Self::new)?;
@ -42,21 +38,17 @@ impl<'de> BorrowReader<'de>
impl<'a> Read for BorrowReader<'a>
{
fn drive<'de>(
&'de self,
scanner: &mut Scanner,
queue: &mut Queue<TokenEntry<'de>>,
options: Flags,
) -> Result<()>
fn drive<'de>(&'de self, cxt: ReadContext<'_, '_, 'de>) -> Result<(), ReadError>
{
// This implementation is never extendable, so we remove the
// option from the set if it exists
scanner.scan_tokens(options & !O_EXTENDABLE, self.data, queue)?;
cxt.scanner
.scan_tokens(cxt.flags & !O_EXTENDABLE, self.data, cxt.queue)?;
Ok(())
}
unsafe fn consume(&self, _bound: usize) -> Result<()>
unsafe fn consume(&self, _bound: usize) -> Result<(), ReadError>
{
Ok(())
}

View File

@ -15,12 +15,12 @@ use crate::{
};
/// Type alias of the `Result`s returned from this module
pub type ReaderResult<T> = std::result::Result<T, ReaderError>;
pub(crate) type ReaderResult<T> = std::result::Result<T, ReaderError>;
/// Possible errors that can occur while reading from YAML
/// byte streams
#[derive(Debug)]
pub enum ReaderError
pub(crate) enum ReaderError
{
/// Encountered invalid an UTF8 sequence
UTF8(Utf8Error),
@ -95,3 +95,43 @@ impl From<ReaderError> for crate::error::Error
crate::error::mkError!(err, KIND)
}
}
/// An intentionally opaque type which hides the
/// implementation details of [`Read`] errors.
#[repr(transparent)]
pub struct ReadError
{
pub(crate) e: ReaderError,
}
impl ReadError
{
pub(crate) fn new(e: ReaderError) -> Self
{
Self { e }
}
}
impl From<ReaderError> for ReadError
{
fn from(err: ReaderError) -> Self
{
Self::new(err)
}
}
impl From<ReadError> for ReaderError
{
fn from(err: ReadError) -> Self
{
err.e
}
}
impl From<ScanError> for ReadError
{
fn from(err: ScanError) -> Self
{
Self::new(ReaderError::from(err))
}
}

View File

@ -13,9 +13,12 @@ use crate::{
error::Error,
queue::Queue,
reader::{
borrow::BorrowReader, error::ReaderResult as Result, owned::OwnedReader, private::Sealed,
borrow::BorrowReader,
error::{ReadError, ReaderResult},
owned::OwnedReader,
private::Sealed,
},
scanner::{entry::TokenEntry, flag::Flags, Scanner},
scanner::{entry::TokenEntry, flag::Flags as ScannerFlags, Scanner},
};
/// Instantiate a new [`Read`]er from the given UTF8 string
@ -92,12 +95,7 @@ pub trait Read: std::fmt::Debug + Sealed
/// Drive the .scanner from the byte stream with the
/// provided .options, placing output into the
/// .queue
fn drive<'de>(
&'de self,
scanner: &mut Scanner,
queue: &mut Queue<TokenEntry<'de>>,
options: Flags,
) -> Result<()>;
fn drive<'de>(&'de self, cxt: ReadContext<'_, '_, 'de>) -> Result<(), ReadError>;
/// Hint to the underlying implementation that no live
/// references exist to any data read below
@ -109,12 +107,37 @@ pub trait Read: std::fmt::Debug + Sealed
/// It is only safe to call this function after the
/// caller has ensured there cannot be any live
/// references to content below the provided .bound.
unsafe fn consume(&self, _bound: usize) -> Result<()>
unsafe fn consume(&self, _bound: usize) -> Result<(), ReadError>
{
Ok(())
}
}
/// An intentionally opaque type which hides the
/// implementation details of [`Read`] methods.
pub struct ReadContext<'a, 'b, 'de>
{
scanner: &'a mut Scanner,
queue: &'b mut Queue<TokenEntry<'de>>,
flags: ScannerFlags,
}
impl<'a, 'b, 'de> ReadContext<'a, 'b, 'de>
{
fn new(
scanner: &'a mut Scanner,
queue: &'b mut Queue<TokenEntry<'de>>,
flags: ScannerFlags,
) -> Self
{
Self {
scanner,
queue,
flags,
}
}
}
/// Responsible for driving a Read implementation,
/// tokenizing the byte stream in preparation for
/// for an Event stream
@ -124,7 +147,7 @@ pub(crate) struct Reader<'de, T: 'de>
scanner: Scanner,
queue: Queue<TokenEntry<'de>>,
options: Flags,
options: ScannerFlags,
exhausted: bool,
inner: &'de T,
@ -134,7 +157,7 @@ impl<'de, T> Reader<'de, T>
where
T: Read,
{
pub fn new(read: &'de T, opts: Flags) -> Self
pub fn new(read: &'de T, opts: ScannerFlags) -> Self
{
Self {
scanner: Scanner::new(),
@ -145,12 +168,15 @@ where
}
}
pub fn scan_tokens(&mut self) -> Result<&mut Queue<TokenEntry<'de>>>
pub fn scan_tokens(&mut self) -> ReaderResult<&mut Queue<TokenEntry<'de>>>
{
let start = self.queue.len();
self.inner
.drive(&mut self.scanner, &mut self.queue, self.options)?;
self.inner.drive(ReadContext::new(
&mut self.scanner,
&mut self.queue,
self.options,
))?;
self.exhausted = start == self.queue.len();
@ -174,7 +200,7 @@ where
pub(crate) fn from_parts(
read: &'de T,
options: Flags,
options: ScannerFlags,
queue: Queue<TokenEntry<'de>>,
exhausted: bool,
) -> Self
@ -205,7 +231,7 @@ where
Self { peek: None, reader }
}
pub fn pop(&mut self) -> Result<Option<TokenEntry<'de>>>
pub fn pop(&mut self) -> ReaderResult<Option<TokenEntry<'de>>>
{
match self.peek.take()
{
@ -220,7 +246,7 @@ where
}
}
pub fn peek(&mut self) -> Result<Option<&mut TokenEntry<'de>>>
pub fn peek(&mut self) -> ReaderResult<Option<&mut TokenEntry<'de>>>
{
match self.peek
{
@ -252,7 +278,7 @@ where
self.reader.queue()
}
fn take_next(&mut self) -> Result<()>
fn take_next(&mut self) -> ReaderResult<()>
{
// Ensure we don't overwrite an existing entry
if self.peek.is_some()
@ -289,7 +315,7 @@ mod test_util
pub(super) type TestResult = anyhow::Result<()>;
pub(super) const TEST_FLAGS: Flags = O_ZEROED;
pub(super) const TEST_FLAGS: ScannerFlags = O_ZEROED;
pub(super) const TEST_DATA: [&str; 3] = [YAML_SCALAR, YAML_SEQUENCE, YAML_MAPPING];
pub(super) const YAML_SCALAR: &str = "'a simple, root scalar'";

View File

@ -6,7 +6,11 @@
use std::{cell::UnsafeCell, fmt, io};
use super::{error::ReaderResult as Result, private::Sealed, Read, Reader};
use super::{
error::{ReadError, ReaderResult},
private::Sealed,
Read, ReadContext, Reader,
};
use crate::{
queue::Queue,
scanner::{
@ -46,7 +50,7 @@ impl OwnedReader
scanner: &mut Scanner,
queue: &mut Queue<TokenEntry<'de>>,
mut opts: Flags,
) -> Result<()>
) -> ReaderResult<()>
{
loop
{
@ -76,17 +80,13 @@ impl OwnedReader
impl Read for OwnedReader
{
fn drive<'de>(
&'de self,
scanner: &mut Scanner,
queue: &mut Queue<TokenEntry<'de>>,
options: Flags,
) -> Result<()>
fn drive<'de>(&'de self, cxt: ReadContext<'_, '_, 'de>) -> Result<(), ReadError>
{
self.drive_scanner(scanner, queue, options)
self.drive_scanner(cxt.scanner, cxt.queue, cxt.flags)
.map_err(Into::into)
}
unsafe fn consume(&self, _bound: usize) -> Result<()>
unsafe fn consume(&self, _bound: usize) -> Result<(), ReadError>
{
Ok(())
}
@ -111,7 +111,7 @@ impl ReadHolder
Self { inner }
}
pub fn read_next_chunk(&self, read_to: Option<usize>) -> Result<()>
pub fn read_next_chunk(&self, read_to: Option<usize>) -> ReaderResult<()>
{
let inner: &mut Impl = unsafe { &mut *self.inner.get() };
@ -180,7 +180,7 @@ impl Impl
&self.head
}
fn refresh_buffer(&mut self, copy_from: Option<usize>) -> Result<()>
fn refresh_buffer(&mut self, copy_from: Option<usize>) -> ReaderResult<()>
{
// Calculate next allocation chunk
let cap = (DEFAULT_BUFFER_SIZE * usize::max(self.tail.len(), 1) + copy_from.unwrap_or(0))

View File

@ -20,15 +20,15 @@ use crate::{
/// Note that this wrapper *does not* compare tokens, so if
/// you desire that ensure that you compare them directly
#[derive(Debug)]
pub struct TokenEntry<'de>
pub(crate) struct TokenEntry<'de>
{
pub wrap: MaybeToken<'de>,
read_at: usize,
pub(crate) wrap: MaybeToken<'de>,
read_at: usize,
}
impl<'de> TokenEntry<'de>
{
pub fn new<T>(token: T, read_at: usize) -> Self
pub(crate) fn new<T>(token: T, read_at: usize) -> Self
where
T: Into<MaybeToken<'de>>,
{
@ -38,22 +38,22 @@ impl<'de> TokenEntry<'de>
}
}
pub fn read_at(&self) -> usize
pub(crate) fn read_at(&self) -> usize
{
self.read_at
}
pub fn marker(&self) -> Marker
pub(crate) fn marker(&self) -> Marker
{
self.wrap.marker()
}
pub fn is_processed(&self) -> bool
pub(crate) fn is_processed(&self) -> bool
{
matches!(&self.wrap, MaybeToken::Token(_))
}
pub fn into_token(self) -> Result<Token<'de>>
pub(crate) fn into_token(self) -> Result<Token<'de>>
{
self.wrap.into_token()
}
@ -86,7 +86,7 @@ impl<'de> Ord for TokenEntry<'de>
}
#[derive(Debug)]
pub enum MaybeToken<'de>
pub(crate) enum MaybeToken<'de>
{
Token(Token<'de>),
Deferred(Lazy<'de>),
@ -132,7 +132,7 @@ where
}
#[derive(Debug, Clone)]
pub struct Lazy<'de>
pub(crate) struct Lazy<'de>
{
inner: LazyImpl<'de>,
}

View File

@ -8,10 +8,10 @@ use std::fmt;
use crate::error::internal::ErrorCode;
pub type ScanResult<T> = std::result::Result<T, ScanError>;
pub(crate) type ScanResult<T> = std::result::Result<T, ScanError>;
#[derive(Debug, PartialEq, Eq)]
pub enum ScanError
pub(crate) enum ScanError
{
/// Directive was not either YAML or TAG
UnknownDirective,
@ -131,3 +131,11 @@ impl From<ScanError> for ErrorCode
}
}
}
impl From<ScanError> for crate::error::Error
{
fn from(err: ScanError) -> Self
{
crate::error::mkError!(err, CODE)
}
}

View File

@ -41,7 +41,7 @@ use crate::{
type Tokens<'de> = Queue<TokenEntry<'de>>;
#[derive(Debug)]
pub struct Scanner
pub(crate) struct Scanner
{
/// Offset into the data buffer to start at
offset: usize,

View File

@ -4,7 +4,7 @@
* was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
pub type Slice<'a> = std::borrow::Cow<'a, str>;
pub use crate::event::types::{ScalarStyle, Slice, StreamEncoding};
/// Tokens that may be emitted by a YAML scanner
#[derive(Debug, PartialEq)]
@ -199,19 +199,3 @@ impl PartialEq<Token<'_>> for Marker
self == &Self::from(other)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum StreamEncoding
{
UTF8,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScalarStyle
{
Plain,
SingleQuote,
DoubleQuote,
Literal,
Folded,
}