65 lines
1.8 KiB
Python
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,
|
|
)
|