use std::path::Path;
use either::Either;
use globset::Glob;
use toml_edit::{Array, Entry, Item, Table, TableLike, Value};
pub struct Workspace<'a>(pub(super) &'a dyn TableLike);
impl<'a> Workspace<'a> {
pub fn members(&self) -> WorkspaceMembers<'a> {
match self.0.get("members") {
Some(item) => WorkspaceMembers(item.as_array()),
None => WorkspaceMembers(None),
}
}
pub fn exclude(&self) -> WorkspaceExclude<'a> {
match self.0.get("exclude") {
Some(item) => WorkspaceExclude(item.as_array()),
None => WorkspaceExclude(None),
}
}
}
pub struct WorkspaceMut<'a> {
table: Either<&'a mut dyn TableLike, Option<Entry<'a>>>,
}
impl<'a> WorkspaceMut<'a> {
pub(super) fn new(entry: Entry<'a>) -> Self {
Self {
table: Either::Right(Some(entry)),
}
}
}
impl WorkspaceMut<'_> {
pub fn add_member(&mut self, path: impl AsRef<Path>) {
let table = self.table(true).expect("table");
let members = table
.entry("members")
.or_insert_with(|| Item::Value(Value::Array(Array::new())));
if let Some(members) = members.as_array_mut() {
for member in members.iter() {
if let Some(member) = member.as_str() {
if path.as_ref() == Path::new(member) {
return;
}
if let Ok(glob) = Glob::new(member.trim_start_matches("./"))
&& glob.compile_matcher().is_match(path.as_ref())
{
return;
}
}
}
members.push(path.as_ref().to_string_lossy().to_string());
}
}
}
impl WorkspaceMut<'_> {
fn init_table(&mut self, overwrite: bool) -> Option<()> {
if let Either::Right(option) = &mut self.table {
match option.take().expect("some") {
Entry::Occupied(entry) if entry.get().as_table_like().is_some() => {
self.table = Either::Left(entry.into_mut().as_table_like_mut().expect("table"));
}
Entry::Occupied(mut entry) if overwrite => {
*entry.get_mut() = Item::Table(Table::new());
self.table = Either::Left(entry.into_mut().as_table_like_mut().expect("table"));
}
Entry::Vacant(entry) if overwrite => {
self.table = Either::Left(
entry
.insert(Item::Table(Table::new()))
.as_table_like_mut()
.expect("table"),
);
}
entry => {
option.replace(entry);
return None;
}
}
}
Some(())
}
fn table(&mut self, overwrite: bool) -> Option<&mut dyn TableLike> {
self.init_table(overwrite)?;
match self.table.as_mut().left() {
Some(table) => Some(*table),
None => None,
}
}
}
pub struct WorkspaceMembers<'a>(Option<&'a Array>);
impl<'a> IntoIterator for WorkspaceMembers<'a> {
type Item = &'a str;
type IntoIter = Box<dyn Iterator<Item = &'a str> + 'a>;
fn into_iter(self) -> Self::IntoIter {
match self.0 {
Some(arr) => Box::new(arr.into_iter().flat_map(Value::as_str)),
None => Box::new(std::iter::empty()),
}
}
}
pub struct WorkspaceExclude<'a>(Option<&'a Array>);
impl<'a> IntoIterator for WorkspaceExclude<'a> {
type Item = &'a str;
type IntoIter = Box<dyn Iterator<Item = &'a str> + 'a>;
fn into_iter(self) -> Self::IntoIter {
match self.0 {
Some(arr) => Box::new(arr.into_iter().flat_map(Value::as_str)),
None => Box::new(std::iter::empty()),
}
}
}