use crate::detail::container::{find_from, find_prev_token, mark};
use crate::detail::element::build_element_value;
use crate::detail::token::{
is_close_bracket_token, is_dash_token, is_delimiter_token, is_free_token, is_identified_token,
is_not_delimiter_token, is_open_bracket_token, Token,
};
use crate::element::{Element, ElementKind};
fn find_release_group(tokens: &[Token]) -> (usize, usize) {
let len = tokens.len();
let mut search_start = 0usize;
loop {
let first = find_from(tokens, search_start, |t| {
t.is_enclosed && !is_identified_token(t)
});
let last = if first == len {
len
} else {
find_from(tokens, first, |t| {
is_close_bracket_token(t) || is_identified_token(t)
})
};
if first != len {
let blocked_by_prev = tokens
.get(search_start..first)
.and_then(|s| s.iter().rposition(is_not_delimiter_token))
.is_some_and(|rel| {
!tokens
.get(search_start + rel)
.is_some_and(is_open_bracket_token)
});
if blocked_by_prev {
search_start = last;
continue;
}
if last != len && !tokens.get(last).is_some_and(is_close_bracket_token) {
search_start = last;
continue;
}
return (first, last);
}
let region = tokens.get(search_start..).unwrap_or(&[]);
let fallback = find_prev_token(region, region.len(), |t| {
t.element_kind != Some(ElementKind::FileExtension) && is_not_delimiter_token(t)
});
if let Some(local_idx) = fallback {
let idx = search_start + local_idx;
if idx > 0 && tokens.get(idx).is_some_and(is_free_token) {
let prev_idx = idx - 1;
if tokens
.get(prev_idx)
.is_some_and(|t| is_delimiter_token(t) && is_dash_token(t))
{
return (idx, idx + 1);
}
}
}
return (len, len);
}
}
pub(super) fn parse_release_group(tokens: &mut [Token]) -> Option<Element> {
let (first, last) = find_release_group(tokens);
if first >= last {
return None;
}
let value = build_element_value(tokens.get(first..last)?, true);
if value.is_empty() {
return None;
}
let position = tokens.get(first)?.position;
for i in first..last {
mark(tokens, i, ElementKind::ReleaseGroup);
}
Some(Element {
kind: ElementKind::ReleaseGroup,
value,
position,
})
}