use std::cmp::Ordering;
use std::hash::Hash;
use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
static NEXT_QUERY_POLICY_GENERATION: AtomicU64 = AtomicU64::new(1);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TreeChildren<'a, Id> {
Leaf,
Unloaded,
Loading,
Loaded(&'a [Id]),
}
impl<'a, Id> TreeChildren<'a, Id> {
#[must_use]
pub const fn loaded(children: &'a [Id]) -> Self {
if children.is_empty() {
Self::Leaf
} else {
Self::Loaded(children)
}
}
#[must_use]
pub const fn loaded_slice(self) -> &'a [Id] {
match self {
Self::Loaded(children) => children,
Self::Leaf | Self::Unloaded | Self::Loading => &[],
}
}
#[must_use]
pub const fn is_branch(self) -> bool {
!matches!(self, Self::Leaf)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum TreeRootVisibility {
#[default]
Visible,
Hidden,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum TreeSelectionFallback {
#[default]
ParentThenNearest,
Nearest,
Clear,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum TreeFilterConfig {
#[default]
Disabled,
Enabled {
auto_expand: bool,
},
}
impl TreeFilterConfig {
#[must_use]
pub const fn enabled() -> Self {
Self::Enabled { auto_expand: true }
}
#[must_use]
pub const fn enabled_manual_expand() -> Self {
Self::Enabled { auto_expand: false }
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TreeRevision(u64);
impl TreeRevision {
pub const INITIAL: Self = Self(0);
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
#[must_use]
pub const fn next(self) -> Self {
Self(self.0.wrapping_add(1))
}
pub const fn advance(&mut self) {
*self = self.next();
}
}
impl From<u64> for TreeRevision {
fn from(value: u64) -> Self {
Self::new(value)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NoFilter;
impl<T: TreeModel> TreeFilter<T> for NoFilter {
#[inline]
fn is_match(&self, _model: &T, _id: T::Id) -> bool {
true
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NoSort;
impl<T: TreeModel> TreeSort<T> for NoSort {
fn compare(&self, _model: &T, _left: T::Id, _right: T::Id) -> Ordering {
Ordering::Equal
}
fn is_enabled(&self) -> bool {
false
}
}
#[derive(Clone, Debug)]
pub struct TreeQuery<F = NoFilter, S = NoSort> {
filter: QueryPolicy<F>,
sort: QueryPolicy<S>,
filter_config: TreeFilterConfig,
root_visibility: TreeRootVisibility,
selection_fallback: TreeSelectionFallback,
}
impl TreeQuery {
#[must_use]
pub const fn new() -> Self {
Self {
filter: QueryPolicy::new(NoFilter, TreeRevision::INITIAL),
sort: QueryPolicy::new(NoSort, TreeRevision::INITIAL),
filter_config: TreeFilterConfig::Disabled,
root_visibility: TreeRootVisibility::Visible,
selection_fallback: TreeSelectionFallback::ParentThenNearest,
}
}
}
impl<F, S> TreeQuery<F, S> {
#[must_use]
pub fn with_filter<NF>(
self,
filter: NF,
config: TreeFilterConfig,
revision: TreeRevision,
) -> TreeQuery<NF, S> {
TreeQuery {
filter: QueryPolicy::replacement(filter, revision),
sort: self.sort,
filter_config: config,
root_visibility: self.root_visibility,
selection_fallback: self.selection_fallback,
}
}
#[must_use]
pub fn with_sort<NS>(self, sort: NS, revision: TreeRevision) -> TreeQuery<F, NS> {
TreeQuery {
filter: self.filter,
sort: QueryPolicy::replacement(sort, revision),
filter_config: self.filter_config,
root_visibility: self.root_visibility,
selection_fallback: self.selection_fallback,
}
}
#[must_use]
pub const fn with_filter_config(mut self, config: TreeFilterConfig) -> Self {
self.filter_config = config;
self
}
#[must_use]
pub const fn with_root_visibility(mut self, visibility: TreeRootVisibility) -> Self {
self.root_visibility = visibility;
self
}
#[must_use]
pub const fn with_selection_fallback(mut self, fallback: TreeSelectionFallback) -> Self {
self.selection_fallback = fallback;
self
}
#[must_use]
pub const fn filter(&self) -> &F {
&self.filter.value
}
pub const fn filter_mut(&mut self) -> &mut F {
self.filter.value_mut()
}
#[must_use]
pub const fn sort(&self) -> &S {
&self.sort.value
}
pub const fn sort_mut(&mut self) -> &mut S {
self.sort.value_mut()
}
pub const fn touch_filter(&mut self) {
self.filter.touch();
}
pub const fn touch_sort(&mut self) {
self.sort.touch();
}
pub fn set_filter_config(&mut self, config: TreeFilterConfig) -> bool {
if self.filter_config == config {
return false;
}
self.filter_config = config;
true
}
pub fn set_root_visibility(&mut self, visibility: TreeRootVisibility) -> bool {
let changed = self.root_visibility != visibility;
self.root_visibility = visibility;
changed
}
pub fn set_selection_fallback(&mut self, fallback: TreeSelectionFallback) -> bool {
let changed = self.selection_fallback != fallback;
self.selection_fallback = fallback;
changed
}
#[must_use]
pub const fn filter_config(&self) -> TreeFilterConfig {
self.filter_config
}
#[must_use]
pub const fn root_visibility(&self) -> TreeRootVisibility {
self.root_visibility
}
#[must_use]
pub const fn selection_fallback(&self) -> TreeSelectionFallback {
self.selection_fallback
}
#[must_use]
pub const fn filter_revision(&self) -> TreeRevision {
self.filter.revision
}
#[must_use]
pub const fn sort_revision(&self) -> TreeRevision {
self.sort.revision
}
pub(crate) const fn filter_generation(&self) -> TreeRevision {
self.filter.generation
}
pub(crate) const fn sort_generation(&self) -> TreeRevision {
self.sort.generation
}
}
impl Default for TreeQuery {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
struct QueryPolicy<P> {
value: P,
revision: TreeRevision,
generation: TreeRevision,
}
impl<P> QueryPolicy<P> {
const fn new(value: P, revision: TreeRevision) -> Self {
Self {
value,
revision,
generation: TreeRevision::INITIAL,
}
}
fn replacement(value: P, revision: TreeRevision) -> Self {
Self {
value,
revision,
generation: next_query_policy_generation(),
}
}
const fn value_mut(&mut self) -> &mut P {
self.revision.advance();
&mut self.value
}
const fn touch(&mut self) {
self.revision.advance();
}
}
pub trait TreeModel {
type Id: Copy + Eq + Hash;
fn roots(&self) -> impl Iterator<Item = Self::Id> + '_;
fn children(&self, id: Self::Id) -> TreeChildren<'_, Self::Id>;
fn revision(&self) -> TreeRevision;
fn size_hint(&self) -> usize {
0
}
}
pub trait TreeFilter<T: TreeModel> {
fn is_match(&self, model: &T, id: T::Id) -> bool;
}
impl<T, F> TreeFilter<T> for F
where
T: TreeModel,
F: Fn(&T, T::Id) -> bool,
{
#[inline]
fn is_match(&self, model: &T, id: T::Id) -> bool {
self(model, id)
}
}
pub trait TreeSort<T: TreeModel> {
fn compare(&self, model: &T, left: T::Id, right: T::Id) -> Ordering;
fn is_enabled(&self) -> bool {
true
}
}
impl<T, F> TreeSort<T> for F
where
T: TreeModel,
F: Fn(&T, T::Id, T::Id) -> Ordering,
{
fn compare(&self, model: &T, left: T::Id, right: T::Id) -> Ordering {
self(model, left, right)
}
}
fn next_query_policy_generation() -> TreeRevision {
TreeRevision::new(NEXT_QUERY_POLICY_GENERATION.fetch_add(1, AtomicOrdering::Relaxed))
}