use std::collections::BTreeMap;
use crate::model::join::Conflict;
use crate::model::types::PaneKey;
use crate::view::lines::{Content, GroupKind, Item, Line, Place};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum Handle {
Bead(Place),
Elided(Place),
Group(GroupKind, Option<String>),
Item(ItemKey),
Project(String),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum ItemKey {
Pane(PaneKey),
Project(String),
Conflict(Conflict),
}
pub(super) fn item_key(item: &Item) -> Option<ItemKey> {
Some(match item {
Item::Loose(pane) => ItemKey::Pane(pane.pane.clone()),
Item::Unconfigured(pane) => ItemKey::Pane(pane.pane.clone()),
Item::Failed(failed) => ItemKey::Project(failed.project.clone()),
Item::Conflict(conflict) => ItemKey::Conflict(conflict.clone()),
})
}
#[derive(Default)]
pub(super) struct Folds(BTreeMap<Handle, Fold>);
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(super) struct Fold {
pub(super) line: Option<Way>,
pub(super) scope: Option<Scope>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Way {
Open,
Shut,
Rests,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum Scope {
Points { open: bool },
Rests,
}
impl Fold {
fn holds_shut(&self) -> bool {
self.line == Some(Way::Shut) || matches!(self.scope, Some(Scope::Points { open: false }))
}
}
impl Folds {
pub(super) fn pointed(&self, handle: &Handle, over: Option<&Scope>) -> Option<bool> {
match self.0.get(handle).and_then(|fold| fold.line) {
Some(Way::Open) => return Some(true),
Some(Way::Shut) => return Some(false),
Some(Way::Rests) => return None,
None => {}
}
Folds::forced(self.beneath(handle, over))
}
pub(super) fn beneath<'a>(
&'a self,
handle: &Handle,
over: Option<&'a Scope>,
) -> Option<&'a Scope> {
self.0
.get(handle)
.and_then(|fold| fold.scope.as_ref())
.or(over)
}
pub(super) fn set(&mut self, handle: Handle, open: bool) {
let way = if open { Way::Open } else { Way::Shut };
self.0.entry(handle).or_default().line = Some(way);
}
pub(super) fn set_over(&mut self, handle: Handle, open: bool, beneath: &[Handle]) {
for under in beneath {
self.0.remove(under);
}
let scope = Some(Scope::Points { open });
self.0.insert(handle, Fold { line: None, scope });
}
pub(super) fn let_go(&mut self, handle: Handle, beneath: &[Handle]) {
for under in beneath {
self.0.remove(under);
}
let scope = Some(Scope::Rests);
self.0.insert(handle, Fold { line: None, scope });
}
pub(super) fn put_back(&mut self, handle: Handle) {
self.0.entry(handle).or_default().line = Some(Way::Rests);
}
pub(super) fn shut(&self) -> impl Iterator<Item = &Handle> {
self.0
.iter()
.filter(|(_, fold)| fold.holds_shut())
.map(|(handle, _)| handle)
}
pub(super) fn spend(&mut self, handle: &Handle, path: impl IntoIterator<Item = Handle>) {
let scope_shut = matches!(
self.0.get(handle),
Some(Fold {
scope: Some(Scope::Points { open: false }),
..
})
);
self.rest(handle.clone());
if scope_shut {
for under in path {
self.rest(under);
}
}
}
fn rest(&mut self, handle: Handle) {
let fold = self.0.entry(handle).or_default();
if fold.line != Some(Way::Open) {
fold.line = Some(Way::Rests);
}
}
pub(super) fn clear(&mut self) {
self.0.clear();
}
pub(super) fn mentioned(&self) -> impl Iterator<Item = &Handle> {
self.0.keys()
}
pub(super) fn forced(over: Option<&Scope>) -> Option<bool> {
match over {
Some(Scope::Points { open, .. }) => Some(*open),
Some(Scope::Rests) | None => None,
}
}
#[cfg(test)]
pub(super) fn entries(&self) -> impl Iterator<Item = (&Handle, &Fold)> {
self.0.iter()
}
}
pub(super) fn handle_of(line: &Line) -> Option<Handle> {
match &line.content {
Content::Bead(_) | Content::Unread(_) => line.place.clone().map(Handle::Bead),
Content::Project(line) => Some(Handle::Project(line.project.clone())),
Content::Elided { under, .. } => Some(Handle::Elided(under.clone())),
Content::Group(group) => Some(Handle::Group(group.kind, group.project.clone())),
Content::Item(item) => item_key(item).map(Handle::Item),
Content::Note(_) | Content::Scoped { .. } => None,
}
}
pub(super) fn selectable(line: &Line) -> bool {
handle_of(line).is_some()
}
pub(super) fn names(line: &Line, handle: &Handle) -> bool {
match (handle, &line.content) {
(Handle::Bead(place), Content::Bead(_) | Content::Unread(_)) => {
line.place.as_ref() == Some(place)
}
(Handle::Project(project), Content::Project(line)) => line.project == *project,
(Handle::Elided(place), Content::Elided { under, .. }) => under == place,
(Handle::Group(kind, project), Content::Group(group)) => {
group.kind == *kind && group.project == *project
}
(Handle::Item(key), Content::Item(item)) => item_key(item).as_ref() == Some(key),
_ => false,
}
}