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
130
131
132
133
134
// Location: ./crates/cpex-core/src/visitor.rs
// Copyright 2025
// SPDX-License-Identifier: Apache-2.0
// Authors: Teryl Taylor
//
// `ConfigVisitor` — extension point for external orchestrators (APL,
// future Rego/Cedar-direct/custom) to participate in unified-config
// loading without cpex-core taking a dep on any specific orchestrator.
//
// # How it fits
//
// The host calls `PluginManager::load_config_yaml(yaml)`. cpex-core
// parses the YAML twice (once into a typed `CpexConfig`, once into a
// raw `serde_yaml::Value`), runs its own plugin instantiation, then
// walks each registered visitor in registration order:
//
// 1. `visit_plugins` — once per visitor, immediately after
// cpex-core's own plugin instantiation,
// receiving the parsed `&[PluginConfig]`
// so the visitor doesn't have to re-parse
// the root `plugins:` block from raw YAML.
// 2. `visit_global` — global config block
// 3. `visit_default` — once per entity_type with a default
// 4. `visit_policy_bundle` — once per named policy group (tag)
// 5. `visit_route` — once per route
//
// Each visitor sees the **raw YAML** so it can find its own block
// (e.g. `apl:`) under any section without cpex-core having to know
// about it. Parsed sibling data is passed alongside (`RouteEntry` for
// routes) for convenience — e.g. APL needs to know whether a route
// matches `tool:` or `resource:` to build the annotation key.
//
// # Why visit per-section rather than per-whole-config
//
// Visitors typically accumulate state across the hierarchy (e.g. APL's
// visitor compiles globals/defaults/tag-bundles into `CompiledRoute`s
// kept in visitor state, then merges them into each route at
// `visit_route`). Per-section calls give the orchestrator a natural
// place to do that accumulation without re-parsing.
//
// # Visit order
//
// All sections for one visitor run before the next visitor starts. For
// single-visitor deployments (the common case) this is identical to
// any other ordering; for multi-visitor it gives each visitor a
// consistent view of its own internal state. Visitor methods are
// invoked synchronously — no async runtime needed at load time.
use Arc;
use crateRouteEntry;
use cratePluginManager;
use cratePluginConfig;
/// Error type returned by a config visitor. Boxed `dyn Error` so each
/// orchestrator can carry its own error variants (parse errors, missing
/// plugin references, etc.) without cpex-core having to enumerate them.
pub type VisitorError = ;
/// Extension point for external orchestrators to participate in unified
/// config loading. Register via [`PluginManager::register_visitor`];
/// invoked during [`PluginManager::load_config_yaml`].
///
/// All methods have default no-op implementations — a visitor only
/// overrides the sections it cares about.