| 1110 | /// when none exist. Internal: `pub` only for cross-crate reuse by the gateway. |
| 1111 | #[doc(hidden)] |
| 1112 | pub fn load_plugin_config_files<I>(paths: I) -> Result<Option<(Json, Vec<PathBuf>)>> |
| 1113 | where |
| 1114 | I: IntoIterator<Item = PathBuf>, |
| 1115 | { |
| 1116 | let mut merged = Json::Object(Map::new()); |
| 1117 | let mut sources = Vec::new(); |
| 1118 | for path in paths { |
| 1119 | if !path.exists() { |
| 1120 | continue; |
| 1121 | } |
| 1122 | let raw = std::fs::read_to_string(&path).map_err(|err| { |
| 1123 | PluginError::InvalidConfig(format!("failed to read {}: {err}", path.display())) |
| 1124 | })?; |
| 1125 | let parsed = raw.parse::<toml::Table>().map_err(|err| { |
| 1126 | PluginError::InvalidConfig(format!("invalid plugin TOML in {}: {err}", path.display())) |
| 1127 | })?; |
| 1128 | let document = serde_json::to_value(parsed)?; |
| 1129 | validate_unique_component_kinds(&path, &document)?; |
| 1130 | layer_config(&mut merged, document); |
| 1131 | sources.push(path); |
| 1132 | } |
| 1133 | Ok((!sources.is_empty()).then_some((merged, sources))) |
| 1134 | } |
| 1135 | |
| 1136 | /// Rejects a single file that declares the same component `kind` more than once. |
| 1137 | fn validate_unique_component_kinds(path: &Path, document: &Json) -> Result<()> { |