1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Config entry trees and loader files for the
//! [cordis-rs](https://crates.io/crates/cordis-rs) plugin framework.
//!
//! This crate is the data half of porting upstream Cordis' loader: it maps
//! between config files on disk and an in-memory tree of
//! [`Entry`] nodes, each described by [`EntryOptions`]. It deliberately
//! knows nothing about *where plugins come from* and never starts or stops
//! fibers — that is `cordis-loader`'s job, plugged in through the
//! [`PluginResolver`] trait defined here.
//!
//! # Example
//!
//! ```no_run
//! use cordis_include::{Document, EntryOptions, EntryTree, LoaderFile};
//!
//! # fn main() -> cordis_include::Result<()> {
//! let file = LoaderFile::open("cordis.yml")?;
//! let mut document = file.read()?;
//!
//! let tree = EntryTree::new();
//! let diff = tree.update(document.entries)?;
//! for entry in &diff.created {
//! println!("new entry {} ({})", entry.path(), entry.name());
//! }
//!
//! // Persist generated ids and later edits back to the file.
//! document.entries = tree.serialize();
//! file.write(&document)?;
//! # Ok(())
//! # }
//! ```
//!
//! # File format
//!
//! A file holds an ordered entry list; nested `group` arrays make groups.
//! Object key order is preserved on round-trip, entry fields serialize as
//! `id`, `name`, `disabled`, `inject`, `group`, `config` (config last), and
//! unknown top-level keys are kept untouched — files stay diff-friendly.
//!
//! ```yaml
//! entries:
//! - id: sched
//! name: group
//! group:
//! - name: adapter-http
//! config:
//! port: 8080
//! host: ${{ env.HOST }}
//! ```
//!
//! `${{ env.NAME }}` templates substitute environment variables when config
//! is handed to a plugin ([`Entry::resolved_config`]); the file itself keeps
//! the template text. `!!js` scalars parse through the crate's own YAML
//! dialect (the [`yaml`] module) and round-trip as expression nodes
//! ([`Node::Expr`]). At the same hand-off point every expression evaluates
//! through the [`expr`] subset — the `process.*` references the shipped
//! bundles use; injected-context expressions (`ctx.*`, `dshHomePath(…)`)
//! fail with a clear subset error. The `disabled` field takes the same
//! `!!js` form (see [`Disabled`]), evaluated at activation through
//! [`Entry::resolved_disabled`].
//!
//! # Patch lists
//!
//! Entry lists compose from *patch* files — bare top-level YAML arrays of
//! [`PatchOptions`] rows (`id`-targeted overrides and `insert` lists), the
//! bundle/profile assembly model. See the [`patch`] module for the apply,
//! composition, provenance, and dump mechanisms.
//!
//! # Suspension
//!
//! Two suspend counters break the reload feedback loop: a file-level guard
//! ([`LoaderFile::suspend`]) suppresses physical writes, and an entry-level
//! guard ([`Entry::suspend`]) tells the loader that an entry's changes came
//! from the file and must not be written back. The `watch` feature adds
//! [`FileWatcher`], a debounced watcher that skips events observed while
//! the file is suspended.
//!
//! # Not in scope
//!
//! Plugin resolution beyond the [`PluginResolver`] contract (static
//! registries and dynamic libraries live in `cordis-loader`), fiber
//! lifecycle, and cascading group semantics (`cordis-group`).
// `deny` instead of `forbid` because the YAML dialect module wraps
// `unsafe-libyaml`'s C-translation parser; every unsafe operation lives in
// that one module, item-scoped behind `#[allow(unsafe_code)]` with SAFETY
// notes (the same pattern `cordis-loader` uses for libloading).
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use PluginResolver;
pub use ;
pub use FileWatcher;
pub use ;
/// Lock a mutex tolerantly, treating a poisoned lock as unlocked.
///
/// Mirrors the pattern used inside `cordis-rs`: a panic in one thread must
/// not cascade into `unwrap` failures elsewhere. The guarded state may be
/// mid-update, which is acceptable for config trees.
pub