|
| 1 | +//! JSON Schema completion support for declarative task factories. |
| 2 | +//! |
| 3 | +//! This module enriches the generated supervisor configuration schema with |
| 4 | +//! `factory_key` values from the caller-provided task factory registry. |
| 5 | +
|
| 6 | +use crate::config::configurable::SupervisorConfig; |
| 7 | +use crate::error::types::SupervisorError; |
| 8 | +use crate::task::factory_registry::TaskFactoryRegistry; |
| 9 | +use serde_json::{Value, json}; |
| 10 | + |
| 11 | +/// Builds a supervisor configuration schema with `factory_key` completion values. |
| 12 | +/// |
| 13 | +/// # Arguments |
| 14 | +/// |
| 15 | +/// - `registry`: Task factory registry that supplies valid completion keys. |
| 16 | +/// |
| 17 | +/// # Returns |
| 18 | +/// |
| 19 | +/// Returns a JSON Schema value whose `ChildDeclaration.factory_key` field |
| 20 | +/// contains registry-backed completion candidates. |
| 21 | +/// |
| 22 | +/// # Errors |
| 23 | +/// |
| 24 | +/// Returns [`SupervisorError`] when the generated schema does not expose the |
| 25 | +/// expected `ChildDeclaration.factory_key` property. |
| 26 | +/// |
| 27 | +/// # Examples |
| 28 | +/// |
| 29 | +/// ``` |
| 30 | +/// use rust_supervisor::config::factory_schema::supervisor_schema_with_factory_registry; |
| 31 | +/// use rust_supervisor::spec::child::TaskKind; |
| 32 | +/// use rust_supervisor::task::factory::{TaskResult, service_fn}; |
| 33 | +/// use rust_supervisor::task::factory_registry::{ |
| 34 | +/// TaskFactoryDescriptor, TaskFactoryRegistry, |
| 35 | +/// }; |
| 36 | +/// use std::sync::Arc; |
| 37 | +/// |
| 38 | +/// # fn example() -> Result<(), rust_supervisor::error::types::SupervisorError> { |
| 39 | +/// let mut registry = TaskFactoryRegistry::new(); |
| 40 | +/// registry.register(TaskFactoryDescriptor::new( |
| 41 | +/// "worker", |
| 42 | +/// "Worker", |
| 43 | +/// "Runs one worker.", |
| 44 | +/// [TaskKind::AsyncWorker], |
| 45 | +/// Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded })), |
| 46 | +/// ))?; |
| 47 | +/// let schema = supervisor_schema_with_factory_registry(®istry)?; |
| 48 | +/// let schema_text = serde_json::to_string(&schema).unwrap(); |
| 49 | +/// assert!(schema_text.contains("worker")); |
| 50 | +/// # Ok(()) |
| 51 | +/// # } |
| 52 | +/// ``` |
| 53 | +pub fn supervisor_schema_with_factory_registry( |
| 54 | + registry: &TaskFactoryRegistry, |
| 55 | +) -> Result<Value, SupervisorError> { |
| 56 | + let schema = schemars::schema_for!(SupervisorConfig); |
| 57 | + let mut value = serde_json::to_value(&schema).map_err(|error| { |
| 58 | + SupervisorError::fatal_config(format!("failed to serialize supervisor schema: {error}")) |
| 59 | + })?; |
| 60 | + inject_factory_key_completions(&mut value, registry)?; |
| 61 | + Ok(value) |
| 62 | +} |
| 63 | + |
| 64 | +/// Injects registry-backed completion values into an existing schema. |
| 65 | +/// |
| 66 | +/// # Arguments |
| 67 | +/// |
| 68 | +/// - `schema`: Schema value generated from [`SupervisorConfig`]. |
| 69 | +/// - `registry`: Task factory registry that supplies valid completion keys. |
| 70 | +/// |
| 71 | +/// # Returns |
| 72 | +/// |
| 73 | +/// Returns `Ok(())` after completion candidates have been injected. |
| 74 | +/// |
| 75 | +/// # Errors |
| 76 | +/// |
| 77 | +/// Returns [`SupervisorError`] when the schema does not expose the expected |
| 78 | +/// `ChildDeclaration.factory_key` property. |
| 79 | +pub fn inject_factory_key_completions( |
| 80 | + schema: &mut Value, |
| 81 | + registry: &TaskFactoryRegistry, |
| 82 | +) -> Result<(), SupervisorError> { |
| 83 | + let pointer = if schema |
| 84 | + .pointer("/definitions/ChildDeclaration/properties/factory_key") |
| 85 | + .is_some() |
| 86 | + { |
| 87 | + "/definitions/ChildDeclaration/properties/factory_key" |
| 88 | + } else if schema |
| 89 | + .pointer("/$defs/ChildDeclaration/properties/factory_key") |
| 90 | + .is_some() |
| 91 | + { |
| 92 | + "/$defs/ChildDeclaration/properties/factory_key" |
| 93 | + } else { |
| 94 | + return Err(SupervisorError::fatal_config( |
| 95 | + "supervisor schema is missing ChildDeclaration.factory_key", |
| 96 | + )); |
| 97 | + }; |
| 98 | + let factory_key_schema = schema.pointer_mut(pointer).ok_or_else(|| { |
| 99 | + SupervisorError::fatal_config("supervisor schema is missing ChildDeclaration.factory_key") |
| 100 | + })?; |
| 101 | + |
| 102 | + let choices = registry |
| 103 | + .descriptors() |
| 104 | + .into_iter() |
| 105 | + .map(|descriptor| { |
| 106 | + json!({ |
| 107 | + "const": descriptor.key, |
| 108 | + "title": descriptor.title, |
| 109 | + "description": descriptor.description, |
| 110 | + }) |
| 111 | + }) |
| 112 | + .collect::<Vec<_>>(); |
| 113 | + |
| 114 | + factory_key_schema["oneOf"] = Value::Array(choices); |
| 115 | + factory_key_schema["description"] = Value::String( |
| 116 | + "TaskFactory registry key used to bind worker children before startup.".to_owned(), |
| 117 | + ); |
| 118 | + Ok(()) |
| 119 | +} |
0 commit comments