nut/chipnut/config.py
2026-04-04 12:02:15 +02:00

65 lines
1.8 KiB
Python

from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, Optional
from .utils import read_yaml
class ConfigError(ValueError):
"""Raised when Nutfile contents are invalid."""
@dataclass(frozen=True)
class BuildConfig:
name: str
build_folder: str
entry: str
@property
def buildFolder(self) -> str:
# Compatibility alias for existing camelCase references.
return self.build_folder
@dataclass(frozen=True)
class Config:
path: Path
raw: Dict[str, Any]
build: BuildConfig
@property
def buildFolder(self) -> str:
# Compatibility alias for older call-sites.
return self.build.build_folder
def _ensure_dict(value: Any, context: str) -> Dict[str, Any]:
if not isinstance(value, dict):
raise ConfigError(f"{context} must be a mapping")
return value
def _read_string(mapping: Dict[str, Any], key: str, default: str) -> str:
value = mapping.get(key, default)
if not isinstance(value, str) or not value.strip():
raise ConfigError(f"build.{key} must be a non-empty string")
return value
def load_config(path: str = "Nutfile") -> Config:
config_path = Path(path)
raw_data: Optional[Dict[str, Any]] = read_yaml(str(config_path))
if raw_data is None:
raw_data = {}
raw_dict = _ensure_dict(raw_data, "Nutfile root")
build_section = raw_dict.get("build", {})
build_dict = _ensure_dict(build_section, "build section")
build = BuildConfig(
name=_read_string(build_dict, "name", "Example Build"),
build_folder=_read_string(build_dict, "buildFolder", "build"),
entry=_read_string(build_dict, "entry", "src/main.nut"),
)
return Config(
path=config_path,
raw=raw_dict,
build=build,
)