|
5 | 5 | import sys |
6 | 6 | import os |
7 | 7 | import argparse |
8 | | -from typing import List, Tuple |
| 8 | +from typing import List, Tuple, Any |
9 | 9 |
|
10 | 10 | from .__version__ import __version__ |
11 | 11 | from .formatting import red |
|
15 | 15 | from .data_access import from_attr_chain |
16 | 16 |
|
17 | 17 |
|
| 18 | +def handle_version_flag(version_flag: bool) -> bool: |
| 19 | + """ |
| 20 | + Handle the version flag if present. |
| 21 | +
|
| 22 | + Args: |
| 23 | + version_flag: Whether the version flag was provided. |
| 24 | +
|
| 25 | + Returns: |
| 26 | + True if the version flag was handled, False otherwise. |
| 27 | + """ |
| 28 | + if version_flag: |
| 29 | + print(f"dotcat version {__version__}") |
| 30 | + return True |
| 31 | + return False |
| 32 | + |
| 33 | + |
| 34 | +def handle_special_case_arguments( |
| 35 | + filename: str, lookup_chain: str, args: List[str] |
| 36 | +) -> Tuple[str, str]: |
| 37 | + """ |
| 38 | + Handle special case where a single argument looks like a dotted-path. |
| 39 | +
|
| 40 | + Args: |
| 41 | + filename: The filename argument. |
| 42 | + lookup_chain: The dotted-path argument. |
| 43 | + args: The original command-line arguments. |
| 44 | +
|
| 45 | + Returns: |
| 46 | + The updated filename and lookup_chain. |
| 47 | + """ |
| 48 | + # Special case: If we have only one argument and it looks like a dotted-path, |
| 49 | + # treat it as the dotted-path rather than the file |
| 50 | + if filename is not None and lookup_chain is None and len(args) == 1: |
| 51 | + if is_likely_dot_path(filename): |
| 52 | + # Swap the arguments |
| 53 | + lookup_chain = filename |
| 54 | + filename = None |
| 55 | + |
| 56 | + return filename, lookup_chain |
| 57 | + |
| 58 | + |
| 59 | +def validate_required_arguments(filename: str, lookup_chain: str) -> None: |
| 60 | + """ |
| 61 | + Validate that the required arguments are present. |
| 62 | +
|
| 63 | + Args: |
| 64 | + filename: The filename argument. |
| 65 | + lookup_chain: The dotted-path argument. |
| 66 | +
|
| 67 | + Raises: |
| 68 | + SystemExit: If required arguments are missing. |
| 69 | + """ |
| 70 | + if lookup_chain is None or filename is None: |
| 71 | + if filename is not None and lookup_chain is None: |
| 72 | + # Case 1: File is provided but dotted-path is missing |
| 73 | + try: |
| 74 | + if os.path.exists(filename): |
| 75 | + # File exists, but dotted-path is missing |
| 76 | + print( |
| 77 | + f"Dotted-path required. Which value do you want me to look up in {filename}?" |
| 78 | + ) |
| 79 | + print(f"\n$dotcat {filename} {red('<dotted-path>')}") |
| 80 | + sys.exit(2) # Invalid usage |
| 81 | + except Exception: |
| 82 | + # If there's any error checking the file, fall back to general usage message |
| 83 | + pass |
| 84 | + elif filename is None and lookup_chain is not None: |
| 85 | + # Case 2: Dotted-path is provided but file is missing |
| 86 | + # Check if the argument looks like a dotted-path (contains dots) |
| 87 | + if "." in lookup_chain: |
| 88 | + # It looks like a dotted-path, so assume the file is missing |
| 89 | + print( |
| 90 | + f"File path required. Which file contains the value at {lookup_chain}?" |
| 91 | + ) |
| 92 | + print(f"\n$dotcat {red('<file>')} {lookup_chain}") |
| 93 | + sys.exit(2) # Invalid usage |
| 94 | + # Otherwise, it might be a file without an extension or something else, |
| 95 | + # so fall back to the general usage message |
| 96 | + |
| 97 | + # General usage message for other cases |
| 98 | + print(USAGE) # Display usage for invalid arguments |
| 99 | + sys.exit(2) # Invalid usage |
| 100 | + |
| 101 | + |
| 102 | +def process_file(filename: str) -> Any: |
| 103 | + """ |
| 104 | + Parse the file and handle any errors. |
| 105 | +
|
| 106 | + Args: |
| 107 | + filename: The file to parse. |
| 108 | +
|
| 109 | + Returns: |
| 110 | + The parsed data. |
| 111 | +
|
| 112 | + Raises: |
| 113 | + SystemExit: If there's an error parsing the file. |
| 114 | + """ |
| 115 | + try: |
| 116 | + return parse_file(filename) |
| 117 | + except FileNotFoundError as e: |
| 118 | + print(str(e)) |
| 119 | + sys.exit(3) # File not found |
| 120 | + except ValueError as e: |
| 121 | + if "File is empty" in str(e): |
| 122 | + print(f"{red('[ERROR]')} {filename}: File is empty") |
| 123 | + elif "Unable to parse file" in str(e): |
| 124 | + print(f"Unable to parse file: {red(filename)}") |
| 125 | + else: |
| 126 | + print(f"{str(e)}: {red(filename)}") |
| 127 | + sys.exit(4) # Parsing error |
| 128 | + |
| 129 | + |
| 130 | +def lookup_and_format_value( |
| 131 | + data: Any, lookup_chain: str, output_format: str, filename: str |
| 132 | +) -> None: |
| 133 | + """ |
| 134 | + Look up the value using the dotted-path and format the output. |
| 135 | +
|
| 136 | + Args: |
| 137 | + data: The parsed data. |
| 138 | + lookup_chain: The dotted-path to look up. |
| 139 | + output_format: The output format. |
| 140 | + filename: The filename (for error messages). |
| 141 | +
|
| 142 | + Raises: |
| 143 | + SystemExit: If the key is not found. |
| 144 | + """ |
| 145 | + try: |
| 146 | + value = from_attr_chain(data, lookup_chain) |
| 147 | + print(format_output(value, output_format)) |
| 148 | + except KeyError as e: |
| 149 | + key = e.args[0].split("'")[1] if "'" in e.args[0] else e.args[0] |
| 150 | + print(f"Key {red(key)} not found in {filename}") |
| 151 | + sys.exit(5) # Key not found |
| 152 | + |
| 153 | + |
18 | 154 | def parse_args(args: List[str]) -> Tuple[str, str, str, bool]: |
19 | 155 | """ |
20 | 156 | Returns the filename, dotted-path, output format, and version flag. |
@@ -91,74 +227,21 @@ def run(args: List[str] = None) -> None: |
91 | 227 | # validates arguments |
92 | 228 | filename, lookup_chain, output_format, version_flag = parse_args(args) |
93 | 229 |
|
94 | | - if version_flag: |
95 | | - print(f"dotcat version {__version__}") |
96 | | - return |
97 | | - |
98 | | - # Special case: If we have only one argument and it looks like a dotted-path, |
99 | | - # treat it as the dotted-path rather than the file |
100 | | - if filename is not None and lookup_chain is None and len(args) == 1: |
101 | | - if is_likely_dot_path(filename): |
102 | | - # Swap the arguments |
103 | | - lookup_chain = filename |
104 | | - filename = None |
105 | | - # Now filename is None and lookup_chain is not None |
| 230 | + # Handle version flag |
| 231 | + if handle_version_flag(version_flag): |
| 232 | + return # Exit early if version flag was handled |
106 | 233 |
|
107 | | - # Handle cases where one of the required arguments is missing |
108 | | - if lookup_chain is None or filename is None: |
109 | | - if filename is not None and lookup_chain is None: |
110 | | - # Case 1: File is provided but dotted-path is missing |
111 | | - try: |
112 | | - if os.path.exists(filename): |
113 | | - # File exists, but dotted-path is missing |
114 | | - print( |
115 | | - f"Dotted-path required. Which value do you want me to look up in {filename}?" |
116 | | - ) |
117 | | - print(f"\n$dotcat {filename} {red('<dotted-path>')}") |
118 | | - sys.exit(2) # Invalid usage |
119 | | - except Exception: |
120 | | - # If there's any error checking the file, fall back to general usage message |
121 | | - pass |
122 | | - elif filename is None and lookup_chain is not None: |
123 | | - # Case 2: Dotted-path is provided but file is missing |
124 | | - # Check if the argument looks like a dotted-path (contains dots) |
125 | | - if "." in lookup_chain: |
126 | | - # It looks like a dotted-path, so assume the file is missing |
127 | | - print( |
128 | | - f"File path required. Which file contains the value at {lookup_chain}?" |
129 | | - ) |
130 | | - print(f"\n$dotcat {red('<file>')} {lookup_chain}") |
131 | | - sys.exit(2) # Invalid usage |
132 | | - # Otherwise, it might be a file without an extension or something else, |
133 | | - # so fall back to the general usage message |
| 234 | + # Handle special case arguments |
| 235 | + filename, lookup_chain = handle_special_case_arguments(filename, lookup_chain, args) |
134 | 236 |
|
135 | | - # General usage message for other cases |
136 | | - print(USAGE) # Display usage for invalid arguments |
137 | | - sys.exit(2) # Invalid usage |
| 237 | + # Validate required arguments |
| 238 | + validate_required_arguments(filename, lookup_chain) |
138 | 239 |
|
139 | | - # gets the parsed data |
140 | | - try: |
141 | | - data = parse_file(filename) |
142 | | - except FileNotFoundError as e: |
143 | | - print(str(e)) |
144 | | - sys.exit(3) # File not found |
145 | | - except ValueError as e: |
146 | | - if "File is empty" in str(e): |
147 | | - print(f"{red('[ERROR]')} {filename}: File is empty") |
148 | | - elif "Unable to parse file" in str(e): |
149 | | - print(f"Unable to parse file: {red(filename)}") |
150 | | - else: |
151 | | - print(f"{str(e)}: {red(filename)}") |
152 | | - sys.exit(4) # Parsing error |
| 240 | + # Parse the file |
| 241 | + data = process_file(filename) |
153 | 242 |
|
154 | | - # get the value at the specified key |
155 | | - try: |
156 | | - value = from_attr_chain(data, lookup_chain) |
157 | | - print(format_output(value, output_format)) |
158 | | - except KeyError as e: |
159 | | - key = e.args[0].split("'")[1] if "'" in e.args[0] else e.args[0] |
160 | | - print(f"Key {red(key)} not found in {filename}") |
161 | | - sys.exit(5) # Key not found |
| 243 | + # Look up and format the value |
| 244 | + lookup_and_format_value(data, lookup_chain, output_format, filename) |
162 | 245 |
|
163 | 246 |
|
164 | 247 | def main() -> None: |
|
0 commit comments