1use crate::error::{IncludeError, Result};
4use crate::expr::evaluate_node;
5use crate::interpolate::interpolate_node;
6use crate::lock;
7use crate::options::{Disabled, EntryOptions};
8use cordis::Fiber;
9use std::sync::{Arc, Mutex, MutexGuard, Weak};
10
11#[derive(Clone)]
18pub struct Entry {
19 inner: Arc<EntryInner>,
20}
21
22struct EntryInner {
24 id: String,
25 state: Mutex<EntryState>,
26}
27
28pub(crate) struct EntryState {
30 options: EntryOptions,
31 parent: Option<Weak<EntryInner>>,
32 children: Vec<Entry>,
33 fiber: Option<Fiber>,
34 suspend: usize,
35}
36
37impl Entry {
38 pub(crate) fn new(id: String, options: EntryOptions) -> Self {
40 debug_assert_eq!(options.id.as_deref(), Some(id.as_str()));
41 Self {
42 inner: Arc::new(EntryInner {
43 id,
44 state: Mutex::new(EntryState {
45 options,
46 parent: None,
47 children: Vec::new(),
48 fiber: None,
49 suspend: 0,
50 }),
51 }),
52 }
53 }
54
55 pub(crate) fn new_root() -> Self {
57 Self::new(
58 String::new(),
59 EntryOptions {
60 id: Some(String::new()),
61 ..EntryOptions::default()
62 },
63 )
64 }
65
66 pub fn id(&self) -> &str {
68 &self.inner.id
69 }
70
71 pub fn ptr_eq(left: &Entry, right: &Entry) -> bool {
73 Arc::ptr_eq(&left.inner, &right.inner)
74 }
75
76 pub(crate) fn state(&self) -> MutexGuard<'_, EntryState> {
78 lock(&self.inner.state)
79 }
80
81 pub fn name(&self) -> String {
83 self.state().options.name.clone()
84 }
85
86 pub fn options(&self) -> EntryOptions {
89 let mut options = self.state().options.clone();
90 options.group = Vec::new();
91 options
92 }
93
94 pub(crate) fn set_options(&self, options: EntryOptions) {
96 let mut state = self.state();
97 state.options = options;
98 state.options.id = Some(self.inner.id.clone());
99 state.options.group = Vec::new();
100 }
101
102 pub fn config(&self) -> Option<crate::node::Node> {
104 self.state().options.config.clone()
105 }
106
107 pub fn resolved_config(&self) -> Result<Option<crate::node::Node>> {
112 match self.config() {
113 Some(node) => evaluate_node(&interpolate_node(&node)?).map(Some),
114 None => Ok(None),
115 }
116 }
117
118 pub fn parent(&self) -> Option<Entry> {
120 let parent = self.state().parent.clone();
121 parent
122 .and_then(|weak| weak.upgrade())
123 .map(|inner| Entry { inner })
124 }
125
126 pub fn children(&self) -> Vec<Entry> {
128 self.state().children.clone()
129 }
130
131 pub fn is_group(&self) -> bool {
133 !self.state().children.is_empty()
134 }
135
136 pub(crate) fn set_children(&self, new_children: Vec<Entry>) {
145 let old_children = {
146 let mut state = self.state();
147 std::mem::replace(&mut state.children, new_children.clone())
148 };
149 for child in &new_children {
150 child.state().parent = Some(Arc::downgrade(&self.inner));
151 }
152 for child in &old_children {
153 let still_present = new_children.iter().any(|kept| Entry::ptr_eq(kept, child));
154 if !still_present && child.parent().is_some_and(|p| Entry::ptr_eq(&p, self)) {
155 child.state().parent = None;
156 }
157 }
158 }
159
160 pub fn path(&self) -> String {
162 let mut parts = vec![self.inner.id.clone()];
163 let mut current = self.parent();
164 while let Some(parent) = current {
165 if parent.inner.id.is_empty() {
166 break;
167 }
168 parts.push(parent.inner.id.clone());
169 current = parent.parent();
170 }
171 parts.reverse();
172 parts.join(":")
173 }
174
175 pub fn is_disabled(&self) -> bool {
184 self.state().options.disabled.is_disabled()
185 }
186
187 pub fn resolved_disabled(&self) -> Result<bool> {
190 let disabled = self.state().options.disabled.clone();
191 match disabled {
192 Disabled::Flag(flag) => Ok(flag),
193 Disabled::Expr(source) => match crate::expr::evaluate(&source)? {
194 crate::node::Node::Bool(flag) => Ok(flag),
195 other => Err(IncludeError::JsExpression {
196 message: format!(
197 "the disabled expression must evaluate to a boolean, found {}",
198 crate::yaml::node_kind(&other)
199 ),
200 expression: source,
201 }),
202 },
203 }
204 }
205
206 pub fn is_enabled(&self) -> bool {
213 if self.is_root() {
214 return true;
215 }
216 !self.is_disabled() && self.parent().is_none_or(|parent| parent.is_enabled())
217 }
218
219 pub fn resolved_enabled(&self) -> Result<bool> {
223 if self.is_root() {
224 return Ok(true);
225 }
226 if self.resolved_disabled()? {
227 return Ok(false);
228 }
229 match self.parent() {
230 Some(parent) => parent.resolved_enabled(),
231 None => Ok(true),
232 }
233 }
234
235 pub fn fiber(&self) -> Option<Fiber> {
237 self.state().fiber.clone()
238 }
239
240 pub fn set_fiber(&self, fiber: Option<Fiber>) {
242 self.state().fiber = fiber;
243 }
244
245 pub fn suspend(&self) -> EntrySuspendGuard {
248 {
249 let mut state = self.state();
250 state.suspend += 1;
251 }
252 EntrySuspendGuard {
253 entry: self.clone(),
254 }
255 }
256
257 pub fn is_suspended(&self) -> bool {
259 self.state().suspend > 0
260 }
261
262 pub(crate) fn is_root(&self) -> bool {
263 self.inner.id.is_empty()
264 }
265
266 pub(crate) fn contains(&self, other: &Entry) -> bool {
268 if Entry::ptr_eq(self, other) {
269 return true;
270 }
271 other.parent().is_some_and(|parent| self.contains(&parent))
272 }
273}
274
275impl std::fmt::Debug for Entry {
276 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 f.debug_struct("Entry")
278 .field("id", &self.inner.id)
279 .field("name", &self.name())
280 .finish_non_exhaustive()
281 }
282}
283
284#[derive(Debug)]
289pub struct EntrySuspendGuard {
290 entry: Entry,
291}
292
293impl Drop for EntrySuspendGuard {
294 fn drop(&mut self) {
295 let mut state = self.entry.state();
296 state.suspend = state.suspend.saturating_sub(1);
297 }
298}