// Lightweight metadata queries for glob patterns.
///|
fn count_separators(tokens : Array[Token]) -> Int {
let mut count = 0
for token in tokens {
match token {
Token::Text(text) => {
let mut i = 0
while i < text.length() {
if text[i].to_int().unsafe_to_char() == '/' {
count = count + 1
}
i = i + 1
}
}
_ => ()
}
}
count
}
///|
/// Returns the static prefix before the first wildcard construct.
pub fn literal_prefix(pattern : String) -> Result[String, GlobError] {
let tokens = tokenize(pattern)
match tokens {
Err(err) => Err(err)
Ok(tokens) =>
// Compile too, so metadata APIs enforce the same complete syntax rules.
match parse(tokens) {
Err(err) => Err(err)
Ok(_) => {
let builder = StringBuilder::new()
let mut stopped = false
for token in tokens {
if stopped {
continue
}
match token {
Token::Text(text) => builder.write_string(text)
_ => stopped = true
}
}
Ok(builder.to_string())
}
}
}
}
///|
/// Returns the number of `/` separators in a valid pattern.
pub fn separator_count(pattern : String) -> Result[Int, GlobError] {
match tokenize(pattern) {
Err(err) => Err(err)
Ok(tokens) =>
match parse(tokens) {
Err(err) => Err(err)
Ok(_) => Ok(count_separators(tokens))
}
}
}
///|
/// Returns whether a pattern contains a recursive `**` wildcard.
pub fn has_recursive_wildcard(pattern : String) -> Result[Bool, GlobError] {
match tokenize(pattern) {
Err(err) => Err(err)
Ok(tokens) =>
match parse(tokens) {
Err(err) => Err(err)
Ok(_) => {
let mut found = false
for token in tokens {
if token is Token::GlobStar {
found = true
}
}
Ok(found)
}
}
}
}
///|
/// Returns whether a valid pattern contains a path separator.
pub fn is_path_pattern(pattern : String) -> Result[Bool, GlobError] {
separator_count(pattern).map(fn(count) { count > 0 })
}
///|
fn append_segment(
segments : Array[String],
start : Int,
end : Int,
path : String,
) -> Unit {
if end > start {
segments.push(path[start:end].to_owned())
}
}
///|
fn split_segments(path : String) -> Array[String] {
let segments : Array[String] = []
let mut start = 0
let mut i = 0
while i <= path.length() {
if i == path.length() || path[i].to_int().unsafe_to_char() == '/' {
append_segment(segments, start, i, path)
start = i + 1
}
i = i + 1
}
segments
}
///|
/// Returns the literal path components known before the first wildcard.
pub fn static_segments(pattern : String) -> Result[Array[String], GlobError] {
let prefix = literal_prefix(pattern)
match prefix {
Err(err) => Err(err)
Ok(prefix) =>
if prefix.is_empty() && !pattern.is_empty() {
Err(GlobError::NonLiteralSegment)
} else {
Ok(split_segments(normalize_path(prefix)))
}
}
}