Compare commits
1 Commits
master
...
cli-redesi
Author | SHA1 | Date |
---|---|---|
Avril | 11ad053ff1 | 4 years ago |
@ -1,24 +0,0 @@
|
||||
|
||||
extern crate rustc_version;
|
||||
use rustc_version::{version, version_meta, Channel};
|
||||
|
||||
fn main() {
|
||||
// Assert we haven't travelled back in time
|
||||
assert!(version().unwrap().major >= 1);
|
||||
|
||||
// Set cfg flags depending on release channel
|
||||
match version_meta().unwrap().channel {
|
||||
Channel::Stable => {
|
||||
println!("cargo:rustc-cfg=stable");
|
||||
}
|
||||
Channel::Beta => {
|
||||
println!("cargo:rustc-cfg=beta");
|
||||
}
|
||||
Channel::Nightly => {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
Channel::Dev => {
|
||||
println!("cargo:rustc-cfg=dev");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use std::{
|
||||
mem,
|
||||
};
|
||||
struct Cons<T>
|
||||
{
|
||||
item: T,
|
||||
next: Option<Box<Cons<T>>>,
|
||||
}
|
||||
|
||||
/// A safe linked list, with remove() for non-nightly build.
|
||||
pub struct LinkedList<T>
|
||||
{
|
||||
head: Option<Cons<T>>,
|
||||
sz: usize,
|
||||
}
|
||||
|
||||
pub struct Iter<'a, T>
|
||||
{
|
||||
current: Option<&'a Cons<T>>,
|
||||
}
|
||||
|
||||
pub struct IterMut<'a, T>
|
||||
{
|
||||
current: Option<&'a mut Cons<T>>,
|
||||
}
|
||||
|
||||
pub struct IntoIter<T>
|
||||
{
|
||||
current: Option<Box<Cons<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Default for LinkedList<T>
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> LinkedList<T>
|
||||
{
|
||||
pub const fn new() -> Self
|
||||
{
|
||||
Self { head: None, sz: 0 }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T)
|
||||
{
|
||||
if let Some(head) = &mut self.head
|
||||
{
|
||||
head.next = Some(Box::new(mem::replace(head, Cons{ item: value, next: None })));
|
||||
self.sz+=1;
|
||||
} else {
|
||||
self.head = Some(Cons{item: value, next: None});
|
||||
self.sz=1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize
|
||||
{
|
||||
self.sz
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<T>
|
||||
{
|
||||
if let Some(_) = self.head
|
||||
{
|
||||
let old = mem::replace(&mut self.head, None).unwrap();
|
||||
self.head = old.next.map(|x| *x);
|
||||
self.sz-=1;
|
||||
Some(old.item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self)
|
||||
{
|
||||
self.head = None;
|
||||
self.sz=0;
|
||||
}
|
||||
|
||||
pub fn iter<'a>(&'a self) -> Iter<'a, T>
|
||||
{
|
||||
Iter{ current: self.head.as_ref() }
|
||||
}
|
||||
|
||||
pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, T>
|
||||
{
|
||||
IterMut{ current: self.head.as_mut() }
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, at: usize) -> T
|
||||
{
|
||||
assert!(at < self.sz, "Cannot remove at element > than size");
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T>
|
||||
{
|
||||
type Item = &'a T;
|
||||
fn next(&mut self) -> Option<Self::Item>
|
||||
{
|
||||
if self.current.is_some() {
|
||||
let mut current = mem::replace(&mut self.current, None);
|
||||
let mut nref = current.unwrap().next.as_ref().map(|x| x.as_ref())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
//! Searcher in slice
|
||||
|
||||
use memchr::{
|
||||
memchr_iter,
|
||||
memrchr_iter,
|
||||
};
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature="mem_find")]
|
||||
pub fn find(haystack: &[u8], needle: &[u8]) -> Option<usize>
|
||||
{
|
||||
if needle.len() < 1 {
|
||||
return None;
|
||||
}
|
||||
for sz in memchr_iter(needle[0], haystack) {
|
||||
if &haystack[sz..std::cmp::min(sz+needle.len(), haystack.len())] == needle {
|
||||
return Some(sz);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature="mem_find")]
|
||||
pub fn find_back(haystack: &[u8], needle: &[u8]) -> Option<usize>
|
||||
{
|
||||
if needle.len() < 1 {
|
||||
return None;
|
||||
}
|
||||
for sz in memrchr_iter(needle[0], haystack) {
|
||||
if &haystack[sz..std::cmp::min(sz+needle.len(), haystack.len())] == needle {
|
||||
return Some(sz+needle.len());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(feature="mem_find"))]
|
||||
pub fn find(haystack: &[u8], needle: &[u8]) -> Option<usize>
|
||||
{
|
||||
wfind(haystack, needle)
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(feature="mem_find"))]
|
||||
pub fn find_back(haystack: &[u8], needle: &[u8]) -> Option<usize>
|
||||
{
|
||||
wfind_back(haystack, needle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wfind(haystack: &[u8], needle: &[u8]) -> Option<usize> {
|
||||
haystack.windows(needle.len()).position(|window| window == needle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wfind_back(haystack: &[u8], needle: &[u8]) -> Option<usize> {
|
||||
haystack.windows(needle.len()).rev().position(|window| window == needle).and_then(|v| Some(haystack.len() - v))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
use super::*;
|
||||
const HAYSTACK: &str = r#"awn r9anw09e8tja\ł]erj\æ][jw3-9r8ja9w8jera9je\đ →je\¶}j g\}iejđ \→æje\¶iaj-wie ¶3-928H09Q82H39 -Ł ¶j@\}]ẻ¶\ }æ]ĸ«ø𳶲38r 9W³¶\ ³¶}→ E→ ÞÆØ→ŁẺLOLE!!!¶ØĸÆŁŁ¶ĸØÞ@ĸ³øþĸ}@→Ħ³¶Ø@ŁŁĸ}→²ĦE}Ħ¶ 30r EJ}AJ"#;
|
||||
const NEEDLE: &str = "LOLE!!!";
|
||||
#[test]
|
||||
fn mfind()
|
||||
{
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
assert_eq!(wfind(haystack, needle), find(haystack, needle));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mfind_back()
|
||||
{
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
assert_eq!(wfind_back(haystack, needle), find_back(haystack, needle));
|
||||
}
|
||||
|
||||
#[cfg(nightly)]
|
||||
mod benchmarks {
|
||||
use super::{
|
||||
NEEDLE, HAYSTACK,
|
||||
};
|
||||
use test::{
|
||||
Bencher, black_box,
|
||||
};
|
||||
#[bench]
|
||||
fn mfind(b: &mut Bencher)
|
||||
{
|
||||
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(super::find(haystack,needle));
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn mfind_back(b: &mut Bencher)
|
||||
{
|
||||
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(super::find_back(haystack,needle));
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn wfind(b: &mut Bencher)
|
||||
{
|
||||
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(super::wfind(haystack,needle));
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn wfind_back(b: &mut Bencher)
|
||||
{
|
||||
|
||||
let haystack = HAYSTACK.as_bytes();
|
||||
let needle= NEEDLE.as_bytes();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(super::wfind_back(haystack,needle));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
use std::{
|
||||
cmp::PartialEq,
|
||||
fmt,
|
||||
mem,
|
||||
collections::HashSet,
|
||||
str,
|
||||
};
|
||||
|
||||
/// Tag matching rules
|
||||
#[derive(Clone,Debug,PartialEq,Eq,Hash)]
|
||||
pub enum Rule
|
||||
{
|
||||
/// This tag /must/ be present.
|
||||
Required,
|
||||
/// At least one tag marked `optional` must be present.
|
||||
Optional,
|
||||
/// This tag /must not/ be present.
|
||||
Rejected,
|
||||
}
|
||||
|
||||
/// Represents a tag
|
||||
#[derive(Clone,Debug,PartialEq,Eq,Hash)]
|
||||
pub struct Tag
|
||||
{
|
||||
rule: Rule,
|
||||
strings: Vec<String>,
|
||||
normal_idx: usize,
|
||||
repr: RepresentationMode,
|
||||
}
|
||||
|
||||
/// How should tags be interpreted?
|
||||
#[derive(Debug,Clone,PartialEq,Eq,Hash)]
|
||||
pub enum RepresentationMode
|
||||
{
|
||||
/// Ignores spaces, case, underscores, things in parenthesis, and special symbols.
|
||||
Lenient,
|
||||
/// Ignores spaces, case, and underscores.
|
||||
Normal,
|
||||
/// Ignores nothing
|
||||
Strict,
|
||||
}
|
||||
|
||||
impl Default for RepresentationMode
|
||||
{
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::Strict
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove consecutive whitespace, and normalise them to `' '`
|
||||
fn remove_whitespace<T: IntoIterator<Item=char>>(input: T) -> String
|
||||
{
|
||||
let mut last = false;
|
||||
input.into_iter()
|
||||
.filter_map(|ch| {
|
||||
if ch.is_whitespace() {
|
||||
if last {
|
||||
None
|
||||
} else {
|
||||
last = true;
|
||||
Some(' ')
|
||||
}
|
||||
}
|
||||
else {
|
||||
last = false;
|
||||
Some(ch)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// How big are the max output of `fuzz` likely to be?
|
||||
const FUZZ_SIZE_HINT: usize = 7;
|
||||
/// Creates representations
|
||||
fn fuzz<S: Into<String>>(input: S, output: &mut Vec<String>, mode: &RepresentationMode) -> usize
|
||||
{
|
||||
let input = input.into();
|
||||
|
||||
match mode {
|
||||
RepresentationMode::Strict => {
|
||||
output.push(input);
|
||||
0
|
||||
},
|
||||
RepresentationMode::Normal => {
|
||||
output.push(input.to_lowercase());
|
||||
{
|
||||
let normal = remove_whitespace(input.chars());
|
||||
|
||||
output.push(normal.replace(" ", "_"));
|
||||
output.push(normal.chars().filter(|ch| !ch.is_whitespace()).collect());
|
||||
output.push(normal);
|
||||
}
|
||||
output.push(input);
|
||||
output.len()-1
|
||||
},
|
||||
RepresentationMode::Lenient => {
|
||||
const REMOVE: &[char] = &[
|
||||
':',
|
||||
'.',
|
||||
',',
|
||||
' ',
|
||||
];
|
||||
output.push(input.chars().filter(|ch| !REMOVE.contains(ch)).collect());
|
||||
fuzz(input, output, mode)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Tag
|
||||
{
|
||||
|
||||
/// Create a new tag representation
|
||||
pub fn new<S: Into<String>>(tag: S, rule: Rule, mode: RepresentationMode) -> Self
|
||||
{
|
||||
let mut fz = Vec::with_capacity(FUZZ_SIZE_HINT);
|
||||
let normal_idx = fuzz(tag, &mut fz, &mode);
|
||||
Self {
|
||||
rule,
|
||||
strings: fz,
|
||||
normal_idx,
|
||||
repr: mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returned when an empty tag is tried to be parsed
|
||||
#[derive(Debug)]
|
||||
pub struct ParseError;
|
||||
|
||||
impl std::error::Error for ParseError{}
|
||||
|
||||
impl fmt::Display for ParseError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "cannot have empty tag rule")
|
||||
}
|
||||
}
|
||||
|
||||
impl str::FromStr for Tag
|
||||
{
|
||||
type Err = ParseError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err>
|
||||
{
|
||||
let mut chars = s.chars();
|
||||
let mut next = String::new();
|
||||
let rule = match chars.next() {
|
||||
Some('+') => Rule::Optional,
|
||||
Some('-') => Rule::Rejected,
|
||||
Some(x) => {
|
||||
next.push(x);
|
||||
Rule::Required
|
||||
},
|
||||
_ => return Err(ParseError),
|
||||
};
|
||||
let s = chars.as_str();
|
||||
if s.len() == 0 {
|
||||
return Err(ParseError);
|
||||
}
|
||||
|
||||
Ok(Self::new(if next.len() > 0 {
|
||||
next.push_str(s);
|
||||
next
|
||||
} else {s.into()}, rule, RepresentationMode::default()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for Tag
|
||||
where T: AsRef<str>
|
||||
{
|
||||
fn eq(&self, other: &T) -> bool
|
||||
{
|
||||
let mut fz = Vec::with_capacity(FUZZ_SIZE_HINT);
|
||||
fuzz(other.as_ref(), &mut fz, &self.repr);
|
||||
for (i,j) in self.strings.iter().zip(fz.into_iter())
|
||||
{
|
||||
if i == j.as_str() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tag> for String
|
||||
{
|
||||
fn from(mut tag: Tag) -> Self
|
||||
{
|
||||
mem::replace(&mut tag.strings[tag.normal_idx], Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Tag
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
write!(f, "{}", &self.strings[self.normal_idx])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<'a>
|
||||
{
|
||||
Required(&'a Tag),
|
||||
NoOptional,
|
||||
Rejected(&'a Tag),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OwnedError
|
||||
{
|
||||
Required(Tag),
|
||||
NoOptional,
|
||||
Rejected(Tag),
|
||||
}
|
||||
|
||||
pub type Result<'a> = std::result::Result<Vec<&'a Tag>, Error<'a>>;
|
||||
|
||||
/// Search a set of strings for a matching ruleset for `tags`. Returns the matched tags on success
|
||||
pub fn search<'a, T, U, V>(matches: T, tags: U) -> Result<'a>
|
||||
where T: IntoIterator<Item=V>,
|
||||
U: IntoIterator<Item=&'a Tag>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let tags: Vec<&'a Tag> = tags.into_iter().collect();
|
||||
let mut output = Vec::with_capacity(tags.len());
|
||||
|
||||
let mut matched = HashSet::new();
|
||||
for matches in matches.into_iter()
|
||||
{
|
||||
for tag in tags.iter() {
|
||||
if *tag == &matches {
|
||||
matched.insert(tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut needs_opt = false;
|
||||
let mut has_opt = false;
|
||||
for tag in tags.iter()
|
||||
{
|
||||
if matched.contains(tag) {
|
||||
match tag.rule {
|
||||
Rule::Optional => {has_opt = true; needs_opt = true;},
|
||||
Rule::Rejected => return Err(Error::Rejected(tag)),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
output.push(tag.clone());
|
||||
} else if tag.rule == Rule::Optional {
|
||||
needs_opt = true;
|
||||
} else if tag.rule == Rule::Required {
|
||||
return Err(Error::Required(tag));
|
||||
}
|
||||
}
|
||||
|
||||
if needs_opt && !has_opt {
|
||||
return Err(Error::NoOptional);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Parse a string of many tag rules, ignoring empty or invalid entries
|
||||
#[inline]
|
||||
pub fn parse<T>(string: T) -> Vec<Tag>
|
||||
where T: AsRef<str>
|
||||
{
|
||||
let string = string.as_ref();
|
||||
parse_iter(string.split(" "))
|
||||
}
|
||||
|
||||
/// Parse a collection of tag rules, ignoring empty or invalid entries
|
||||
pub fn parse_iter<T,I>(from: I) -> Vec<Tag>
|
||||
where I: IntoIterator<Item=T>,
|
||||
T: AsRef<str>
|
||||
{
|
||||
from.into_iter()
|
||||
.filter_map(|s| {
|
||||
if let Ok(tag) = s.as_ref().parse() {
|
||||
Some(tag)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
impl fmt::Display for Error<'_>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self {
|
||||
Self::NoOptional => write!(f, "there were no optional tags, at least one is needed"),
|
||||
Self::Rejected(tag) => write!(f, "a rejected tag was present: {}", tag),
|
||||
Self::Required(tag) => write!(f, "a required tag was not present: {}", tag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for OwnedError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self {
|
||||
Self::NoOptional => write!(f, "there were no optional tags, at least one is needed"),
|
||||
Self::Rejected(tag) => write!(f, "a rejected tag was present: {}", tag),
|
||||
Self::Required(tag) => write!(f, "a required tag was not present: {}", tag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error<'_>{}
|
||||
impl std::error::Error for OwnedError{}
|
||||
|
||||
impl From<Error<'_>> for OwnedError
|
||||
{
|
||||
fn from(er: Error<'_>) -> Self
|
||||
{
|
||||
match er {
|
||||
Error::NoOptional => Self::NoOptional,
|
||||
Error::Rejected(t) => Self::Rejected(t.to_owned()),
|
||||
Error::Required(t) => Self::Required(t.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue