factor out crate
This commit is contained in:
parent
bb8fa818d2
commit
af09bcd403
11 changed files with 43 additions and 22 deletions
123
logparse/src/ast.rs
Normal file
123
logparse/src/ast.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use proptest_derive::Arbitrary;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Separator {
|
||||
Eq,
|
||||
Colon,
|
||||
DoubleColon,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Arbitrary, PartialEq)]
|
||||
pub enum QuoteType {
|
||||
Single,
|
||||
Double,
|
||||
Backtick,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Arbitrary, PartialEq)]
|
||||
pub enum Delimiter {
|
||||
Paren,
|
||||
Bracket,
|
||||
Brace,
|
||||
Angle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AnyString<'a> {
|
||||
pub prefix: Cow<'a, str>,
|
||||
pub ty: QuoteType,
|
||||
pub contents: Cow<'a, str>,
|
||||
pub num_hashtags: usize,
|
||||
pub suffix: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Space<'a>(pub Cow<'a, str>);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Arbitrary)]
|
||||
pub enum PathSep {
|
||||
/// Happens at the start of paths, for the no leading / case
|
||||
None,
|
||||
Slash,
|
||||
Backslash,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PathSegment<'a> {
|
||||
pub leading_separator: PathSep,
|
||||
pub segment: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FileLocation<'a> {
|
||||
pub line: Cow<'a, str>,
|
||||
pub offset: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FileName<'a> {
|
||||
pub leading_separator: PathSep,
|
||||
pub segment: Cow<'a, str>,
|
||||
pub ext_excluding_dot: Option<Cow<'a, str>>,
|
||||
pub location: Option<FileLocation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Path<'a> {
|
||||
pub drive_excluding_colon: Option<char>,
|
||||
|
||||
pub segments: Vec<PathSegment<'a>>,
|
||||
pub filename: FileName<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Number<'a>(pub Cow<'a, str>);
|
||||
|
||||
/// Anything that doesn't contain spaces, and that can be a prefix of `Delimited`.
|
||||
/// i.e. an english word, or rust `::`-separated Path
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Atom<'a> {
|
||||
Text(Cow<'a, str>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Token<'a> {
|
||||
True,
|
||||
False,
|
||||
None,
|
||||
|
||||
Path(Path<'a>),
|
||||
String(AnyString<'a>),
|
||||
Number(Number<'a>),
|
||||
|
||||
// TODO: RustPath
|
||||
Separated {
|
||||
before: Box<Token<'a>>,
|
||||
space_before: Space<'a>,
|
||||
separator: Separator,
|
||||
after: Box<Segment<'a>>,
|
||||
},
|
||||
Delimited(Delimited<'a>),
|
||||
|
||||
Atom(Atom<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Delimited<'a> {
|
||||
pub prefix: Option<Atom<'a>>,
|
||||
pub delimiter: Delimiter,
|
||||
pub contents: Segments<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Segment<'a> {
|
||||
pub leading_space: Space<'a>,
|
||||
pub token: Token<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Segments<'a> {
|
||||
pub segments: Vec<Segment<'a>>,
|
||||
pub trailing_space: Space<'a>,
|
||||
}
|
||||
187
logparse/src/display.rs
Normal file
187
logparse/src/display.rs
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
use super::ast::*;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
impl Display for Separator {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Separator::Eq => write!(f, "="),
|
||||
Separator::Colon => write!(f, ":"),
|
||||
Separator::DoubleColon => write!(f, "::"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for QuoteType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
QuoteType::Single => write!(f, "\'"),
|
||||
QuoteType::Double => write!(f, "\""),
|
||||
QuoteType::Backtick => write!(f, "`"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Delimiter {
|
||||
fn fmt_start(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Delimiter::Paren => write!(f, "("),
|
||||
Delimiter::Bracket => write!(f, "["),
|
||||
Delimiter::Brace => write!(f, "{{"),
|
||||
Delimiter::Angle => write!(f, "<"),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_end(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Delimiter::Paren => write!(f, ")"),
|
||||
Delimiter::Bracket => write!(f, "]"),
|
||||
Delimiter::Brace => write!(f, "}}"),
|
||||
Delimiter::Angle => write!(f, ">"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for AnyString<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.prefix)?;
|
||||
for _ in 0..self.num_hashtags {
|
||||
write!(f, "#")?;
|
||||
}
|
||||
write!(f, "{}", self.ty)?;
|
||||
write!(f, "{}", self.contents)?;
|
||||
write!(f, "{}", self.ty)?;
|
||||
|
||||
for _ in 0..self.num_hashtags {
|
||||
write!(f, "#")?;
|
||||
}
|
||||
write!(f, "{}", self.suffix)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Space<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PathSep {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
PathSep::Slash => write!(f, "/"),
|
||||
PathSep::Backslash => write!(f, "\\"),
|
||||
PathSep::None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for PathSegment<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.leading_separator)?;
|
||||
write!(f, "{}", self.segment)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for FileLocation<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, ":{}", self.line)?;
|
||||
if let Some(offset) = &self.offset {
|
||||
write!(f, ":{offset}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for FileName<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.leading_separator)?;
|
||||
write!(f, "{}", self.segment)?;
|
||||
if let Some(ext) = &self.ext_excluding_dot {
|
||||
write!(f, ".{ext}")?;
|
||||
}
|
||||
if let Some(loc) = &self.location {
|
||||
write!(f, "{loc}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Path<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
drive_excluding_colon,
|
||||
segments,
|
||||
filename,
|
||||
} = self;
|
||||
|
||||
if let Some(drive) = &drive_excluding_colon {
|
||||
write!(f, "{drive}:")?;
|
||||
}
|
||||
|
||||
for segment in segments {
|
||||
write!(f, "{segment}")?;
|
||||
}
|
||||
write!(f, "{filename}")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Number<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Atom<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Atom::Text(text) => write!(f, "{text}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Token<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Token::Number(number) => write!(f, "{number}"),
|
||||
Token::True => write!(f, "true"),
|
||||
Token::False => write!(f, "false"),
|
||||
Token::None => write!(f, "None"),
|
||||
Token::Atom(atom) => write!(f, "{atom}"),
|
||||
Token::Path(path) => write!(f, "{path}"),
|
||||
Token::Separated {
|
||||
before,
|
||||
space_before,
|
||||
separator,
|
||||
after,
|
||||
} => write!(f, "{before}{space_before}{separator}{after}"),
|
||||
Token::Delimited(delimited) => write!(f, "{delimited}"),
|
||||
Token::String(s) => write!(f, "{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Delimited<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(prefix) = &self.prefix {
|
||||
write!(f, "{prefix}")?;
|
||||
}
|
||||
self.delimiter.fmt_start(f)?;
|
||||
write!(f, "{}", self.contents)?;
|
||||
self.delimiter.fmt_end(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Segment<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.leading_space)?;
|
||||
write!(f, "{}", self.token)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Display for Segments<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for segment in &self.segments {
|
||||
write!(f, "{segment}")?;
|
||||
}
|
||||
write!(f, "{}", self.trailing_space)
|
||||
}
|
||||
}
|
||||
10
logparse/src/lib.rs
Normal file
10
logparse/src/lib.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
pub mod ast;
|
||||
mod display;
|
||||
mod parse;
|
||||
mod spans;
|
||||
|
||||
#[cfg(test)]
|
||||
mod proptesting;
|
||||
|
||||
pub use parse::parse_input;
|
||||
pub use spans::{Config, Kind as SpanKind, Span, into_spans};
|
||||
769
logparse/src/parse.rs
Normal file
769
logparse/src/parse.rs
Normal file
|
|
@ -0,0 +1,769 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use super::ast::*;
|
||||
use winnow::{Parser, combinator::trace, error::ParserError};
|
||||
|
||||
impl<'a> AnyString<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
let quote = alt((
|
||||
'`'.value(QuoteType::Backtick),
|
||||
'\''.value(QuoteType::Single),
|
||||
'\"'.value(QuoteType::Double),
|
||||
));
|
||||
|
||||
macro_rules! surrounding {
|
||||
() => {
|
||||
take_while(0.., |b: char| {
|
||||
!b.is_whitespace() && b.is_alphabetic() && !['\'', '"', '`', '#'].contains(&b)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
let preamble = (
|
||||
surrounding!(),
|
||||
take_while(0.., |c| c == '#').map(|i: &'a str| i.len()),
|
||||
quote,
|
||||
);
|
||||
|
||||
trace(
|
||||
"string",
|
||||
preamble.flat_map(
|
||||
|(prefix, num_hashtags, quote): (&'a str, usize, QuoteType)| {
|
||||
let end = (
|
||||
match quote {
|
||||
QuoteType::Single => '\'',
|
||||
QuoteType::Double => '\"',
|
||||
QuoteType::Backtick => '`',
|
||||
},
|
||||
repeat::<_, _, Cow<'a, str>, _, _>(
|
||||
num_hashtags..=num_hashtags,
|
||||
literal("#"),
|
||||
),
|
||||
);
|
||||
|
||||
let contents = repeat_till(0.., any, end).map(|(contents, _)| contents);
|
||||
|
||||
(contents, surrounding!()).map(
|
||||
move |(contents, suffix): (Cow<'a, str>, &'a str)| Self {
|
||||
prefix: prefix.into(),
|
||||
ty: quote,
|
||||
contents,
|
||||
num_hashtags,
|
||||
suffix: suffix.into(),
|
||||
},
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Space<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{prelude::*, token::*};
|
||||
|
||||
trace(
|
||||
"space",
|
||||
take_while(0.., |b: char| b.is_whitespace()).map(|i: &'a str| Self(i.into())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PathSep {
|
||||
fn parse<'a, E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
||||
trace(
|
||||
"pathsep",
|
||||
alt(('/'.value(Self::Slash), '\\'.value(Self::Backslash))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Separator {
|
||||
fn parse<'a, E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
trace(
|
||||
"separator",
|
||||
alt((
|
||||
"::".value(Self::DoubleColon),
|
||||
(literal('=')).value(Self::Eq),
|
||||
(literal(':')).value(Self::Colon),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FileName<'a> {
|
||||
fn parse(segment: PathSegment<'a>) -> Self {
|
||||
fn rsplit<'a>(
|
||||
input: Cow<'a, str>,
|
||||
delimiter: char,
|
||||
) -> Option<(Cow<'a, str>, Cow<'a, str>)> {
|
||||
match input {
|
||||
Cow::Borrowed(s) => s
|
||||
.rsplit_once(delimiter)
|
||||
.map(|(a, b)| (Cow::Borrowed(a), Cow::Borrowed(b))),
|
||||
Cow::Owned(s) => s
|
||||
.rsplit_once(delimiter)
|
||||
.map(|(a, b)| (Cow::Owned(a.to_string()), Cow::Owned(b.to_string()))),
|
||||
}
|
||||
}
|
||||
|
||||
let (rest, location) = if let Some((before, offset)) = rsplit(segment.segment.clone(), ':')
|
||||
{
|
||||
if let Some((before, line)) = rsplit(before.clone(), ':') {
|
||||
(
|
||||
before,
|
||||
Some(FileLocation {
|
||||
line,
|
||||
offset: Some(offset),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
before,
|
||||
Some(FileLocation {
|
||||
line: offset,
|
||||
offset: None,
|
||||
}),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(segment.segment, None)
|
||||
};
|
||||
|
||||
let (new_segment, ext_excluding_dot) =
|
||||
if let Some((segment, ext_excluding_dot)) = rsplit(rest.clone(), '.') {
|
||||
(segment, Some(ext_excluding_dot))
|
||||
} else {
|
||||
(rest, None)
|
||||
};
|
||||
|
||||
Self {
|
||||
leading_separator: segment.leading_separator,
|
||||
segment: new_segment,
|
||||
ext_excluding_dot,
|
||||
location,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Path<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
let terminator = || {
|
||||
alt((
|
||||
eof.value(()),
|
||||
any::<&'a str, E>
|
||||
.verify(|i: &char| {
|
||||
(*i).is_whitespace()
|
||||
|| !(i.is_alphanumeric()
|
||||
|| ['_', '-', '\"', '\'', '.', '/', '\\'].contains(i))
|
||||
})
|
||||
.value(()),
|
||||
))
|
||||
};
|
||||
|
||||
let terminator_or_sep = || alt((PathSep::parse().value(()), terminator()));
|
||||
|
||||
let till_next_sep = || {
|
||||
trace(
|
||||
"till next sep",
|
||||
repeat_till(0.., any::<&'a str, E>, peek(terminator_or_sep()))
|
||||
.map(|(segment, _)| segment),
|
||||
)
|
||||
};
|
||||
|
||||
let sep_and_next =
|
||||
(PathSep::parse(), till_next_sep()).map(|(leading_separator, segment)| PathSegment {
|
||||
leading_separator,
|
||||
segment,
|
||||
});
|
||||
let opt_sep_and_next =
|
||||
(opt(PathSep::parse()), till_next_sep()).map(|(leading_separator, segment)| {
|
||||
PathSegment {
|
||||
leading_separator: leading_separator.unwrap_or(PathSep::None),
|
||||
segment,
|
||||
}
|
||||
});
|
||||
|
||||
let drive = opt((
|
||||
any::<&'a str, E>.verify(|x: &char| matches!(*x, 'A'..='Z' | 'a' ..= 'z')),
|
||||
':',
|
||||
))
|
||||
.map(|i| i.map(|(letter, _): (char, char)| letter));
|
||||
let drive_and_segments = (
|
||||
drive,
|
||||
opt_sep_and_next,
|
||||
repeat_till(0.., sep_and_next, peek(terminator()))
|
||||
.map(|(segments, _): (Vec<PathSegment>, _)| segments),
|
||||
);
|
||||
|
||||
trace(
|
||||
"path",
|
||||
drive_and_segments
|
||||
.map(|(drive, segment, segments)| {
|
||||
let (segments, last) = {
|
||||
let mut segments = segments;
|
||||
segments.insert(0, segment);
|
||||
let last = segments.pop().unwrap();
|
||||
(segments, last)
|
||||
};
|
||||
|
||||
let filename = FileName::parse(last);
|
||||
|
||||
Self {
|
||||
drive_excluding_colon: drive,
|
||||
segments,
|
||||
filename,
|
||||
}
|
||||
})
|
||||
.verify(|i| {
|
||||
!i.segments.is_empty()
|
||||
|| i.drive_excluding_colon.is_some()
|
||||
|| i.filename.ext_excluding_dot.is_some()
|
||||
|| !matches!(i.filename.leading_separator, PathSep::None)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Atom<'a> {
|
||||
fn parse<E: ParserError<&'a str>, T: 'a>(
|
||||
terminated_by: impl Parser<&'a str, T, E>,
|
||||
) -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
let text = repeat::<_, _, Cow<'a, str>, _, _>(
|
||||
1..,
|
||||
(
|
||||
peek(not(terminated_by)),
|
||||
any::<&str, _>.verify(move |i: &char| !(*i).is_whitespace()),
|
||||
)
|
||||
.map(|(_, i)| i),
|
||||
)
|
||||
.map(Self::Text);
|
||||
|
||||
trace("atom", alt((text,)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Token<'a> {
|
||||
fn parse_without_separator<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
||||
let delimited: Box<dyn Parser<&'a str, Self, E>> =
|
||||
Box::new(Delimited::parse().map(Self::Delimited));
|
||||
|
||||
trace(
|
||||
"token-without-sep",
|
||||
alt((
|
||||
"true".value(Self::True),
|
||||
"false".value(Self::False),
|
||||
"None".value(Self::None),
|
||||
Path::parse().map(Self::Path),
|
||||
Number::parse().map(Self::Number),
|
||||
AnyString::parse().map(Self::String),
|
||||
delimited,
|
||||
Atom::parse(alt((Separator::parse().value(""), ")", "]", "}"))).map(Self::Atom),
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> Box<dyn Parser<&'a str, Self, E> + 'a> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
||||
let before = Self::parse_without_separator();
|
||||
|
||||
Box::new(trace(
|
||||
"token",
|
||||
alt((
|
||||
(
|
||||
before,
|
||||
opt(
|
||||
(Space::parse(), Separator::parse()).flat_map(|(space, sep)| {
|
||||
let box_dyn_segment: Box<dyn Parser<_, _, _>> =
|
||||
Box::new(Segment::parse());
|
||||
box_dyn_segment.map(move |segment| (space.clone(), sep, segment))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.map(|(before, trailer)| {
|
||||
if let Some((space_before, separator, after)) = trailer {
|
||||
Token::Separated {
|
||||
before: Box::new(before),
|
||||
space_before,
|
||||
separator,
|
||||
after: Box::new(after),
|
||||
}
|
||||
} else {
|
||||
before
|
||||
}
|
||||
}),
|
||||
Atom::parse(fail::<_, (), _>).map(Self::Atom),
|
||||
)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Delimited<'a> {
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{combinator::*, prelude::*, token::*};
|
||||
|
||||
trace(
|
||||
"delimited",
|
||||
(
|
||||
opt(Atom::parse(alt((
|
||||
"(",
|
||||
"[",
|
||||
"{",
|
||||
Separator::parse().value(""),
|
||||
)))),
|
||||
alt((
|
||||
literal('(').map(|_| literal(')').value(Delimiter::Paren)),
|
||||
literal('[').map(|_| literal(']').value(Delimiter::Bracket)),
|
||||
literal('{').map(|_| literal('}').value(Delimiter::Brace)),
|
||||
))
|
||||
.flat_map(|end| Segments::parse(end)),
|
||||
)
|
||||
.map(|(prefix, (contents, delimiter))| Self {
|
||||
prefix,
|
||||
delimiter,
|
||||
contents,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Segments<'a> {
|
||||
fn parse<E: ParserError<&'a str> + 'a, End: 'a>(
|
||||
end: impl Parser<&'a str, End, E> + 'a,
|
||||
) -> Box<dyn Parser<&'a str, (Self, End), E> + 'a> {
|
||||
use winnow::{combinator::*, prelude::*};
|
||||
|
||||
Box::new(trace(
|
||||
"segments",
|
||||
repeat_till(0.., Segment::parse(), (Space::parse(), end)).map(
|
||||
|(segments, (trailing_space, end)): (Vec<_>, _)| {
|
||||
(
|
||||
Self {
|
||||
segments,
|
||||
trailing_space,
|
||||
},
|
||||
end,
|
||||
)
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Number<'a> {
|
||||
fn parse<E: ParserError<&'a str>>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::{ascii::*, combinator::*, prelude::*, token::*};
|
||||
|
||||
trace(
|
||||
"number",
|
||||
(
|
||||
alt((float::<_, f64, _>.take(), dec_int::<_, i64, _>.take())),
|
||||
peek(not(any::<&'a str, E>.verify(|x: &char| x.is_alphabetic()))),
|
||||
)
|
||||
.map(|(i, _): (&str, _)| Self(i.into())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Segment<'a> {
|
||||
fn parse<E: ParserError<&'a str> + 'a>() -> impl Parser<&'a str, Self, E> {
|
||||
use winnow::prelude::*;
|
||||
|
||||
trace(
|
||||
"segment",
|
||||
(Space::parse(), Token::parse()).map(|(leading_space, token)| Self {
|
||||
leading_space,
|
||||
token,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_input<'a>(i: &'a str) -> Result<Segments<'a>, String> {
|
||||
use winnow::combinator::eof;
|
||||
Segments::parse(eof::<&str, winnow::error::EmptyError>)
|
||||
.map(|(segments, _)| segments)
|
||||
.parse(i)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
use winnow::Parser;
|
||||
|
||||
use crate::format_debug_output::{
|
||||
ast::{Path, Segments},
|
||||
parse_input,
|
||||
};
|
||||
|
||||
fn parse_path_only<'a>(i: &'a str) -> Path<'a> {
|
||||
Path::parse::<winnow::error::EmptyError>().parse(i).unwrap()
|
||||
}
|
||||
|
||||
fn parse<'a>(input: &'a str) -> Segments<'a> {
|
||||
parse_input(input).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_path() {
|
||||
assert_debug_snapshot!(parse_path_only(r#"tests/ui/impl-trait/unsized_coercion.rs"#), @r#"
|
||||
Path {
|
||||
drive_excluding_colon: None,
|
||||
segments: [
|
||||
PathSegment {
|
||||
leading_separator: None,
|
||||
segment: "tests",
|
||||
},
|
||||
PathSegment {
|
||||
leading_separator: Slash,
|
||||
segment: "ui",
|
||||
},
|
||||
PathSegment {
|
||||
leading_separator: Slash,
|
||||
segment: "impl-trait",
|
||||
},
|
||||
],
|
||||
filename: FileName {
|
||||
leading_separator: Slash,
|
||||
segment: "unsized_coercion",
|
||||
ext_excluding_dot: Some(
|
||||
"rs",
|
||||
),
|
||||
location: None,
|
||||
},
|
||||
}
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_path_with_file_line() {
|
||||
assert_debug_snapshot!(parse_path_only(r#"tests/ui/impl-trait/unsized_coercion.rs:3:4"#), @r#"
|
||||
Path {
|
||||
drive_excluding_colon: None,
|
||||
segments: [
|
||||
PathSegment {
|
||||
leading_separator: None,
|
||||
segment: "tests",
|
||||
},
|
||||
PathSegment {
|
||||
leading_separator: Slash,
|
||||
segment: "ui",
|
||||
},
|
||||
PathSegment {
|
||||
leading_separator: Slash,
|
||||
segment: "impl-trait",
|
||||
},
|
||||
],
|
||||
filename: FileName {
|
||||
leading_separator: Slash,
|
||||
segment: "unsized_coercion",
|
||||
ext_excluding_dot: Some(
|
||||
"rs",
|
||||
),
|
||||
location: Some(
|
||||
FileLocation {
|
||||
line: "3",
|
||||
offset: Some(
|
||||
"4",
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty() {
|
||||
assert_debug_snapshot!(parse(r#""#), @r#"
|
||||
Segments {
|
||||
segments: [],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_text() {
|
||||
assert_debug_snapshot!(parse(r#"abc"#), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: Atom(
|
||||
Text(
|
||||
"abc",
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_boolean() {
|
||||
assert_debug_snapshot!(parse(r#"true"#), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: True,
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r#"false"#), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: False,
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_string() {
|
||||
assert_debug_snapshot!(parse(r##""foo""##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "",
|
||||
ty: Double,
|
||||
contents: "foo",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"#"foo"#"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "",
|
||||
ty: Double,
|
||||
contents: "foo",
|
||||
num_hashtags: 1,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"r#"foo"#"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "r",
|
||||
ty: Double,
|
||||
contents: "foo",
|
||||
num_hashtags: 1,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"c"foo""##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "c",
|
||||
ty: Double,
|
||||
contents: "foo",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"b"foo""##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "b",
|
||||
ty: Double,
|
||||
contents: "foo",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"'a'"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "",
|
||||
ty: Single,
|
||||
contents: "a",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"`b`"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "",
|
||||
ty: Backtick,
|
||||
contents: "b",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"b'foo'"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "b",
|
||||
ty: Single,
|
||||
contents: "foo",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
assert_debug_snapshot!(parse(r##"b`foo`"##), @r#"
|
||||
Segments {
|
||||
segments: [
|
||||
Segment {
|
||||
leading_space: Space(
|
||||
"",
|
||||
),
|
||||
token: String(
|
||||
AnyString {
|
||||
prefix: "b",
|
||||
ty: Backtick,
|
||||
contents: "foo",
|
||||
num_hashtags: 0,
|
||||
suffix: "",
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
trailing_space: Space(
|
||||
"",
|
||||
),
|
||||
}
|
||||
"#);
|
||||
}
|
||||
}
|
||||
191
logparse/src/proptesting.rs
Normal file
191
logparse/src/proptesting.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
use super::ast::*;
|
||||
use crate::format_debug_output::parse_input;
|
||||
use crate::format_debug_output::{Config, into_spans};
|
||||
use proptest::prelude::*;
|
||||
use proptest::proptest;
|
||||
|
||||
proptest! {
|
||||
#[test]
|
||||
fn proptest_from_segments(original in Segments::arb(Token::arb())) {
|
||||
let stringified = original.to_string();
|
||||
let parsed = parse_input(&stringified).unwrap();
|
||||
let stringified_again = parsed.to_string();
|
||||
assert_eq!(stringified, stringified_again, "parsed: `{parsed:#?}`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proptest_from_random_text(original in ".*") {
|
||||
let parsed = parse_input(&original).unwrap();
|
||||
let stringified_again = parsed.to_string();
|
||||
assert_eq!(original, stringified_again, "parsed: `{parsed:#?}`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proptest_into_spans(original in Segments::arb(Token::arb())) {
|
||||
let stringified = original.to_string();
|
||||
let spans = into_spans(original, Config {collapse_space: false});
|
||||
let spans_concatenated = spans.into_iter().map(|i| i.text).collect::<String>();
|
||||
assert_eq!(stringified, spans_concatenated);
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyString<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
let prefix = "\\w*";
|
||||
let ty = any::<QuoteType>();
|
||||
let contents = "\\w*";
|
||||
let num_hashtags = 0usize..3;
|
||||
let suffix = "\\w*";
|
||||
|
||||
(prefix, ty, contents, num_hashtags, suffix).prop_map(
|
||||
|(prefix, ty, contents, num_hashtags, suffix)| Self {
|
||||
prefix: prefix.into(),
|
||||
ty,
|
||||
contents: contents.into(),
|
||||
num_hashtags,
|
||||
suffix: suffix.into(),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl PathSegment<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
(any::<PathSep>(), "\\w*").prop_map(|(leading_separator, segment)| Self {
|
||||
leading_separator,
|
||||
segment: segment.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLocation<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::option::*;
|
||||
("[0-9]{0,4}", of("[0-9]{0,4}")).prop_map(|(line, offset)| Self {
|
||||
line: line.into(),
|
||||
offset: offset.map(Into::into),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Space<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
" *".prop_map(|spaces| Self(spaces.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FileName<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::option::*;
|
||||
(
|
||||
any::<PathSep>(),
|
||||
"\\w*",
|
||||
of(".{0,3}"),
|
||||
of(FileLocation::arb()),
|
||||
)
|
||||
.prop_map(
|
||||
|(leading_separator, segment, ext_excluding_dot, location)| Self {
|
||||
leading_separator,
|
||||
segment: segment.into(),
|
||||
ext_excluding_dot: ext_excluding_dot.map(Into::into),
|
||||
location,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Path<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
use proptest::{char::*, collection::*, option::*};
|
||||
(
|
||||
of(range('A', 'Z')),
|
||||
vec(PathSegment::arb(), 0..3),
|
||||
FileName::arb(),
|
||||
)
|
||||
.prop_map(|(drive_excluding_colon, segments, filename)| Self {
|
||||
drive_excluding_colon,
|
||||
segments,
|
||||
filename,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Number<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
prop_oneof![
|
||||
any::<i64>().prop_map(|number| Self(number.to_string().into())),
|
||||
any::<f64>().prop_map(|number| Self(number.to_string().into()))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl Atom<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
"[a-zA-Z]+".prop_map(|i| Self::Text(i.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Token<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb() -> impl Strategy<Value = Self> {
|
||||
let leaf = prop_oneof![
|
||||
Just(Self::True),
|
||||
Just(Self::False),
|
||||
Just(Self::None),
|
||||
Path::arb().prop_map(Self::Path),
|
||||
AnyString::arb().prop_map(Self::String),
|
||||
Number::arb().prop_map(Self::Number),
|
||||
Atom::arb().prop_map(Self::Atom),
|
||||
];
|
||||
|
||||
leaf.prop_recursive(4, 64, 16, |token| {
|
||||
Delimited::arb(token).prop_map(Self::Delimited).boxed()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Delimited<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
use proptest::option::*;
|
||||
(of(Atom::arb()), any::<Delimiter>(), Segments::arb(token)).prop_map(
|
||||
|(prefix, delimiter, contents)| Self {
|
||||
prefix,
|
||||
delimiter,
|
||||
contents,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Segment<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
(Space::arb(), token).prop_map(|(leading_space, token)| Self {
|
||||
leading_space,
|
||||
token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Segments<'static> {
|
||||
#[cfg(test)]
|
||||
fn arb(token: impl Strategy<Value = Token<'static>>) -> impl Strategy<Value = Self> {
|
||||
use proptest::collection::*;
|
||||
|
||||
(vec(Segment::arb(token), 1..10), Space::arb()).prop_map(|(segments, trailing_space)| {
|
||||
Self {
|
||||
segments,
|
||||
trailing_space,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
975
logparse/src/spans.rs
Normal file
975
logparse/src/spans.rs
Normal file
|
|
@ -0,0 +1,975 @@
|
|||
use super::ast::*;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Kind {
|
||||
/// Parentheses e.g.
|
||||
///
|
||||
/// Stores the delimiter depth, for e.g. rainbow delimiters.
|
||||
Delimiter(usize),
|
||||
/// Separators like `=`, ':' and ','
|
||||
Separator,
|
||||
/// Numbers
|
||||
Number,
|
||||
/// Known literals, like `true`, `false`, `None`, `Ok`, `Err`
|
||||
Literal,
|
||||
/// Strings
|
||||
String,
|
||||
/// Paths
|
||||
Path,
|
||||
/// Spaces (returns original number of spaces)
|
||||
Space(usize),
|
||||
|
||||
/// Constructor: the prefix of a delimited block.
|
||||
/// i.e. `Some` in `Some(3)`
|
||||
Constructor,
|
||||
|
||||
/// String prefix, suffix, hashtags, etc
|
||||
StringSurroundings,
|
||||
|
||||
/// Any other text (the default)
|
||||
Text,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Span<'a> {
|
||||
pub text: Cow<'a, str>,
|
||||
pub kind: Kind,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub collapse_space: bool,
|
||||
}
|
||||
|
||||
pub struct Context<'a> {
|
||||
config: Config,
|
||||
res: Vec<Span<'a>>,
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn push(&mut self, text: impl Into<Cow<'a, str>>, kind: Kind) {
|
||||
self.res.push(Span {
|
||||
text: text.into(),
|
||||
kind,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoSpans<'a>: private::IntoSpansImpl<'a> {}
|
||||
|
||||
pub fn into_spans<'a>(ast: impl IntoSpans<'a>, config: Config) -> Vec<Span<'a>> {
|
||||
let mut cx = Context {
|
||||
config,
|
||||
res: Vec::new(),
|
||||
depth: 0,
|
||||
};
|
||||
ast.into_spans(&mut cx);
|
||||
cx.res
|
||||
}
|
||||
|
||||
mod private {
|
||||
use super::*;
|
||||
|
||||
pub trait IntoSpansImpl<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>);
|
||||
}
|
||||
|
||||
impl<'a, T> IntoSpans<'a> for T where T: IntoSpansImpl<'a> {}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Separator {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
match self {
|
||||
Separator::Eq => cx.push("=", Kind::Separator),
|
||||
Separator::Colon => cx.push(":", Kind::Separator),
|
||||
Separator::DoubleColon => cx.push("::", Kind::Separator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for QuoteType {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
match self {
|
||||
QuoteType::Single => cx.push("'", Kind::Separator),
|
||||
QuoteType::Double => cx.push("\"", Kind::Separator),
|
||||
QuoteType::Backtick => cx.push("`", Kind::Separator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for AnyString<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
let Self {
|
||||
prefix,
|
||||
ty,
|
||||
contents,
|
||||
num_hashtags,
|
||||
suffix,
|
||||
} = self;
|
||||
cx.push(prefix, Kind::StringSurroundings);
|
||||
for _ in 0..num_hashtags {
|
||||
cx.push("#", Kind::StringSurroundings)
|
||||
}
|
||||
|
||||
ty.into_spans(cx);
|
||||
cx.push(contents, Kind::String);
|
||||
ty.into_spans(cx);
|
||||
|
||||
for _ in 0..num_hashtags {
|
||||
cx.push("#", Kind::StringSurroundings)
|
||||
}
|
||||
cx.push(suffix, Kind::StringSurroundings);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Path<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
cx.push(self.to_string(), Kind::Path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Number<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
cx.push(self.0, Kind::Number)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Atom<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
match self {
|
||||
Atom::Text(text) => cx.push(text, Kind::Text),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Space<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
match self.0.len() {
|
||||
0 => {}
|
||||
1 => cx.push(self.0, Kind::Space(1)),
|
||||
n if cx.config.collapse_space => cx.push(" ", Kind::Space(n)),
|
||||
n => cx.push(self.0, Kind::Space(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Token<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
match self {
|
||||
Token::True => cx.push("true", Kind::Literal),
|
||||
Token::False => cx.push("false", Kind::Literal),
|
||||
Token::None => cx.push("None", Kind::Literal),
|
||||
Token::Path(path) => path.into_spans(cx),
|
||||
Token::String(string) => string.into_spans(cx),
|
||||
Token::Number(number) => number.into_spans(cx),
|
||||
Token::Separated {
|
||||
before,
|
||||
space_before,
|
||||
separator,
|
||||
after,
|
||||
} => {
|
||||
before.into_spans(cx);
|
||||
space_before.into_spans(cx);
|
||||
separator.into_spans(cx);
|
||||
after.into_spans(cx);
|
||||
}
|
||||
Token::Delimited(delimited) => {
|
||||
delimited.into_spans(cx);
|
||||
}
|
||||
Token::Atom(atom) => {
|
||||
atom.into_spans(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Segment<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
let Self {
|
||||
leading_space,
|
||||
token,
|
||||
} = self;
|
||||
leading_space.into_spans(cx);
|
||||
token.into_spans(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Segments<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
let Self {
|
||||
segments,
|
||||
trailing_space,
|
||||
} = self;
|
||||
for segment in segments {
|
||||
segment.into_spans(cx);
|
||||
}
|
||||
trailing_space.into_spans(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoSpansImpl<'a> for Delimited<'a> {
|
||||
fn into_spans(self, cx: &mut Context<'a>) {
|
||||
let Self {
|
||||
prefix,
|
||||
delimiter,
|
||||
contents,
|
||||
} = self;
|
||||
|
||||
match prefix {
|
||||
Some(Atom::Text(text)) => cx.push(text, Kind::Constructor),
|
||||
None => {}
|
||||
}
|
||||
|
||||
match delimiter {
|
||||
Delimiter::Paren => cx.push("(", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Bracket => cx.push("[", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Brace => cx.push("{", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Angle => cx.push("<", Kind::Delimiter(cx.depth)),
|
||||
}
|
||||
|
||||
cx.depth += 1;
|
||||
contents.into_spans(cx);
|
||||
cx.depth -= 1;
|
||||
|
||||
match delimiter {
|
||||
Delimiter::Paren => cx.push(")", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Bracket => cx.push("]", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Brace => cx.push("}", Kind::Delimiter(cx.depth)),
|
||||
Delimiter::Angle => cx.push(">", Kind::Delimiter(cx.depth)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
|
||||
use super::Kind;
|
||||
use crate::format_debug_output::{Config, into_spans, parse_input};
|
||||
|
||||
fn spans(input: &str) -> Vec<(String, Kind)> {
|
||||
let res = parse_input(input).unwrap();
|
||||
into_spans(
|
||||
res,
|
||||
Config {
|
||||
collapse_space: true,
|
||||
},
|
||||
)
|
||||
.into_iter()
|
||||
.map(|i| (i.text.into_owned(), i.kind))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_ex1() {
|
||||
assert_debug_snapshot!(spans(
|
||||
r#"def_id=DefId(0:3 ~ unsized_coercion[10fa]::Trait)"#
|
||||
), @r#"
|
||||
[
|
||||
(
|
||||
"def_id",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"DefId",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"0",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"3",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"unsized_coercion",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"[",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"10fa",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"]",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"Trait",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
]
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_ex2() {
|
||||
assert_debug_snapshot!(spans(
|
||||
r#"data=TypeNs("MetaSized") visible_parent=DefId(2:3984 ~ core[bcc4]::marker) actual_parent=Some(DefId(2:3984 ~ core[bcc4]::marker))"#
|
||||
), @r#"
|
||||
[
|
||||
(
|
||||
"data",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"TypeNs",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"",
|
||||
StringSurroundings,
|
||||
),
|
||||
(
|
||||
"\"",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"MetaSized",
|
||||
String,
|
||||
),
|
||||
(
|
||||
"\"",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"",
|
||||
StringSurroundings,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"visible_parent",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"DefId",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"2",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"3984",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"core",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"[",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"bcc4",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"]",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"marker",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"actual_parent",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"Some",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"DefId",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"2",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"3984",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"core",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"[",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
"bcc4",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"]",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"marker",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
]
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_ex3() {
|
||||
assert_debug_snapshot!(spans(
|
||||
r#"insert(DefId(0:4 ~ unsized_coercion[10fa]::{impl#0})): inserting TraitRef <u32 as Trait> into specialization graph"#
|
||||
), @r#"
|
||||
[
|
||||
(
|
||||
"insert",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"DefId",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"0",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"4",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"unsized_coercion",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"[",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
"10fa",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"]",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"{",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
"impl#0",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"}",
|
||||
Delimiter(
|
||||
2,
|
||||
),
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"inserting",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"TraitRef",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"<u32",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"as",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"Trait>",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"into",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"specialization",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"graph",
|
||||
Text,
|
||||
),
|
||||
]
|
||||
"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spans_ex4() {
|
||||
assert_debug_snapshot!(spans(
|
||||
r#"inspecting def_id=DefId(3:662 ~ alloc[ef11]::boxed::Box) span=tests/ui/impl-trait/unsized_coercion.rs:12:15: 12:30 (#0)"#
|
||||
), @r##"
|
||||
[
|
||||
(
|
||||
"inspecting",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"def_id",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"DefId",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"3",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"662",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"~",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"alloc",
|
||||
Constructor,
|
||||
),
|
||||
(
|
||||
"[",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"ef11",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"]",
|
||||
Delimiter(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"boxed",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"::",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"Box",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"span",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
"=",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"tests/ui/impl-trait/unsized_coercion.rs",
|
||||
Path,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"12",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"15",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"12",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
":",
|
||||
Separator,
|
||||
),
|
||||
(
|
||||
"30",
|
||||
Number,
|
||||
),
|
||||
(
|
||||
" ",
|
||||
Space(
|
||||
1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"(",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
(
|
||||
"#0",
|
||||
Text,
|
||||
),
|
||||
(
|
||||
")",
|
||||
Delimiter(
|
||||
0,
|
||||
),
|
||||
),
|
||||
]
|
||||
"##)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue