|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 | """CLI for search-query.""" |
| 3 | +from __future__ import annotations |
| 4 | + |
3 | 5 | import argparse |
4 | 6 | import sys |
5 | 7 | from pathlib import Path |
6 | 8 |
|
7 | 9 | import search_query.linter |
8 | 10 | import search_query.parser |
9 | 11 | from search_query import load_search_file |
| 12 | +from search_query.exception import QuerySyntaxError |
| 13 | + |
| 14 | +def _cmd_translate(args: argparse.Namespace) -> int: |
| 15 | + """Translate a query file from one platform/format to another.""" |
| 16 | + |
| 17 | + print(f"Reading query from {args.input_file}") |
| 18 | + input_path = Path(args.input_file) |
| 19 | + if input_path.suffix != ".json": |
| 20 | + print("Only .json search files are supported at the moment.", file=sys.stderr) |
| 21 | + return 2 |
| 22 | + |
| 23 | + search_file = load_search_file(args.input_file) |
| 24 | + try: |
| 25 | + query = search_query.parser.parse( |
| 26 | + search_file.search_string, |
| 27 | + platform=search_file.platform, |
| 28 | + field_general=search_file.field, |
| 29 | + ) |
| 30 | + except QuerySyntaxError as e: |
| 31 | + print(f"Fatal error parsing query.") |
| 32 | + return 1 |
| 33 | + |
| 34 | + print(f"Converting from {search_file.platform} to {args.target}") |
| 35 | + try: |
| 36 | + translated_query = query.translate(args.target) |
| 37 | + except Exception as e: |
| 38 | + print(f"Error translating query: {e}") |
| 39 | + return 1 |
| 40 | + |
| 41 | + converted_query = translated_query.to_string() |
| 42 | + search_file.search_string = converted_query |
| 43 | + search_file.platform = args.target |
| 44 | + |
| 45 | + print(f"Writing converted query to {args.output_file}") |
| 46 | + search_file.save(args.output_file) |
| 47 | + return 0 |
| 48 | + |
| 49 | + |
| 50 | +def _lint(args: argparse.Namespace) -> int: |
| 51 | + """Lint files.""" |
| 52 | + exit_code = 0 |
| 53 | + for file_path in args.files: |
| 54 | + search_file = load_search_file(file_path) |
| 55 | + try: |
| 56 | + result = search_query.linter.lint_file(search_file) |
| 57 | + if result: |
| 58 | + exit_code = 1 |
| 59 | + except QuerySyntaxError as e: |
| 60 | + print(f"Error linting file {file_path}: {e}") |
| 61 | + exit_code = 1 |
10 | 62 |
|
| 63 | + return exit_code |
11 | 64 |
|
12 | | -def translate() -> None: |
13 | | - """Main entrypoint for the query translation CLI""" |
14 | 65 |
|
| 66 | +def build_parser() -> argparse.ArgumentParser: |
15 | 67 | parser = argparse.ArgumentParser( |
16 | | - description="Convert search queries between formats" |
| 68 | + prog="search-query", |
| 69 | + description="Tools for working with search queries (linting, translation, etc.)", |
17 | 70 | ) |
18 | | - parser.add_argument( |
19 | | - "--from", |
20 | | - dest="source", |
21 | | - required=True, |
22 | | - help="Source query format (e.g., colrev_web_of_science)", |
| 71 | + subparsers = parser.add_subparsers(dest="command", metavar="<command>", required=True) |
| 72 | + |
| 73 | + # translate |
| 74 | + p_tr = subparsers.add_parser( |
| 75 | + "translate", |
| 76 | + help="Convert search queries between formats", |
| 77 | + description="Convert search queries between formats.", |
23 | 78 | ) |
24 | | - parser.add_argument( |
| 79 | + p_tr.add_argument( |
25 | 80 | "--input", |
26 | 81 | dest="input_file", |
27 | 82 | required=True, |
28 | | - help="Input file containing the query", |
| 83 | + help="Input .json file containing the query", |
29 | 84 | ) |
30 | | - parser.add_argument( |
| 85 | + p_tr.add_argument( |
31 | 86 | "--to", |
32 | 87 | dest="target", |
33 | 88 | required=True, |
34 | 89 | help="Target query format (e.g., colrev_pubmed)", |
35 | 90 | ) |
36 | | - parser.add_argument( |
| 91 | + p_tr.add_argument( |
37 | 92 | "--output", |
38 | 93 | dest="output_file", |
39 | 94 | required=True, |
40 | | - help="Output file for the converted query", |
| 95 | + help="Output file path for the converted query", |
41 | 96 | ) |
| 97 | + p_tr.set_defaults(func=_cmd_translate) |
42 | 98 |
|
43 | | - args = parser.parse_args() |
44 | | - |
45 | | - # Placeholder: Print what would happen |
46 | | - print(f"Converting from {args.source} to {args.target}") |
47 | | - print(f"Reading query from {args.input_file}") |
48 | | - print(f"Writing converted query to {args.output_file}") |
49 | | - print(f"Convert from {args.source} to {args.target}") |
50 | | - |
51 | | - if Path(args.input_file).suffix == ".json": |
52 | | - search_file = load_search_file(args.input_file) |
53 | | - query = search_query.parser.parse( |
54 | | - search_file.search_string, |
55 | | - platform=args.source, |
56 | | - field_general=search_file.field, |
57 | | - ) |
58 | | - |
59 | | - translated_query = query.translate(args.target) |
60 | | - converted_query = translated_query.to_string() |
61 | | - search_file.search_string = converted_query |
62 | | - search_file.save(args.output_file) |
| 99 | + # lint |
| 100 | + p_li = subparsers.add_parser( |
| 101 | + "lint", |
| 102 | + help="Lint query files", |
| 103 | + description="Lint one or more query files. Intended for standalone use or pre-commit.", |
| 104 | + ) |
| 105 | + p_li.add_argument( |
| 106 | + "files", |
| 107 | + nargs="+", |
| 108 | + help="File(s) to lint", |
| 109 | + ) |
| 110 | + p_li.set_defaults(func=_lint) |
63 | 111 |
|
64 | | - else: |
65 | | - raise NotImplementedError |
| 112 | + return parser |
66 | 113 |
|
67 | 114 |
|
68 | | -def lint() -> None: |
69 | | - """Main entrypoint for the query linter hook""" |
| 115 | +def main(argv: list[str] | None = None) -> int: |
| 116 | + parser = build_parser() |
| 117 | + args = parser.parse_args(argv) |
| 118 | + try: |
| 119 | + return args.func(args) |
| 120 | + except KeyboardInterrupt: |
| 121 | + return 130 |
70 | 122 |
|
71 | | - file_path = sys.argv[1] |
72 | 123 |
|
73 | | - raise SystemExit(search_query.linter.pre_commit_hook(file_path)) |
| 124 | +if __name__ == "__main__": |
| 125 | + raise SystemExit(main()) |
0 commit comments