Files
ecm_sync_system/sync_state_machine/config/persist_config.py
T
2026-04-08 09:28:14 +08:00

71 lines
2.4 KiB
Python

from typing import Any, Dict, Optional
from pydantic import BaseModel, ConfigDict, Field, model_validator
class PersistConfig(BaseModel):
model_config = ConfigDict(extra="forbid", validate_assignment=True)
backend: str = "sqlite"
db_path: Optional[str] = None
url: Optional[str] = None
backend_options: Dict[str, Any] = Field(default_factory=dict)
enable: bool = True
wipe_on_start: bool = False
@model_validator(mode="before")
@classmethod
def _validate_target(cls, data: Any) -> Any:
if not isinstance(data, dict):
return data
normalized = dict(data)
normalized["backend"] = (str(normalized.get("backend") or "sqlite").strip().lower() or "sqlite")
normalized["db_path"] = cls._normalize_optional_text(normalized.get("db_path"))
normalized["url"] = cls._normalize_optional_text(normalized.get("url"))
if not normalized["db_path"] and not normalized["url"]:
raise ValueError("persist requires either db_path or url")
if normalized["backend"] != "sqlite" and not normalized["url"]:
raise ValueError(f"persist.url is required for backend={normalized['backend']}")
return normalized
@staticmethod
def _normalize_optional_text(value: Optional[str]) -> Optional[str]:
if value is None:
return None
normalized = str(value).strip()
return normalized or None
def resolved_url(self) -> str:
if self.url:
return self.url
if self.backend == "sqlite" and self.db_path:
return f"sqlite+aiosqlite:///{self.db_path}"
raise ValueError(f"persist.url is required for backend={self.backend}")
def resolved_db_path(self) -> Optional[str]:
if self.db_path:
return self.db_path
if not self.url:
return None
from sqlalchemy.engine import make_url
parsed = make_url(self.url)
if parsed.get_backend_name() != "sqlite":
return None
database = parsed.database
if not database:
return None
return str(database)
def wipe_file_path(self) -> Optional[str]:
db_path = self.resolved_db_path()
if not db_path or db_path == ":memory:":
return None
return db_path
def display_target(self) -> str:
return self.url or self.db_path or "<unset>"