use crate::error::Result;
use crate::interpolate::interpolate_node;
use crate::lock;
use crate::options::EntryOptions;
use cordis::Fiber;
use std::sync::{Arc, Mutex, MutexGuard, Weak};
#[derive(Clone)]
pub struct Entry {
inner: Arc<EntryInner>,
}
struct EntryInner {
id: String,
state: Mutex<EntryState>,
}
pub(crate) struct EntryState {
options: EntryOptions,
parent: Option<Weak<EntryInner>>,
children: Vec<Entry>,
fiber: Option<Fiber>,
suspend: usize,
}
impl Entry {
pub(crate) fn new(id: String, options: EntryOptions) -> Self {
debug_assert_eq!(options.id.as_deref(), Some(id.as_str()));
Self {
inner: Arc::new(EntryInner {
id,
state: Mutex::new(EntryState {
options,
parent: None,
children: Vec::new(),
fiber: None,
suspend: 0,
}),
}),
}
}
pub(crate) fn new_root() -> Self {
Self::new(
String::new(),
EntryOptions {
id: Some(String::new()),
..EntryOptions::default()
},
)
}
pub fn id(&self) -> &str {
&self.inner.id
}
pub fn ptr_eq(left: &Entry, right: &Entry) -> bool {
Arc::ptr_eq(&left.inner, &right.inner)
}
pub(crate) fn state(&self) -> MutexGuard<'_, EntryState> {
lock(&self.inner.state)
}
pub fn name(&self) -> String {
self.state().options.name.clone()
}
pub fn options(&self) -> EntryOptions {
let mut options = self.state().options.clone();
options.group = Vec::new();
options
}
pub(crate) fn set_options(&self, options: EntryOptions) {
let mut state = self.state();
state.options = options;
state.options.id = Some(self.inner.id.clone());
state.options.group = Vec::new();
}
pub fn config(&self) -> Option<crate::node::Node> {
self.state().options.config.clone()
}
pub fn resolved_config(&self) -> Result<Option<crate::node::Node>> {
match self.config() {
Some(node) => interpolate_node(&node).map(Some),
None => Ok(None),
}
}
pub fn parent(&self) -> Option<Entry> {
let parent = self.state().parent.clone();
parent
.and_then(|weak| weak.upgrade())
.map(|inner| Entry { inner })
}
pub fn children(&self) -> Vec<Entry> {
self.state().children.clone()
}
pub fn is_group(&self) -> bool {
!self.state().children.is_empty()
}
pub(crate) fn set_children(&self, new_children: Vec<Entry>) {
let old_children = {
let mut state = self.state();
std::mem::replace(&mut state.children, new_children.clone())
};
for child in &new_children {
child.state().parent = Some(Arc::downgrade(&self.inner));
}
for child in &old_children {
let still_present = new_children.iter().any(|kept| Entry::ptr_eq(kept, child));
if !still_present {
child.state().parent = None;
}
}
}
pub fn path(&self) -> String {
let mut parts = vec![self.inner.id.clone()];
let mut current = self.parent();
while let Some(parent) = current {
if parent.inner.id.is_empty() {
break;
}
parts.push(parent.inner.id.clone());
current = parent.parent();
}
parts.reverse();
parts.join(":")
}
pub fn disabled(&self) -> bool {
self.state().options.disabled
}
pub fn enabled(&self) -> bool {
if self.is_root() {
return true;
}
!self.disabled() && self.parent().is_none_or(|parent| parent.enabled())
}
pub fn fiber(&self) -> Option<Fiber> {
self.state().fiber.clone()
}
pub fn set_fiber(&self, fiber: Option<Fiber>) {
self.state().fiber = fiber;
}
pub fn suspend(&self) -> EntrySuspendGuard {
{
let mut state = self.state();
state.suspend += 1;
}
EntrySuspendGuard {
entry: self.clone(),
}
}
pub fn is_suspended(&self) -> bool {
self.state().suspend > 0
}
pub(crate) fn is_root(&self) -> bool {
self.inner.id.is_empty()
}
pub(crate) fn contains(&self, other: &Entry) -> bool {
if Entry::ptr_eq(self, other) {
return true;
}
other.parent().is_some_and(|parent| self.contains(&parent))
}
}
impl std::fmt::Debug for Entry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Entry")
.field("id", &self.inner.id)
.field("name", &self.name())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct EntrySuspendGuard {
entry: Entry,
}
impl Drop for EntrySuspendGuard {
fn drop(&mut self) {
let mut state = self.entry.state();
state.suspend = state.suspend.saturating_sub(1);
}
}