use super::{context, single_multispace};
use crate::lang::{
elements::{Located, Region},
parsers::{Captured, IResult, Span},
};
use nom::{
character::complete::anychar,
combinator::{map_res, not, recognize},
multi::many1,
sequence::pair,
};
use std::{borrow::Cow, convert::TryFrom};
use uriparse::URIReference;
pub fn deeper<'a, T>(
mut parser: impl FnMut(Span<'a>) -> IResult<T>,
) -> impl FnMut(Span<'a>) -> IResult<T> {
context("Deeper", move |input: Span<'a>| {
let (input, x) = parser(input.with_deeper_depth())?;
Ok((input.with_shallower_depth(), x))
})
}
pub fn locate<'a, T>(
mut parser: impl FnMut(Span<'a>) -> IResult<Captured<T>>,
) -> impl FnMut(Span<'a>) -> IResult<Located<T>> {
context("Locate", move |input: Span<'a>| {
let (input, c) = parser(input)?;
let region = Region::from(c.input());
Ok((input, Located::new(c.into_inner(), region)))
})
}
pub fn capture<'a, T>(
mut parser: impl FnMut(Span<'a>) -> IResult<T>,
) -> impl FnMut(Span<'a>) -> IResult<Captured<T>> {
context("Capture", move |input: Span<'a>| {
let start = input;
let (input, x) = parser(input)?;
let start =
start.with_length(input.start_offset() - start.start_offset());
Ok((input, Captured::new(x, start)))
})
}
pub fn cow_str<'a>(input: Span<'a>) -> IResult<Cow<'a, str>> {
Ok((input, input.into()))
}
pub fn uri_ref<'a>(input: Span<'a>) -> IResult<URIReference<'a>> {
context(
"Normal URI Reference",
map_res(
recognize(many1(pair(not(single_multispace), anychar))),
URIReference::try_from,
),
)(input)
}
#[cfg(test)]
mod tests {
use super::*;
use nom::bytes::complete::tag;
#[test]
fn locate_should_return_parser_result_with_consumed_input_location() {
let input = Span::from("123abc");
let (input, located) =
locate(capture(map_res(tag("123"), |s: Span| {
s.as_unsafe_remaining_str().parse::<u32>()
})))(input)
.unwrap();
assert_eq!(input, "abc");
assert_eq!(located.region().offset(), 0);
assert_eq!(located.region().len(), 3);
assert_eq!(located.into_inner(), 123);
}
#[test]
fn capture_should_return_parser_result_with_consumed_input() {
let input = Span::from("123abc");
let (input, captured) = capture(map_res(tag("123"), |s: Span| {
s.as_unsafe_remaining_str().parse::<u32>()
}))(input)
.unwrap();
assert_eq!(input, "abc");
assert_eq!(captured.input(), "123");
assert_eq!(captured.into_inner(), 123);
}
#[test]
fn cow_str_should_return_input_as_cow_str() {
let input = Span::from("abc");
let (input, result) = cow_str(input).unwrap();
assert_eq!(input, "abc");
assert_eq!(result, Cow::from("abc"));
}
#[test]
fn uri_ref_should_fail_if_input_empty() {
let input = Span::from("");
assert!(uri_ref(input).is_err());
}
#[test]
fn uri_ref_should_succeed_even_if_no_scheme_or_subdomain_as_long_as_url() {
let input = Span::from("example.com");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme(), None);
assert_eq!(u.host(), None);
assert_eq!(u.path(), "example.com");
}
#[test]
fn uri_ref_should_succeed_even_if_no_scheme_as_long_as_url() {
let input = Span::from("www.example.com");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme(), None);
assert_eq!(u.host(), None);
assert_eq!(u.path(), "www.example.com");
}
#[test]
fn uri_ref_should_succeed_if_starts_with_network_path() {
let input = Span::from("//some/network/path");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme(), None);
assert_eq!(u.host().map(ToString::to_string), Some("some".to_string()));
assert_eq!(u.path(), "/network/path");
}
#[test]
fn uri_ref_should_succeed_if_starts_with_scheme() {
let input = Span::from("https://github.com/vimwiki/vimwiki.git");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme().unwrap(), "https");
assert_eq!(u.host().unwrap().to_string(), "github.com");
assert_eq!(u.path(), "/vimwiki/vimwiki.git");
let input = Span::from("mailto:habamax@gmail.com");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme().unwrap(), "mailto");
assert_eq!(u.path(), "habamax@gmail.com");
let input = Span::from("ftp://vim.org");
let (input, u) = uri_ref(input).expect("Failed to parse uri ref");
assert!(input.is_empty());
assert_eq!(u.scheme().unwrap(), "ftp");
assert_eq!(u.host().unwrap().to_string(), "vim.org");
}
}