ModelsConfig.fromYaml constructor

ModelsConfig.fromYaml(
  1. Object? node
)

Parses the models: yaml node, strictly: the node must be a map with only slots/custom keys; slot names must come from mediaModelSlotIds; entry-level rules are in MediaSlotModelConfig.fromYaml and CustomModelDefinition.fromYaml.

Implementation

factory ModelsConfig.fromYaml(Object? node) {
  if (node is! YamlMap) {
    throw ConfigException(
      'models must be a map with slots/custom sections, got: $node',
    );
  }
  for (final key in node.keys) {
    if (key != 'slots' && key != 'custom') {
      throw ConfigException(
        'unknown models section "$key" — expected slots/custom',
      );
    }
  }
  final slots = <String, MediaSlotModelConfig>{};
  switch (node['slots']) {
    case null:
      break;
    case final YamlMap map:
      for (final entry in map.entries) {
        final slot = entry.key;
        if (slot is! String || !mediaModelSlotIds.contains(slot)) {
          throw ConfigException(
            'unknown media slot "$slot" in models.slots — slots: '
            '${mediaModelSlotIds.join(', ')}',
          );
        }
        slots[slot] = MediaSlotModelConfig.fromYaml(entry.value, slot: slot);
      }
    case final other:
      throw ConfigException('models.slots must be a map, got: $other');
  }
  final custom = <String, CustomModelDefinition>{};
  switch (node['custom']) {
    case null:
      break;
    case final YamlMap map:
      for (final entry in map.entries) {
        final name = entry.key;
        if (name is! String || name.trim().isEmpty) {
          throw ConfigException(
            'models.custom names must be non-empty strings, got: $name',
          );
        }
        custom[name] = CustomModelDefinition.fromYaml(name, entry.value);
      }
    case final other:
      throw ConfigException('models.custom must be a map, got: $other');
  }
  return ModelsConfig(slots: slots, custom: custom);
}