#[allow(
clippy::derivable_impls,
clippy::incompatible_msrv,
clippy::unwrap_used
)]
mod generated {
include!(concat!(env!("OUT_DIR"), "/capability_manifest_types.rs"));
}
pub use generated::*;
pub use NonoCapabilityManifest as CapabilityManifest;
impl CapabilityManifest {
pub fn from_json(json: &str) -> crate::Result<Self> {
serde_json::from_str(json).map_err(|e| {
crate::NonoError::ConfigParse(format!("invalid capability manifest JSON: {e}"))
})
}
pub fn to_json(&self) -> crate::Result<String> {
serde_json::to_string_pretty(self).map_err(|e| {
crate::NonoError::ConfigParse(format!("failed to serialize manifest: {e}"))
})
}
pub fn validate(&self) -> crate::Result<()> {
if let Some(ref rb) = self.rollback {
if rb.enabled {
let exec_strategy = self
.process
.as_ref()
.map_or(ExecStrategy::Monitor, |p| p.exec_strategy);
if exec_strategy != ExecStrategy::Supervised {
return Err(crate::NonoError::ConfigParse(
"rollback.enabled: true requires exec_strategy: \"supervised\" \
(rollback needs a parent process for snapshots)"
.to_string(),
));
}
}
}
for cred in &self.credentials {
let source = cred.source.as_str();
if (crate::keystore::is_op_uri(source)
|| crate::keystore::is_apple_password_uri(source)
|| crate::keystore::is_file_uri(source))
&& cred.env_var.is_none()
{
return Err(crate::NonoError::ConfigParse(format!(
"credential '{}': env_var is required when source is a URI manager \
reference (op://, apple-password://, or file://); \
set it to the SDK API key env var name (e.g., \"OPENAI_API_KEY\")",
cred.name.as_str()
)));
}
if let Some(ref inject) = cred.inject {
match inject.mode {
InjectMode::UrlPath if inject.path_pattern.is_none() => {
return Err(crate::NonoError::ConfigParse(format!(
"credential '{}': url_path inject mode requires path_pattern",
cred.name.as_str()
)));
}
InjectMode::QueryParam if inject.query_param_name.is_none() => {
return Err(crate::NonoError::ConfigParse(format!(
"credential '{}': query_param inject mode requires query_param_name",
cred.name.as_str()
)));
}
_ => {}
}
}
}
Ok(())
}
}