Python · 5454 bytes Raw Blame History
1 """
2 Data-driven argument definitions for sultree.
3
4 This module contains all command-line argument definitions in a clean,
5 table-driven format to eliminate repetitive argparse code.
6 """
7
8 from dataclasses import dataclass
9 from typing import List, Optional, Callable, Any, Dict
10
11 from .validators import (
12 validate_depth,
13 validate_file_limit,
14 validate_pattern,
15 validate_selinux_pattern,
16 validate_path
17 )
18
19
20 @dataclass
21 class ArgumentDefinition:
22 """Defines a command-line argument."""
23 short_name: Optional[str] = None
24 long_name: str = ''
25 action: str = 'store'
26 type_func: Optional[Callable] = None
27 dest: Optional[str] = None
28 metavar: Optional[str] = None
29 help_text: str = ''
30 default: Any = None
31 nargs: Optional[str] = None
32
33
34 # Data-driven argument definitions table
35 ARGUMENT_DEFINITIONS = [
36 # Positional arguments
37 ArgumentDefinition(
38 long_name='directories',
39 action='store',
40 nargs='*',
41 type_func=validate_path,
42 help_text='directories to tree (default: current directory)',
43 default=None # Will be set to [Path('.')] in parsing
44 ),
45
46 # Basic listing options
47 ArgumentDefinition(
48 short_name='-a',
49 long_name='--all',
50 action='store_true',
51 help_text='show all files (including hidden files)'
52 ),
53 ArgumentDefinition(
54 short_name='-d',
55 long_name='--dirs-only',
56 action='store_true',
57 help_text='list directories only'
58 ),
59 ArgumentDefinition(
60 short_name='-l',
61 long_name='--follow-links',
62 action='store_true',
63 help_text='follow symbolic links like directories'
64 ),
65 ArgumentDefinition(
66 short_name='-f',
67 long_name='--full-path',
68 action='store_true',
69 help_text='print full path prefix for each file'
70 ),
71 ArgumentDefinition(
72 short_name='-x',
73 long_name='--one-file-system',
74 action='store_true',
75 help_text='stay on current filesystem only'
76 ),
77
78 # Depth control
79 ArgumentDefinition(
80 short_name='-L',
81 long_name='--level',
82 type_func=validate_depth,
83 metavar='level',
84 help_text='descend only level directories deep'
85 ),
86
87 # Pattern filtering
88 ArgumentDefinition(
89 short_name='-P',
90 long_name='--pattern',
91 type_func=validate_pattern,
92 action='append',
93 dest='include_patterns',
94 metavar='pattern',
95 help_text='list only files that match the pattern'
96 ),
97 ArgumentDefinition(
98 short_name='-I',
99 long_name='--ignore',
100 type_func=validate_pattern,
101 action='append',
102 dest='exclude_patterns',
103 metavar='pattern',
104 help_text='do not list files that match the pattern'
105 ),
106 ArgumentDefinition(
107 long_name='--match-dirs',
108 action='store_true',
109 help_text='include directory names in pattern matching'
110 ),
111 ArgumentDefinition(
112 long_name='--ignore-case',
113 action='store_true',
114 help_text='ignore case when pattern matching'
115 ),
116
117 # File limits
118 ArgumentDefinition(
119 long_name='--filelimit',
120 type_func=validate_file_limit,
121 metavar='N',
122 help_text='do not descend dirs with more than N files'
123 ),
124
125 # SELinux options
126 ArgumentDefinition(
127 short_name='-S',
128 long_name='--selinux',
129 type_func=validate_selinux_pattern,
130 action='append',
131 dest='selinux_patterns',
132 metavar='pattern',
133 help_text='show only files matching SELinux pattern (can be used multiple times)'
134 ),
135
136 # Output options
137 ArgumentDefinition(
138 long_name='--no-report',
139 action='store_true',
140 help_text='turn off file/directory count at end of tree listing'
141 ),
142 ArgumentDefinition(
143 long_name='--version',
144 action='version',
145 help_text='show version information',
146 # Note: version string will be set dynamically
147 ),
148 ]
149
150
151 def build_argument_kwargs(arg_def: ArgumentDefinition) -> Dict[str, Any]:
152 """
153 Convert ArgumentDefinition to kwargs for argparse.add_argument().
154
155 Args:
156 arg_def: Argument definition
157
158 Returns:
159 Dictionary of kwargs for add_argument()
160 """
161 kwargs = {}
162
163 # Action
164 if arg_def.action:
165 kwargs['action'] = arg_def.action
166
167 # Type function
168 if arg_def.type_func:
169 kwargs['type'] = arg_def.type_func
170
171 # Destination
172 if arg_def.dest:
173 kwargs['dest'] = arg_def.dest
174
175 # Metavar
176 if arg_def.metavar:
177 kwargs['metavar'] = arg_def.metavar
178
179 # Help text
180 if arg_def.help_text:
181 kwargs['help'] = arg_def.help_text
182
183 # Default value
184 if arg_def.default is not None:
185 kwargs['default'] = arg_def.default
186
187 # Nargs
188 if arg_def.nargs:
189 kwargs['nargs'] = arg_def.nargs
190
191 return kwargs
192
193
194 def get_argument_names(arg_def: ArgumentDefinition) -> List[str]:
195 """
196 Get the argument names (short and long) for an ArgumentDefinition.
197
198 Args:
199 arg_def: Argument definition
200
201 Returns:
202 List of argument names
203 """
204 names = []
205 if arg_def.short_name:
206 names.append(arg_def.short_name)
207 if arg_def.long_name:
208 names.append(arg_def.long_name)
209 return names