1use crate::error::Result;
4use crate::interpolate::interpolate_node;
5use crate::lock;
6use crate::options::EntryOptions;
7use cordis::Fiber;
8use std::sync::{Arc, Mutex, MutexGuard, Weak};
9
10#[derive(Clone)]
17pub struct Entry {
18 inner: Arc<EntryInner>,
19}
20
21struct EntryInner {
23 id: String,
24 state: Mutex<EntryState>,
25}
26
27pub(crate) struct EntryState {
29 options: EntryOptions,
30 parent: Option<Weak<EntryInner>>,
31 children: Vec<Entry>,
32 fiber: Option<Fiber>,
33 suspend: usize,
34}
35
36impl Entry {
37 pub(crate) fn new(id: String, options: EntryOptions) -> Self {
39 debug_assert_eq!(options.id.as_deref(), Some(id.as_str()));
40 Self {
41 inner: Arc::new(EntryInner {
42 id,
43 state: Mutex::new(EntryState {
44 options,
45 parent: None,
46 children: Vec::new(),
47 fiber: None,
48 suspend: 0,
49 }),
50 }),
51 }
52 }
53
54 pub(crate) fn new_root() -> Self {
56 Self::new(
57 String::new(),
58 EntryOptions {
59 id: Some(String::new()),
60 ..EntryOptions::default()
61 },
62 )
63 }
64
65 pub fn id(&self) -> &str {
67 &self.inner.id
68 }
69
70 pub fn ptr_eq(left: &Entry, right: &Entry) -> bool {
72 Arc::ptr_eq(&left.inner, &right.inner)
73 }
74
75 pub(crate) fn state(&self) -> MutexGuard<'_, EntryState> {
77 lock(&self.inner.state)
78 }
79
80 pub fn name(&self) -> String {
82 self.state().options.name.clone()
83 }
84
85 pub fn options(&self) -> EntryOptions {
88 let mut options = self.state().options.clone();
89 options.group = Vec::new();
90 options
91 }
92
93 pub(crate) fn set_options(&self, options: EntryOptions) {
95 let mut state = self.state();
96 state.options = options;
97 state.options.id = Some(self.inner.id.clone());
98 state.options.group = Vec::new();
99 }
100
101 pub fn config(&self) -> Option<crate::node::Node> {
103 self.state().options.config.clone()
104 }
105
106 pub fn resolved_config(&self) -> Result<Option<crate::node::Node>> {
109 match self.config() {
110 Some(node) => interpolate_node(&node).map(Some),
111 None => Ok(None),
112 }
113 }
114
115 pub fn parent(&self) -> Option<Entry> {
117 let parent = self.state().parent.clone();
118 parent
119 .and_then(|weak| weak.upgrade())
120 .map(|inner| Entry { inner })
121 }
122
123 pub fn children(&self) -> Vec<Entry> {
125 self.state().children.clone()
126 }
127
128 pub fn is_group(&self) -> bool {
130 !self.state().children.is_empty()
131 }
132
133 pub(crate) fn set_children(&self, new_children: Vec<Entry>) {
137 let old_children = {
138 let mut state = self.state();
139 std::mem::replace(&mut state.children, new_children.clone())
140 };
141 for child in &new_children {
142 child.state().parent = Some(Arc::downgrade(&self.inner));
143 }
144 for child in &old_children {
145 let still_present = new_children.iter().any(|kept| Entry::ptr_eq(kept, child));
146 if !still_present {
147 child.state().parent = None;
148 }
149 }
150 }
151
152 pub fn path(&self) -> String {
154 let mut parts = vec![self.inner.id.clone()];
155 let mut current = self.parent();
156 while let Some(parent) = current {
157 if parent.inner.id.is_empty() {
158 break;
159 }
160 parts.push(parent.inner.id.clone());
161 current = parent.parent();
162 }
163 parts.reverse();
164 parts.join(":")
165 }
166
167 pub fn disabled(&self) -> bool {
169 self.state().options.disabled
170 }
171
172 pub fn enabled(&self) -> bool {
175 if self.is_root() {
176 return true;
177 }
178 !self.disabled() && self.parent().is_none_or(|parent| parent.enabled())
179 }
180
181 pub fn fiber(&self) -> Option<Fiber> {
183 self.state().fiber.clone()
184 }
185
186 pub fn set_fiber(&self, fiber: Option<Fiber>) {
188 self.state().fiber = fiber;
189 }
190
191 pub fn suspend(&self) -> EntrySuspendGuard {
194 {
195 let mut state = self.state();
196 state.suspend += 1;
197 }
198 EntrySuspendGuard {
199 entry: self.clone(),
200 }
201 }
202
203 pub fn is_suspended(&self) -> bool {
205 self.state().suspend > 0
206 }
207
208 pub(crate) fn is_root(&self) -> bool {
209 self.inner.id.is_empty()
210 }
211
212 pub(crate) fn contains(&self, other: &Entry) -> bool {
214 if Entry::ptr_eq(self, other) {
215 return true;
216 }
217 other.parent().is_some_and(|parent| self.contains(&parent))
218 }
219}
220
221impl std::fmt::Debug for Entry {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 f.debug_struct("Entry")
224 .field("id", &self.inner.id)
225 .field("name", &self.name())
226 .finish_non_exhaustive()
227 }
228}
229
230#[derive(Debug)]
235pub struct EntrySuspendGuard {
236 entry: Entry,
237}
238
239impl Drop for EntrySuspendGuard {
240 fn drop(&mut self) {
241 let mut state = self.entry.state();
242 state.suspend = state.suspend.saturating_sub(1);
243 }
244}