use crc32fast::Hasher;
#[derive(Clone, Debug)]
pub struct ClosureTreeConfig {
entity_name: String,
hierarchy_name: String,
parent_column: String,
name_column: String,
hierarchy_table: String,
dependent_behavior: DependentBehavior,
order_strategy: Option<OrderStrategy>,
advisory_lock_strategy: AdvisoryLockStrategy,
}
impl ClosureTreeConfig {
pub fn new(entity_name: impl Into<String>, hierarchy_name: impl Into<String>) -> Self {
let entity_name = entity_name.into();
let hierarchy_name = hierarchy_name.into();
let default_lock = AdvisoryLockStrategy::Namespaced(AdvisoryLockKey::derived_from(
&entity_name,
&hierarchy_name,
));
Self {
entity_name,
hierarchy_name,
parent_column: "parent_id".to_string(),
name_column: "name".to_string(),
hierarchy_table: String::new(),
dependent_behavior: DependentBehavior::default(),
order_strategy: None,
advisory_lock_strategy: default_lock,
}
}
pub(crate) fn apply_options(mut self, options: ClosureTreeOptions) -> Self {
if let Some(parent_column) = options.parent_column {
self.parent_column = parent_column;
}
if let Some(name_column) = options.name_column {
self.name_column = name_column;
}
if let Some(hierarchy_table) = options.hierarchy_table {
self.hierarchy_table = hierarchy_table;
}
if let Some(behavior) = options.dependent_behavior {
self.dependent_behavior = behavior;
}
if let Some(order_strategy) = options.order_strategy {
self.order_strategy = Some(order_strategy);
}
if let Some(strategy) = options.advisory_lock_strategy {
self.advisory_lock_strategy = strategy;
}
self
}
pub fn entity_name(&self) -> &str {
&self.entity_name
}
pub fn hierarchy_name(&self) -> &str {
&self.hierarchy_name
}
pub fn parent_column(&self) -> &str {
&self.parent_column
}
pub fn name_column(&self) -> &str {
&self.name_column
}
pub fn hierarchy_table(&self) -> &str {
&self.hierarchy_table
}
pub fn dependent_behavior(&self) -> DependentBehavior {
self.dependent_behavior
}
pub fn order_strategy(&self) -> Option<&OrderStrategy> {
self.order_strategy.as_ref()
}
pub fn advisory_lock_strategy(&self) -> &AdvisoryLockStrategy {
&self.advisory_lock_strategy
}
}
#[derive(Clone, Debug, Default)]
pub struct ClosureTreeOptions {
parent_column: Option<String>,
name_column: Option<String>,
hierarchy_table: Option<String>,
dependent_behavior: Option<DependentBehavior>,
order_strategy: Option<OrderStrategy>,
advisory_lock_strategy: Option<AdvisoryLockStrategy>,
}
impl ClosureTreeOptions {
pub fn parent_column(mut self, value: impl Into<String>) -> Self {
self.parent_column = Some(value.into());
self
}
pub fn name_column(mut self, value: impl Into<String>) -> Self {
self.name_column = Some(value.into());
self
}
pub fn hierarchy_table(mut self, value: impl Into<String>) -> Self {
self.hierarchy_table = Some(value.into());
self
}
pub fn dependent_behavior(mut self, behavior: DependentBehavior) -> Self {
self.dependent_behavior = Some(behavior);
self
}
pub fn order_strategy(mut self, strategy: OrderStrategy) -> Self {
self.order_strategy = Some(strategy);
self
}
pub fn advisory_lock_strategy(mut self, strategy: AdvisoryLockStrategy) -> Self {
self.advisory_lock_strategy = Some(strategy);
self
}
pub fn apply(self, base: ClosureTreeConfig) -> ClosureTreeConfig {
base.apply_options(self)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DependentBehavior {
Nullify,
Destroy,
DeleteAll,
None,
}
impl Default for DependentBehavior {
fn default() -> Self {
Self::Nullify
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OrderStrategy {
Manual,
NumericColumn { column: String },
}
impl OrderStrategy {
pub fn numeric_column(column: impl Into<String>) -> Self {
Self::NumericColumn {
column: column.into(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct AdvisoryLockKey(String);
impl AdvisoryLockKey {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
self.0.as_str()
}
fn derived_from(entity: &str, hierarchy: &str) -> Self {
let mut hasher = Hasher::new();
hasher.update(entity.as_bytes());
hasher.update(b"/");
hasher.update(hierarchy.as_bytes());
let crc = hasher.finalize();
Self(format!("closure-tree::{entity}::{hierarchy}::{crc:x}"))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum AdvisoryLockStrategy {
Disabled,
Namespaced(AdvisoryLockKey),
}
impl AdvisoryLockStrategy {
pub fn key(&self) -> Option<&AdvisoryLockKey> {
match self {
AdvisoryLockStrategy::Disabled => None,
AdvisoryLockStrategy::Namespaced(key) => Some(key),
}
}
}