()
| 304 | # --- Main CLI --- |
| 305 | |
| 306 | def main() -> int: |
| 307 | parser = argparse.ArgumentParser(description="Strict Hostlist Compressor") |
| 308 | parser.add_argument("input", nargs="?", default="-", help="Input file (stdin default)") |
| 309 | parser.add_argument("-o", "--output", help="Output file") |
| 310 | parser.add_argument("-i", "--in-place", action="store_true", help="Modify input file") |
| 311 | parser.add_argument("--filtered", help="Save filtered rules to file") |
| 312 | parser.add_argument("--include-wildcards", action="store_true", help="Keep wildcard rules") |
| 313 | |
| 314 | args = parser.parse_args() |
| 315 | |
| 316 | # 1. Input Reading |
| 317 | try: |
| 318 | if args.input == "-": |
| 319 | if args.in_place: |
| 320 | logger.error("Cannot use --in-place with stdin") |
| 321 | return 1 |
| 322 | content = sys.stdin.read().splitlines() |
| 323 | else: |
| 324 | content = Path(args.input).read_text(encoding="utf-8").splitlines() |
| 325 | except Exception as e: |
| 326 | logger.error(f"Read error: {e}") |
| 327 | return 1 |
| 328 | |
| 329 | # 2. Strict Validation (The JS Logic) |
| 330 | validator = RuleValidator(allow_ip=False) |
| 331 | valid_content, validation_filtered = validator.validate(content) |
| 332 | |
| 333 | logger.info(f"验证域名: {len(content)} -> {len(valid_content)} (移除 {len(validation_filtered)} 条)") |
| 334 | |
| 335 | # 3. Compression |
| 336 | compressed, compression_filtered = compress_rules(valid_content, args.include_wildcards) |
| 337 | |
| 338 | all_filtered = validation_filtered + compression_filtered |
| 339 | |
| 340 | logger.info(f"压缩完成 - {len(content)} -> {len(compressed)} (移除 {len(all_filtered)} 条)") |
| 341 | |
| 342 | # 4. Output |
| 343 | output_lines = "\n".join(compressed) + "\n" |
| 344 | |
| 345 | try: |
| 346 | if args.in_place and args.input != "-": |
| 347 | Path(args.input).write_text(output_lines, encoding="utf-8", newline="\n") |
| 348 | elif args.output: |
| 349 | Path(args.output).write_text(output_lines, encoding="utf-8", newline="\n") |
| 350 | else: |
| 351 | sys.stdout.write(output_lines) |
| 352 | |
| 353 | if args.filtered: |
| 354 | Path(args.filtered).write_text("\n".join(all_filtered) + "\n", encoding="utf-8", newline="\n") |
| 355 | |
| 356 | except Exception as e: |
| 357 | logger.error(f"Write error: {e}") |
| 358 | return 1 |
| 359 | |
| 360 | return 0 |
| 361 | |
| 362 | if __name__ == "__main__": |
| 363 | sys.exit(main()) |
no test coverage detected