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>) {
140 let old_children = {
141 let mut state = self.state();
142 std::mem::replace(&mut state.children, new_children.clone())
143 };
144 for child in &new_children {
145 child.state().parent = Some(Arc::downgrade(&self.inner));
146 }
147 for child in &old_children {
148 let still_present = new_children.iter().any(|kept| Entry::ptr_eq(kept, child));
149 if !still_present {
150 child.state().parent = None;
151 }
152 }
153 }
154
155 pub fn path(&self) -> String {
157 let mut parts = vec![self.inner.id.clone()];
158 let mut current = self.parent();
159 while let Some(parent) = current {
160 if parent.inner.id.is_empty() {
161 break;
162 }
163 parts.push(parent.inner.id.clone());
164 current = parent.parent();
165 }
166 parts.reverse();
167 parts.join(":")
168 }
169
170 pub fn disabled(&self) -> bool {
175 self.state().options.disabled.is_disabled()
176 }
177
178 pub fn resolved_disabled(&self) -> Result<bool> {
181 let disabled = self.state().options.disabled.clone();
182 match disabled {
183 Disabled::Flag(flag) => Ok(flag),
184 Disabled::Expr(source) => match crate::expr::evaluate(&source)? {
185 crate::node::Node::Bool(flag) => Ok(flag),
186 other => Err(IncludeError::JsExpression {
187 message: format!(
188 "the disabled expression must evaluate to a boolean, found {}",
189 crate::yaml::node_kind(&other)
190 ),
191 expression: source,
192 }),
193 },
194 }
195 }
196
197 pub fn enabled(&self) -> bool {
203 if self.is_root() {
204 return true;
205 }
206 !self.disabled() && self.parent().is_none_or(|parent| parent.enabled())
207 }
208
209 pub fn resolved_enabled(&self) -> Result<bool> {
213 if self.is_root() {
214 return Ok(true);
215 }
216 if self.resolved_disabled()? {
217 return Ok(false);
218 }
219 match self.parent() {
220 Some(parent) => parent.resolved_enabled(),
221 None => Ok(true),
222 }
223 }
224
225 pub fn fiber(&self) -> Option<Fiber> {
227 self.state().fiber.clone()
228 }
229
230 pub fn set_fiber(&self, fiber: Option<Fiber>) {
232 self.state().fiber = fiber;
233 }
234
235 pub fn suspend(&self) -> EntrySuspendGuard {
238 {
239 let mut state = self.state();
240 state.suspend += 1;
241 }
242 EntrySuspendGuard {
243 entry: self.clone(),
244 }
245 }
246
247 pub fn is_suspended(&self) -> bool {
249 self.state().suspend > 0
250 }
251
252 pub(crate) fn is_root(&self) -> bool {
253 self.inner.id.is_empty()
254 }
255
256 pub(crate) fn contains(&self, other: &Entry) -> bool {
258 if Entry::ptr_eq(self, other) {
259 return true;
260 }
261 other.parent().is_some_and(|parent| self.contains(&parent))
262 }
263}
264
265impl std::fmt::Debug for Entry {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 f.debug_struct("Entry")
268 .field("id", &self.inner.id)
269 .field("name", &self.name())
270 .finish_non_exhaustive()
271 }
272}
273
274#[derive(Debug)]
279pub struct EntrySuspendGuard {
280 entry: Entry,
281}
282
283impl Drop for EntrySuspendGuard {
284 fn drop(&mut self) {
285 let mut state = self.entry.state();
286 state.suspend = state.suspend.saturating_sub(1);
287 }
288}