Commit dc24e41a authored by Michael Aaron Murphy's avatar Michael Aaron Murphy
Browse files

Create ion_{braces,ranges} crates

parent 4e5ec035
......@@ -8,4 +8,7 @@ nightly:
script:
- cargo build
- cargo test --lib
- cargo test --manifest-path ion_braces/Cargo.toml
- cargo test --manifest-path ion_builtins/Cargo.toml
- cargo test --manifest-path ion_ranges/Cargo.toml
- bash examples/run_examples.sh
......@@ -165,13 +165,14 @@ dependencies = [
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"ion_braces 0.1.0",
"ion_builtins 0.1.0",
"ion_ranges 0.1.0",
"ion_sys 0.1.0",
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"liner 0.4.5 (git+https://gitlab.redox-os.org/redox-os/liner)",
"permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallstring 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -181,6 +182,14 @@ dependencies = [
"xdg 2.1.0 (git+https://github.com/whitequark/rust-xdg)",
]
[[package]]
name = "ion_braces"
version = "0.1.0"
dependencies = [
"permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ion_builtins"
version = "0.1.0"
......@@ -192,6 +201,13 @@ dependencies = [
"smallvec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ion_ranges"
version = "0.1.0"
dependencies = [
"smallstring 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ion_sys"
version = "0.1.0"
......
......@@ -17,7 +17,7 @@ repository = "https://gitlab.redox-os.org/redox-os/ion"
version = "1.0.0-alpha"
[workspace]
members = [ "ion_builtins", "ion_sys" ]
members = [ "ion_braces", "ion_builtins", "ion_sys", "ion_ranges" ]
[[bin]]
name = "ion"
......@@ -37,15 +37,16 @@ glob = "0.2"
itoa = "0.4"
lazy_static = "1.0"
liner = { git = "https://gitlab.redox-os.org/redox-os/liner" }
permutate = "0.3"
rand = "0.5"
regex = "1.0"
smallstring = "0.1"
smallvec = "0.6"
unicode-segmentation = "1.2"
xdg = { git = "https://github.com/whitequark/rust-xdg" }
ion_braces = { path = "ion_braces" }
ion_builtins = { path = "ion_builtins" }
ion_sys = { path = "ion_sys" }
ion_ranges = { path = "ion_ranges" }
[lib]
path = "src/lib/lib.rs"
......
[package]
name = "ion_braces"
version = "0.1.0"
authors = ["Michael Murphy <mmstickman@gmail.com>"]
[dependencies]
permutate = "0.3"
smallvec = "0.6"
extern crate permutate;
extern crate smallvec;
use permutate::Permutator;
use smallvec::SmallVec;
#[derive(Debug)]
/// A token primitive for the `expand_braces` function.
pub enum BraceToken {
Normal(String),
Expander,
}
pub fn expand<'a>(
tokens: &'a [BraceToken],
expanders: &'a [&'a [&'a str]],
) -> Box<Iterator<Item = String> + 'a> {
if expanders.len() > 1 {
let multiple_brace_expand = MultipleBraceExpand::new(tokens, expanders);
Box::new(multiple_brace_expand)
} else if expanders.len() == 1 {
let single_brace_expand = SingleBraceExpand {
elements: expanders[0].iter().map(|element| *element),
tokens,
loop_count: 0,
};
Box::new(single_brace_expand)
} else {
Box::new(::std::iter::empty())
}
}
fn escape_string(output: &mut SmallVec<[u8; 64]>, input: &str) {
let mut backslash = false;
for character in input.bytes() {
if backslash {
match character {
b'{' | b'}' | b',' => output.push(character),
_ => {
output.push(b'\\');
output.push(character);
}
}
backslash = false;
} else if character == b'\\' {
backslash = true;
} else {
output.push(character);
}
}
}
pub struct MultipleBraceExpand<'a> {
permutator: Permutator<'a, str>,
tokens: &'a [BraceToken],
buffer: Vec<&'a str>
}
impl<'a> MultipleBraceExpand<'a> {
pub fn new(
tokens: &'a [BraceToken],
expanders: &'a [&'a [&'a str]]
) -> MultipleBraceExpand<'a> {
MultipleBraceExpand {
permutator: Permutator::new(expanders),
tokens,
buffer: vec![""; expanders.len()]
}
}
}
impl<'a> Iterator for MultipleBraceExpand<'a> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
if self.permutator.next_with_buffer(&mut self.buffer) {
let mut strings = self.buffer.iter();
let small_vec: SmallVec<[u8; 64]> = self.tokens.iter().fold(
SmallVec::with_capacity(64),
|mut small_vec, token| match *token {
BraceToken::Normal(ref text) => {
escape_string(&mut small_vec, text);
small_vec
}
BraceToken::Expander => {
escape_string(&mut small_vec, strings.next().unwrap());
small_vec
}
},
);
Some(unsafe { String::from_utf8_unchecked(small_vec.to_vec()) })
} else {
None
}
}
}
pub struct SingleBraceExpand<'a, 'b, I>
where
I: Iterator<Item = &'a str>,
{
elements: I,
tokens: &'b [BraceToken],
loop_count: usize,
}
impl<'a, 'b, I> Iterator for SingleBraceExpand<'a, 'b, I>
where
I: Iterator<Item = &'a str>,
{
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
match self.loop_count {
0 => {
let small_vec: SmallVec<[u8; 64]> = self.tokens.iter().fold(
SmallVec::with_capacity(64),
|mut small_vec, token| match *token {
BraceToken::Normal(ref text) => {
escape_string(&mut small_vec, text);
small_vec
}
BraceToken::Expander => {
escape_string(&mut small_vec, self.elements.next().unwrap());
small_vec
}
},
);
self.loop_count = 1;
Some(unsafe { String::from_utf8_unchecked(small_vec.to_vec()) })
}
_ => {
if let Some(element) = self.elements.next() {
let small_vec: SmallVec<[u8; 64]> = self.tokens.iter().fold(
SmallVec::with_capacity(64),
|mut small_vec, token| match *token {
BraceToken::Normal(ref text) => {
escape_string(&mut small_vec, text);
small_vec
}
BraceToken::Expander => {
escape_string(&mut small_vec, element);
small_vec
}
},
);
Some(unsafe { String::from_utf8_unchecked(small_vec.to_vec()) })
} else {
None
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_multiple_brace_expand() {
let expanders: &[&[&str]] = &[&["1", "2"][..], &["3", "4"][..], &["5", "6"][..]];
let tokens: &[BraceToken] = &[
BraceToken::Normal("AB".to_owned()),
BraceToken::Expander,
BraceToken::Normal("CD".to_owned()),
BraceToken::Expander,
BraceToken::Normal("EF".to_owned()),
BraceToken::Expander,
BraceToken::Normal("GH".to_owned()),
];
assert_eq!(
MultipleBraceExpand ::new(tokens, expanders).collect::<Vec<String>>(),
vec![
"AB1CD3EF5GH".to_owned(),
"AB1CD3EF6GH".to_owned(),
"AB1CD4EF5GH".to_owned(),
"AB1CD4EF6GH".to_owned(),
"AB2CD3EF5GH".to_owned(),
"AB2CD3EF6GH".to_owned(),
"AB2CD4EF5GH".to_owned(),
"AB2CD4EF6GH".to_owned(),
]
);
}
#[test]
fn test_single_brace_expand() {
let elements = &["one", "two", "three"];
let tokens: &[BraceToken] = &[BraceToken::Normal("A=".to_owned()), BraceToken::Expander];
assert_eq!(
SingleBraceExpand {
elements: elements.iter().map(|element| *element),
tokens,
loop_count: 0,
}.collect::<Vec<String>>(),
vec!["A=one".to_owned(), "A=two".to_owned(), "A=three".to_owned()]
);
}
}
......@@ -282,7 +282,7 @@ fn file_has_read_permission(filepath: &str) -> bool {
/// To extract the permissions from the mode, the bitwise AND operator will be used and compared
/// with the respective write bits.
fn file_has_write_permission(filepath: &str) -> bool {
const USER: u32 = 0b10000000;
const USER: u32 = 0b1000_0000;
const GROUP: u32 = 0b10000;
const GUEST: u32 = 0b10;
......@@ -299,7 +299,7 @@ fn file_has_write_permission(filepath: &str) -> bool {
/// Note: This function is 1:1 the same as src/builtins/exists.rs:file_has_execute_permission
/// If you change the following function, please also update the one in src/builtins/exists.rs
fn file_has_execute_permission(filepath: &str) -> bool {
const USER: u32 = 0b1000000;
const USER: u32 = 0b100_0000;
const GROUP: u32 = 0b1000;
const GUEST: u32 = 0b1;
......@@ -423,39 +423,39 @@ fn test_integers_arguments() {
#[test]
fn test_file_exists() {
assert_eq!(file_exists("testing/empty_file"), true);
assert_eq!(file_exists("../testing/empty_file"), true);
assert_eq!(file_exists("this-does-not-exist"), false);
}
#[test]
fn test_file_is_regular() {
assert_eq!(file_is_regular("testing/empty_file"), true);
assert_eq!(file_is_regular("testing"), false);
assert_eq!(file_is_regular("../testing/empty_file"), true);
assert_eq!(file_is_regular("../testing"), false);
}
#[test]
fn test_file_is_directory() {
assert_eq!(file_is_directory("testing"), true);
assert_eq!(file_is_directory("testing/empty_file"), false);
assert_eq!(file_is_directory("../testing"), true);
assert_eq!(file_is_directory("../testing/empty_file"), false);
}
#[test]
fn test_file_is_symlink() {
assert_eq!(file_is_symlink("testing/symlink"), true);
assert_eq!(file_is_symlink("testing/empty_file"), false);
assert_eq!(file_is_symlink("../testing/symlink"), true);
assert_eq!(file_is_symlink("../testing/empty_file"), false);
}
#[test]
fn test_file_has_execute_permission() {
assert_eq!(file_has_execute_permission("testing/executable_file"), true);
assert_eq!(file_has_execute_permission("testing/empty_file"), false);
assert_eq!(file_has_execute_permission("../testing/executable_file"), true);
assert_eq!(file_has_execute_permission("../testing/empty_file"), false);
}
#[test]
fn test_file_size_is_greater_than_zero() {
assert_eq!(
file_size_is_greater_than_zero("testing/file_with_text"),
file_size_is_greater_than_zero("../testing/file_with_text"),
true
);
assert_eq!(file_size_is_greater_than_zero("testing/empty_file"), false);
assert_eq!(file_size_is_greater_than_zero("../testing/empty_file"), false);
}
[package]
name = "ion_ranges"
version = "0.1.0"
authors = ["Michael Murphy <mmstickman@gmail.com>"]
[dependencies]
smallstring = "0.1"
/// Index into a vector-like object
#[derive(Debug, PartialEq, Copy, Clone)]
pub(crate) enum Index {
pub enum Index {
/// Index starting from the beginning of the vector, where `Forward(0)`
/// is the first element
Forward(usize),
......@@ -10,7 +10,7 @@ pub(crate) enum Index {
}
impl Index {
pub(crate) fn resolve(&self, vector_length: usize) -> Option<usize> {
pub fn resolve(&self, vector_length: usize) -> Option<usize> {
match *self {
Index::Forward(n) => Some(n),
Index::Backward(n) => if n >= vector_length {
......@@ -27,7 +27,7 @@ impl Index {
/// ```ignore,rust
/// assert_eq!(Index::new(-1), Index::Backward(0))
/// ```
pub(crate) fn new(input: isize) -> Index {
pub fn new(input: isize) -> Index {
if input < 0 {
Index::Backward((input.abs() as usize) - 1)
} else {
......
extern crate smallstring;
mod index;
mod parse;
mod range;
mod select;
pub use self::index::*;
pub use self::parse::*;
pub use self::range::*;
pub use self::select::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ranges() {
let range1 = Range::exclusive(Index::new(1), Index::new(5));
assert_eq!(Some((1, 4)), range1.bounds(42));
assert_eq!(Some((1, 4)), range1.bounds(7));
let range2 = Range::inclusive(Index::new(2), Index::new(-4));
assert_eq!(Some((2, 5)), range2.bounds(10));
assert_eq!(None, range2.bounds(3));
}
#[test]
fn index_ranges() {
let valid_cases = vec![
(
Range::exclusive(Index::Forward(0), Index::Forward(3)),
"0..3",
),
(
Range::inclusive(Index::Forward(0), Index::Forward(2)),
"0...2",
),
(
Range::inclusive(Index::Forward(2), Index::Backward(1)),
"2...-2",
),
(
Range::inclusive(Index::Forward(0), Index::Backward(0)),
"0...-1",
),
(
Range::exclusive(Index::Backward(2), Index::Backward(0)),
"-3..-1",
),
(Range::from(Index::Backward(2)), "-3.."),
(Range::to(Index::Forward(5)), "..5"),
];
for (range, string) in valid_cases {
assert_eq!(Some(range), parse_index_range(string));
}
let invalid_cases = vec!["0..A", "3-3..42"];
for range in invalid_cases {
assert_eq!(None, parse_index_range(range))
}
}
#[test]
fn range_expand() {
if let Some(_) = parse_range("abc") {
panic!("parse_range() failed");
}
let actual: Vec<String> = parse_range("-3...3").unwrap().collect();
let expected: Vec<String> = vec![
"-3".to_owned(),
"-2".to_owned(),
"-1".to_owned(),
"0".to_owned(),
"1".to_owned(),
"2".to_owned(),
"3".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("07...12").unwrap().collect();
let expected: Vec<String> = vec![
"07".to_owned(),
"08".to_owned(),
"09".to_owned(),
"10".to_owned(),
"11".to_owned(),
"12".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("-3...10").unwrap().collect();
let expected: Vec<String> = vec![
"-3".to_owned(),
"-2".to_owned(),
"-1".to_owned(),
"0".to_owned(),
"1".to_owned(),
"2".to_owned(),
"3".to_owned(),
"4".to_owned(),
"5".to_owned(),
"6".to_owned(),
"7".to_owned(),
"8".to_owned(),
"9".to_owned(),
"10".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("3...-3").unwrap().collect();
let expected: Vec<String> = vec![
"3".to_owned(),
"2".to_owned(),
"1".to_owned(),
"0".to_owned(),
"-1".to_owned(),
"-2".to_owned(),
"-3".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("03...-3").unwrap().collect();
let expected: Vec<String> = vec![
"03".to_owned(),
"02".to_owned(),
"01".to_owned(),
"00".to_owned(),
"-1".to_owned(),
"-2".to_owned(),
"-3".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("3...-03").unwrap().collect();
let expected: Vec<String> = vec![
"003".to_owned(),
"002".to_owned(),
"001".to_owned(),
"000".to_owned(),
"-01".to_owned(),
"-02".to_owned(),
"-03".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("a...c").unwrap().collect();
let expected: Vec<String> = vec!["a".to_owned(), "b".to_owned(), "c".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("c...a").unwrap().collect();
let expected: Vec<String> = vec!["c".to_owned(), "b".to_owned(), "a".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("A...C").unwrap().collect();
let expected: Vec<String> = vec!["A".to_owned(), "B".to_owned(), "C".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("C...A").unwrap().collect();
let expected: Vec<String> = vec!["C".to_owned(), "B".to_owned(), "A".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("C..A").unwrap().collect();
let expected: Vec<String> = vec!["C".to_owned(), "B".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("c..a").unwrap().collect();
let expected: Vec<String> = vec!["c".to_owned(), "b".to_owned()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("-3..4").unwrap().collect();
let expected: Vec<String> = vec![
"-3".to_owned(),
"-2".to_owned(),
"-1".to_owned(),
"0".to_owned(),
"1".to_owned(),
"2".to_owned(),
"3".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("3..-4").unwrap().collect();
let expected: Vec<String> = vec![
"3".to_owned(),
"2".to_owned(),
"1".to_owned(),
"0".to_owned(),
"-1".to_owned(),
"-2".to_owned(),
"-3".to_owned(),
];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("-3...0").unwrap().collect();
let expected: Vec<String> = vec!["-3".into(), "-2".into(), "-1".into(), "0".into()];
assert_eq!(actual, expected);
let actual: Vec<String> = parse_range("-3..0").unwrap().collect();
let expected: Vec<String> = vec!["-3".into(), "-2".into(), "-1".into()];
assert_eq!(actual, expected);
}
}
use super::words::{Index, Range};
use super::{Index, Range};