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 disabled(&self) -> bool {
180 self.state().options.disabled.is_disabled()
181 }
182
183 pub fn resolved_disabled(&self) -> Result<bool> {
186 let disabled = self.state().options.disabled.clone();
187 match disabled {
188 Disabled::Flag(flag) => Ok(flag),
189 Disabled::Expr(source) => match crate::expr::evaluate(&source)? {
190 crate::node::Node::Bool(flag) => Ok(flag),
191 other => Err(IncludeError::JsExpression {
192 message: format!(
193 "the disabled expression must evaluate to a boolean, found {}",
194 crate::yaml::node_kind(&other)
195 ),
196 expression: source,
197 }),
198 },
199 }
200 }
201
202 pub fn enabled(&self) -> bool {
208 if self.is_root() {
209 return true;
210 }
211 !self.disabled() && self.parent().is_none_or(|parent| parent.enabled())
212 }
213
214 pub fn resolved_enabled(&self) -> Result<bool> {
218 if self.is_root() {
219 return Ok(true);
220 }
221 if self.resolved_disabled()? {
222 return Ok(false);
223 }
224 match self.parent() {
225 Some(parent) => parent.resolved_enabled(),
226 None => Ok(true),
227 }
228 }
229
230 pub fn fiber(&self) -> Option<Fiber> {
232 self.state().fiber.clone()
233 }
234
235 pub fn set_fiber(&self, fiber: Option<Fiber>) {
237 self.state().fiber = fiber;
238 }
239
240 pub fn suspend(&self) -> EntrySuspendGuard {
243 {
244 let mut state = self.state();
245 state.suspend += 1;
246 }
247 EntrySuspendGuard {
248 entry: self.clone(),
249 }
250 }
251
252 pub fn is_suspended(&self) -> bool {
254 self.state().suspend > 0
255 }
256
257 pub(crate) fn is_root(&self) -> bool {
258 self.inner.id.is_empty()
259 }
260
261 pub(crate) fn contains(&self, other: &Entry) -> bool {
263 if Entry::ptr_eq(self, other) {
264 return true;
265 }
266 other.parent().is_some_and(|parent| self.contains(&parent))
267 }
268}
269
270impl std::fmt::Debug for Entry {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 f.debug_struct("Entry")
273 .field("id", &self.inner.id)
274 .field("name", &self.name())
275 .finish_non_exhaustive()
276 }
277}
278
279#[derive(Debug)]
284pub struct EntrySuspendGuard {
285 entry: Entry,
286}
287
288impl Drop for EntrySuspendGuard {
289 fn drop(&mut self) {
290 let mut state = self.entry.state();
291 state.suspend = state.suspend.saturating_sub(1);
292 }
293}