88 lines
3.4 KiB
Python
88 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import configparser
|
|
from pathlib import Path
|
|
|
|
from .gcode import normalize_newlines
|
|
from .models import DEFAULT_INSERT_BEFORE_MARKER, GcodePatchConfig, GcodePatchRule
|
|
from .paths import default_patch_config_path
|
|
|
|
|
|
def parse_bool(value: str | None, default: bool = True) -> bool:
|
|
if value is None:
|
|
return default
|
|
return value.strip().lower() in {"1", "true", "yes", "on", "enabled"}
|
|
|
|
|
|
def parse_patch_config(path: Path | None = None) -> GcodePatchConfig:
|
|
config_path = path or default_patch_config_path()
|
|
if not config_path.exists():
|
|
return GcodePatchConfig(rules=())
|
|
parser = configparser.ConfigParser(interpolation=None, strict=False)
|
|
parser.optionxform = str
|
|
parser.read(config_path, encoding="utf-8-sig")
|
|
|
|
insert_before_marker = DEFAULT_INSERT_BEFORE_MARKER
|
|
if parser.has_section("swap"):
|
|
insert_before_marker = parser.get("swap", "insert_before_marker", fallback=insert_before_marker).strip().strip('"')
|
|
|
|
rules: list[GcodePatchRule] = []
|
|
for section in parser.sections():
|
|
if section.lower().startswith("patch."):
|
|
enabled = parse_bool(parser.get(section, "enabled", fallback="true"), True)
|
|
find = parser.get(section, "find", fallback="").strip().strip('"')
|
|
replace = parser.get(section, "replace", fallback="").strip().strip('"')
|
|
flag = parser.get(section, "flag", fallback="").strip().strip('"')
|
|
max_count_text = parser.get(section, "max_count", fallback="1").strip()
|
|
try:
|
|
max_count = max(0, int(max_count_text))
|
|
except ValueError:
|
|
max_count = 1
|
|
if find and enabled:
|
|
rules.append(GcodePatchRule(section.split(".", 1)[1], find, replace, flag, enabled, max_count))
|
|
|
|
if parser.has_section("Gcode"):
|
|
gcode = parser["Gcode"]
|
|
base_names = sorted(
|
|
key[:-5]
|
|
for key in gcode.keys()
|
|
if key.startswith("Postion") and key.endswith("_flag")
|
|
)
|
|
for base in base_names:
|
|
find = gcode.get(base, "").strip().strip('"')
|
|
replace = gcode.get(f"{base}_edit", "").strip().strip('"')
|
|
flag = gcode.get(f"{base}_flag", "").strip().strip('"')
|
|
if find and replace:
|
|
rules.append(GcodePatchRule(base, find, replace, flag, True, 1))
|
|
marker = gcode.get("FinishFlag", "").strip().strip('"')
|
|
if marker:
|
|
insert_before_marker = marker
|
|
return GcodePatchConfig(tuple(rules), insert_before_marker)
|
|
|
|
|
|
def apply_patch_rule(text: str, rule: GcodePatchRule) -> str:
|
|
if not rule.enabled or not rule.find or rule.max_count == 0:
|
|
return text
|
|
lines = normalize_newlines(text).split("\n")
|
|
start_index = 0
|
|
if rule.flag:
|
|
for index, line in enumerate(lines):
|
|
if line.strip() == rule.flag or rule.flag in line:
|
|
start_index = index + 1
|
|
break
|
|
replacements = 0
|
|
for index in range(start_index, len(lines)):
|
|
if lines[index].strip() == rule.find or lines[index] == rule.find:
|
|
lines[index] = rule.replace
|
|
replacements += 1
|
|
if replacements >= rule.max_count:
|
|
break
|
|
return "\n".join(lines)
|
|
|
|
|
|
def apply_gcode_patches(text: str, config: GcodePatchConfig) -> str:
|
|
patched = normalize_newlines(text)
|
|
for rule in config.rules:
|
|
patched = apply_patch_rule(patched, rule)
|
|
return patched
|