Add formatters, utils, schemas and version
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
This commit is contained in:
114
configforge/formatters/env_handler.py
Normal file
114
configforge/formatters/env_handler.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
"""ENV format handler for ConfigForge."""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from configforge.exceptions import ConversionError
|
||||||
|
|
||||||
|
|
||||||
|
class ENVHandler:
|
||||||
|
"""Handler for ENV (environment variable) configuration files."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def loads(content: str) -> Dict[str, Any]:
|
||||||
|
"""Parse ENV file content."""
|
||||||
|
result: Dict[str, Any] = {}
|
||||||
|
current_section: Optional[str] = None
|
||||||
|
|
||||||
|
for line_num, line in enumerate(content.splitlines(), 1):
|
||||||
|
stripped = line.strip()
|
||||||
|
|
||||||
|
if not stripped or stripped.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stripped.startswith("[") and stripped.endswith("]"):
|
||||||
|
current_section = stripped[1:-1].strip()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "=" in stripped:
|
||||||
|
key, _, value = stripped.partition("=")
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip()
|
||||||
|
|
||||||
|
if not key:
|
||||||
|
continue
|
||||||
|
|
||||||
|
value = ENVHandler._parse_value(value)
|
||||||
|
|
||||||
|
if current_section:
|
||||||
|
if current_section not in result:
|
||||||
|
result[current_section] = {}
|
||||||
|
result[current_section][key] = value
|
||||||
|
else:
|
||||||
|
result[key] = value
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_value(value: str) -> Any:
|
||||||
|
"""Parse a value from ENV format."""
|
||||||
|
value = value.strip('"').strip("'")
|
||||||
|
|
||||||
|
if value.lower() == "true":
|
||||||
|
return True
|
||||||
|
if value.lower() == "false":
|
||||||
|
return False
|
||||||
|
if value.lower() == "null":
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
if "." in value:
|
||||||
|
return float(value)
|
||||||
|
return int(value)
|
||||||
|
except ValueError:
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dumps(data: Dict[str, Any], section_name: Optional[str] = None) -> str:
|
||||||
|
"""Serialize dictionary to ENV format."""
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
if section_name:
|
||||||
|
lines.append(f"[{section_name}]")
|
||||||
|
|
||||||
|
for key, value in data.items():
|
||||||
|
lines.append(f"{key}={ENVHandler._format_value(value)}")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_value(value: Any) -> str:
|
||||||
|
"""Format a value for ENV output."""
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return str(value).lower()
|
||||||
|
if value is None:
|
||||||
|
return ""
|
||||||
|
if isinstance(value, str):
|
||||||
|
if " " in value or '"' in value or "'" in value:
|
||||||
|
return f'"{value}"'
|
||||||
|
return value
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read(filepath: str) -> Dict[str, Any]:
|
||||||
|
"""Read and parse ENV file."""
|
||||||
|
try:
|
||||||
|
with open(filepath, "r", encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
return ENVHandler.loads(content)
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise ConversionError(f"File not found: {filepath}")
|
||||||
|
except PermissionError:
|
||||||
|
raise ConversionError(f"Permission denied: {filepath}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def write(filepath: str, data: Dict[str, Any], section_name: Optional[str] = None) -> None:
|
||||||
|
"""Write data to ENV file."""
|
||||||
|
try:
|
||||||
|
content = ENVHandler.dumps(data, section_name)
|
||||||
|
with open(filepath, "w", encoding="utf-8") as f:
|
||||||
|
f.write(content)
|
||||||
|
except FileNotFoundError:
|
||||||
|
raise ConversionError(f"Directory not found for: {filepath}")
|
||||||
|
except PermissionError:
|
||||||
|
raise ConversionError(f"Permission denied to write: {filepath}")
|
||||||
Reference in New Issue
Block a user