from __future__ import annotations from pathlib import Path from typing import Any class GrepLogExecutor: def execute(self, params: dict[str, Any]) -> tuple[bool, str, dict[str, Any], dict[str, Any]]: path = Path(str(params["path"])) keyword = str(params["keyword"]) limit = int(params.get("limit", 100)) encoding = str(params.get("encoding", "utf-8")) case_sensitive = bool(params.get("case_sensitive", False)) if not path.exists(): return False, f"log file not found: {path}", {}, {} keyword_cmp = keyword if case_sensitive else keyword.lower() matches: list[dict[str, Any]] = [] with path.open("r", encoding=encoding, errors="ignore") as handle: for line_number, line in enumerate(handle, start=1): text = line.rstrip("\n") text_cmp = text if case_sensitive else text.lower() if keyword_cmp in text_cmp: matches.append({"line_number": line_number, "content": text}) if len(matches) >= limit: break success = len(matches) > 0 message = "keyword matched" if success else "keyword not found" return ( success, message, { "path": str(path), "keyword": keyword, "matched_count": len(matches), }, { "matches": matches, }, )