Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

factory_key Configuration

Language: 中文

1. Summary

factory_key is a task factory key in YAML configuration. Its value is the name shared by the configuration file and Rust code, such as api_server. It connects a declarative worker child to a TaskFactory registered in Rust code.

The configuration file stores only declarations. It does not store executable closures. The real task startup logic must be supplied by Rust code.

2. Problem

A Supervisor task tree can declare children through configuration files. However, async_worker and blocking_worker children need an executable TaskFactory when they actually start. A TaskFactory contains Rust code and usually a closure, so it cannot be safely stored directly in YAML.

factory_key defines the boundary. The configuration file writes an agreed key, and Rust code registers a task factory under the same key. Before startup, the system binds the declaration to the executable factory.

3. Configuration

children.yaml can declare workers like this:

- name: api
  kind: async_worker
  factory_key: api_server

- name: exporter
  kind: blocking_worker
  factory_key: report_exporter

api_server and report_exporter are not function names. They are configuration-level task factory keys. Rust code must register matching TaskFactory values.

4. Rust Registration

Rust code uses TaskFactoryRegistry to map keys to TaskFactory values.

#![allow(unused)]
fn main() {
use rust_supervisor::spec::child::TaskKind;
use rust_supervisor::task::factory::{TaskResult, service_fn};
use rust_supervisor::task::factory_registry::{
    TaskFactoryDescriptor, TaskFactoryRegistry,
};
use std::sync::Arc;

let mut registry = TaskFactoryRegistry::new();

registry.register(TaskFactoryDescriptor::new(
    "api_server",
    "API Server",
    "Runs the API service.",
    [TaskKind::AsyncWorker],
    Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded })),
))?;

registry.register(TaskFactoryDescriptor::new(
    "report_exporter",
    "Report Exporter",
    "Runs blocking export work.",
    [TaskKind::BlockingWorker],
    Arc::new(service_fn(|_ctx| async { TaskResult::Succeeded })),
))?;
}

TaskFactoryDescriptor stores 3 kinds of data:

  • key: The task factory key used by configuration files.
  • title and description: Metadata shown by schema-backed editor completion.
  • allowed_kinds: The task kinds that may use this factory, such as TaskKind::AsyncWorker or TaskKind::BlockingWorker.

5. Startup Binding

After configuration loading, factory_key is still only a string. Before startup, the string must be resolved to a real TaskFactory.

The current binding path is:

  1. ConfigState reads child declarations from YAML.
  2. to_supervisor_spec_with_factories uses TaskFactoryRegistry to bind workers.
  3. bind_task_factories checks every worker’s factory_key.
  4. The registry resolves the matching TaskFactory and writes it into ChildSpec.
  5. Supervisor starts with already-bound executable task factories.

Binding rules:

  • Worker children must declare factory_key.
  • Supervisor child nodes must not declare factory_key.
  • An unknown factory_key causes a configuration error.
  • A factory that does not support the current TaskKind causes a configuration error.

6. Completion Generation

Editor completion depends on JSON Schema. The current implementation does not rewrite the rust-config-tree schema generator. Instead, it post-processes the base schema generated by rust-config-tree.

The flow is:

  1. generate-template or generate-schema asks rust-config-tree to generate the base schema.
  2. supervisor_schema_targets_with_factory_registry receives the root schema and split-section schemas.
  3. Each schema is parsed into serde_json::Value.
  4. inject_factory_key_completions_if_present finds the factory_key field.
  5. The system writes keys from TaskFactoryRegistry into oneOf.
  6. The schema is serialized again and written to the target file.

After generation, children.schema.json contains a factory_key field like this:

{
  "factory_key": {
    "description": "TaskFactory registry key used to bind worker children before startup.",
    "oneOf": [
      {
        "const": "api_server",
        "description": "Runs the API service.",
        "title": "API Server"
      },
      {
        "const": "report_exporter",
        "description": "Runs blocking export work.",
        "title": "Report Exporter"
      }
    ],
    "type": [
      "string",
      "null"
    ]
  }
}

When an editor reads the yaml-language-server schema directive at the top of children.yaml, it can offer factory_key candidates.

7. Commands

Generate templates:

target/debug/rust-tokio-supervisor generate-template

This command writes configuration templates and schemas with completion metadata.

Generate schemas only:

target/debug/rust-tokio-supervisor generate-schema

This command writes schemas only, and the generated schema also contains factory_key candidates in oneOf.

8. Current Boundaries

  • factory_key is a configuration declaration, not executable code.
  • Completion candidates come from the TaskFactoryRegistry used by the command.
  • If Rust code does not register a key, a configuration file using that key cannot start.
  • Schema-backed completion helps editors suggest valid candidates, but it does not replace startup binding validation.
  • Runtime child addition goes through the same kind of binding validation, so dynamic additions cannot bypass the registry.