11import argparse
2+ import collections
3+ import re
24import sys
35import typing
46
@@ -12,13 +14,24 @@ def convert_parser(root_parser):
1214 convert .add_argument ("-s" , "--simple" , help = "Match by simple match" , required = False , action = "append" )
1315 convert .add_argument ("-d" , "--depth" , help = "Match by depth" , required = False , action = "append" )
1416
15- convert .add_argument ("-r" , "--replace" , help = "Replaces matched part by given string" , required = True )
17+ action = convert .add_mutually_exclusive_group (required = True )
18+ action .add_argument ("-r" , "--replace" , help = "Replaces matched part by given string" )
19+ action .add_argument ("-o" , "--shorten" , help = "Shortens matched data" , nargs = 2 , metavar = ("MAX_COUNT" , "TERMINATOR" ))
1620
1721
1822def extract_parser (root_parser ):
1923 extract = root_parser .add_parser ("extract" , help = "Passes only matched parts of JSON" )
2024 extract .add_argument ("-s" , "--simple" , help = "Match by simple match" , required = False , action = "append" )
2125 extract .add_argument ("-d" , "--depth" , help = "Match by depth" , required = False , action = "append" )
26+ extract .add_argument ("-b" , "--before" , help = "Will be printed before matched outputs" , required = False , default = "" )
27+ extract .add_argument ("-a" , "--after" , help = "Will be printed after matched outputs" , required = False , default = "" )
28+ extract .add_argument (
29+ "-S" ,
30+ "--separator" ,
31+ help = "Will be printed to separate matched outputs" ,
32+ required = False ,
33+ default = "" ,
34+ )
2235
2336
2437def filter_parser (root_parser ):
@@ -64,16 +77,23 @@ def trigger_parser(root_parser):
6477 default = [],
6578 )
6679
80+ trigger_parser .add_argument (
81+ "-s" ,
82+ "--struct" ,
83+ help = "Goes through a json and prints JSON structure at the end of processing." ,
84+ action = "store_true" ,
85+ )
86+
6787
6888def filter_strategy (options : argparse .Namespace , input_gen : typing .Generator [bytes , None , None ]):
6989 matcher : typing .Optional [streamson .Matcher ] = None
70- for simple in options .simple :
90+ for simple in options .simple or [] :
7191 if matcher :
7292 matcher |= streamson .SimpleMatcher (simple )
7393 else :
7494 matcher = streamson .SimpleMatcher (simple )
7595
76- for depth in options .depth :
96+ for depth in options .depth or [] :
7797 if matcher :
7898 matcher |= streamson .DepthMatcher (depth )
7999 else :
@@ -90,44 +110,68 @@ def filter_strategy(options: argparse.Namespace, input_gen: typing.Generator[byt
90110
91111def extract_strategy (options : argparse .Namespace , input_gen : typing .Generator [bytes , None , None ]):
92112 matcher : typing .Optional [streamson .Matcher ] = None
93- for simple in options .simple :
113+ for simple in options .simple or [] :
94114 if matcher :
95115 matcher |= streamson .SimpleMatcher (simple )
96116 else :
97117 matcher = streamson .SimpleMatcher (simple )
98118
99- for depth in options .depth :
119+ for depth in options .depth or [] :
100120 if matcher :
101121 matcher |= streamson .DepthMatcher (depth )
102122 else :
103123 matcher = streamson .DepthMatcher (depth )
104124
105125 if matcher :
126+ sys .stdout .write (options .before )
127+ first = True
106128 for _ , output in streamson .extract_iter (input_gen , matcher ):
129+ if not first :
130+ sys .stdout .write (options .separator )
131+ else :
132+ first = False
107133 sys .stdout .write (output )
134+ sys .stdout .write (options .after )
108135
109136
110137def convert_strategy (options : argparse .Namespace , input_gen : typing .Generator [bytes , None , None ]):
111138 matcher : typing .Optional [streamson .Matcher ] = None
112- for simple in options .simple :
139+ for simple in options .simple or [] :
113140 if matcher :
114141 matcher |= streamson .SimpleMatcher (simple )
115142 else :
116143 matcher = streamson .SimpleMatcher (simple )
117144
118- for depth in options .depth :
145+ for depth in options .depth or [] :
119146 if matcher :
120147 matcher |= streamson .DepthMatcher (depth )
121148 else :
122149 matcher = streamson .DepthMatcher (depth )
123150
124- replace_str = options .replace
151+ if options .shorten :
152+
153+ def shorten (
154+ path : typing .Optional [str ], matcher_idx : int , data : typing .Optional [bytes ]
155+ ) -> typing .Optional [bytes ]:
156+ length , end = options .shorten
157+ if data :
158+ return bytes (bytearray (data [: int (length ) + 1 ])) + end .encode ()
159+
160+ return None
161+
162+ converter = shorten
163+
164+ elif options .replace :
125165
126- def replace (path : typing .Optional [str ], matcher_idx : int , data : typing .Optional [bytes ]) -> typing .Optional [bytes ]:
127- return replace_str .encode ()
166+ def replace (
167+ path : typing .Optional [str ], matcher_idx : int , data : typing .Optional [bytes ]
168+ ) -> typing .Optional [bytes ]:
169+ return options .replace .encode ()
170+
171+ converter = replace
128172
129173 if matcher :
130- for output in streamson .convert_iter (input_gen , [replace ], matcher , False ):
174+ for output in streamson .convert_iter (input_gen , [converter ], matcher , False ):
131175 sys .stdout .write (output )
132176
133177
@@ -192,9 +236,28 @@ def handler(
192236 matcher = make_matcher (matcher_name , matcher_str )
193237 handler_matcher_combinations .append ((handler , matcher ))
194238
239+ if options .struct :
240+ counter : typing .Counter [str ] = collections .Counter ()
241+
242+ def handler (
243+ path : typing .Optional [str ], matcher_idx : int , data : typing .Optional [bytes ]
244+ ) -> typing .Optional [bytes ]:
245+
246+ key = "<root>" if not path else re .sub (r"\[[0-9\-,]*\]" , "[]" , path )
247+ counter [key ] += 1
248+ return None
249+
250+ matcher = streamson .AllMatcher ()
251+ handler_matcher_combinations .append ((handler , matcher ))
252+
195253 for output in streamson .trigger_iter (input_gen , handler_matcher_combinations , True ):
196254 pass
197255
256+ if options .struct :
257+ print ("JSON structure:" )
258+ for key in sorted (counter ):
259+ print (f" { key } : { counter [key ]} " )
260+
198261
199262def main ():
200263 version = pkg_resources .get_distribution ("streamson-python" ).version
0 commit comments