openapi-sourcetools 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 205e65ab29d8392a612d241cf0afc0197aba2884c076a7cbeb6523399520e573
4
- data.tar.gz: f3cc5e36a89cc174c260a695c5e7d21914660ad8c3813e15d78b03409a964373
3
+ metadata.gz: 23e0cd1aefa69559d35171e477d8b749ec61251b67c51040b1130ce48fb57725
4
+ data.tar.gz: 1fa4a40b88aa863d3eed153db455aed0bd4d9b9b88a4aa9ca8f92874e07613f5
5
5
  SHA512:
6
- metadata.gz: ec20a103c6150baccb9d8ff315bdf3849993fd977ef4738c251f77aefc07b9dbc4f6521e55b14445d07df04dde2cf307bd0d025a33d124b272fea0052f69a956
7
- data.tar.gz: 6c763b05cf087a982a53106ca9f889c221cea2788c57fddb1e120b1287f15a8a7d4e42711e0c24c1b87f79187b38f27cf431de918502609317d35deedfe0d170
6
+ metadata.gz: c56924c74979a016aeb8789bd7071f0d54e3c1de746c7bd92d9166cf0a03001f76f5b7b772d128f3e278bbc7835f52f7f139e4c71fe814032c49a252c24f6ece
7
+ data.tar.gz: f7c87b497a0c154d99b7cbd33da06cce91e9a7aa8d6b8d72bc6b64f86a50fa1f93a93a0e96f922dab1fdb0c189989cd197f48d12ee5399254fe78120e286ddd4
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2019 Ismo Kärkkäinen
1
+ Copyright (c) 2021-2024 Ismo Kärkkäinen
2
2
 
3
3
  The Universal Permissive License (UPL), Version 1.0
4
4
 
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/apiobjects'
8
+ require_relative '../lib/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def replace_headers(obj, components)
14
+ return unless obj.is_a?(Hash)
15
+ obj.each do |k, v|
16
+ if k == 'headers'
17
+ v.keys.sort!.each do |name|
18
+ r = v[name]
19
+ next unless r.is_a?(Hash) # Could complain.
20
+ next if r.key?('$ref')
21
+ v[name] = { '$ref' => components.reference(r) }
22
+ end
23
+ else
24
+ replace_headers(v, components)
25
+ end
26
+ end
27
+ end
28
+
29
+ def main
30
+ input_name = nil
31
+ output_name = nil
32
+ path = %w[components headers]
33
+ components = Components.new(path, 'Header')
34
+
35
+ ENV['POSIXLY_CORRECT'] = '1'
36
+ parser = OptionParser.new do |opts|
37
+ opts.summary_indent = ' '
38
+ opts.summary_width = 26
39
+ opts.banner = 'Usage: openapi-addheaders [options]'
40
+ opts.separator ''
41
+ opts.separator 'Options:'
42
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
43
+ input_name = f
44
+ end
45
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
46
+ output_name = f
47
+ end
48
+ components.add_options(opts)
49
+ opts.on('-h', '--help', 'Print this help and exit.') do
50
+ $stdout.puts %(#{opts}
51
+
52
+ Loads API document in OpenAPI format and moves headers under components and
53
+ replaces the original with reference.
54
+
55
+ #{components.help}
56
+ )
57
+ exit 0
58
+ end
59
+ end
60
+ parser.parse!
61
+
62
+ doc = load_source(input_name)
63
+ return 2 if doc.nil?
64
+
65
+ components.items = doc.dig(*path) || {}
66
+ replace_headers(doc.dig('components', 'responses') || {}, components)
67
+ replace_headers(doc.fetch('paths', {}), components)
68
+ bury(doc, path, components.items) unless components.items.empty?
69
+
70
+ dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
71
+ end
72
+
73
+ exit(main) if (defined? $unit_test).nil?
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/apiobjects'
8
+ require_relative '../lib/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def replace_responses(obj, components)
14
+ return unless obj.is_a?(Hash)
15
+ obj.each do |k, v|
16
+ if k == 'responses'
17
+ v.keys.sort!.each do |code|
18
+ r = v[code]
19
+ next unless r.is_a?(Hash) # Could complain.
20
+ next if r.key?('$ref')
21
+ v[code] = { '$ref' => components.reference(r) }
22
+ end
23
+ else
24
+ replace_responses(v, components)
25
+ end
26
+ end
27
+ end
28
+
29
+ def main
30
+ input_name = nil
31
+ output_name = nil
32
+ path = %w[components responses]
33
+ components = Components.new(path, 'Response')
34
+
35
+ ENV['POSIXLY_CORRECT'] = '1'
36
+ parser = OptionParser.new do |opts|
37
+ opts.summary_indent = ' '
38
+ opts.summary_width = 26
39
+ opts.banner = 'Usage: openapi-addresponses [options]'
40
+ opts.separator ''
41
+ opts.separator 'Options:'
42
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
43
+ input_name = f
44
+ end
45
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
46
+ output_name = f
47
+ end
48
+ components.add_options(opts)
49
+ opts.on('-h', '--help', 'Print this help and exit.') do
50
+ $stdout.puts %(#{opts}
51
+
52
+ Loads API document in OpenAPI format and moves responses under components and
53
+ replaces the original with reference.
54
+
55
+ #{components.help}
56
+ )
57
+ exit 0
58
+ end
59
+ end
60
+ parser.parse!
61
+
62
+ doc = load_source(input_name)
63
+ return 2 if doc.nil?
64
+
65
+ components.items = doc.dig(*path) || {}
66
+ replace_responses(doc.fetch('paths', {}), components)
67
+ bury(doc, path, components.items) unless components.items.empty?
68
+
69
+ dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
70
+ end
71
+
72
+ exit(main) if (defined? $unit_test).nil?
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/apiobjects'
8
+ require_relative '../lib/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def remove_subitem(obj, path)
14
+ return obj.delete(path.first) || {} if path.size == 1
15
+ parent = obj.dig(*path.take(path.size - 1))
16
+ return {} if parent.nil?
17
+ parent.delete(path.last) || {}
18
+ end
19
+
20
+ def replace_inlines(obj, components, top_level = false)
21
+ if obj.is_a?(Array)
22
+ obj.each do |o|
23
+ return false unless replace_inlines(o, components)
24
+ end
25
+ return true
26
+ end
27
+ return true unless obj.is_a?(Hash)
28
+ return true if obj.key?('$ref')
29
+ # Prior return would be the place to get rid of other keys if so desired.
30
+ # Is inlined, process parts recursively.
31
+ items = obj['allOf']
32
+ items = obj['anyOf'] if items.nil?
33
+ items = obj['oneOf'] if items.nil?
34
+ # All above need special treatment in code generation templates.
35
+ return replace_inlines(items, components) unless items.nil?
36
+ t = obj['type']
37
+ if t.nil? # Some kind of intermediate-level object.
38
+ (obj.keys.sort! { |a, b| a.to_s <=> b.to_s }).each do |key|
39
+ return false unless replace_inlines(obj[key], components)
40
+ end
41
+ return true
42
+ end
43
+ case t
44
+ when 'array'
45
+ return false unless replace_inlines(obj['items'], components)
46
+ when 'object'
47
+ props = obj.fetch('properties', {})
48
+ props.keys.sort!.each do |name|
49
+ return false unless replace_inlines(props[name], components)
50
+ end
51
+ end
52
+ obj.replace({ '$ref' => components.reference(obj) }) unless top_level
53
+ true
54
+ end
55
+
56
+ def main
57
+ input_name = nil
58
+ output_name = nil
59
+ path = %w[components schemas]
60
+ components = Components.new(path, 'Schema')
61
+
62
+ ENV['POSIXLY_CORRECT'] = '1'
63
+ parser = OptionParser.new do |opts|
64
+ opts.summary_indent = ' '
65
+ opts.summary_width = 26
66
+ opts.banner = 'Usage: openapi-addschemas [options]'
67
+ opts.separator ''
68
+ opts.separator 'Options:'
69
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
70
+ input_name = f
71
+ end
72
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
73
+ output_name = f
74
+ end
75
+ components.add_options(opts)
76
+ opts.on('-h', '--help', 'Print this help and exit.') do
77
+ $stdout.puts %(#{opts}
78
+
79
+ Loads API document in OpenAPI format and adds a schema for each inline type.
80
+
81
+ #{components.help}
82
+ )
83
+ exit 0
84
+ end
85
+ end
86
+ parser.parse!
87
+
88
+ doc = load_source(input_name)
89
+ return 2 if doc.nil?
90
+
91
+ # Find schema object and remove it temporarily to prevent being
92
+ # processed again.
93
+ components.items = remove_subitem(doc, path)
94
+ # Get rid of inlined types within existing schemas first.
95
+ components.items.keys.sort!.each do |k|
96
+ return 4 unless replace_inlines(components.items[k], components, true)
97
+ end
98
+ return 4 unless replace_inlines(doc, components)
99
+ bury(doc, path, components.items) unless components.items.empty?
100
+
101
+ dump_result(output_name, YAML.dump(doc, line_width: 1_000_000), 3)
102
+ end
103
+
104
+ exit(main) if (defined? $unit_test).nil?
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/apiobjects'
8
+ require_relative '../lib/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def typesame(a, b, ignored_keys)
14
+ return same(a, b, ignored_keys) unless a['type'] == 'object'
15
+ return same(a, b, ignored_keys) unless b['type'] == 'object'
16
+ # For each property in a, find out unused mathing property in b that is
17
+ # also either required or not.
18
+ # Requiring property affects e.g. C++ by allowing direct membership or
19
+ # when optional, a pointer that can have nullptr value. Or class instance
20
+ # or null in TypeScript so I consider required to affect type.
21
+ pa = a.fetch('properties', {})
22
+ ra = a.fetch('required', [])
23
+ pb = b.fetch('properties', {}).merge
24
+ rb = b.fetch('required', [])
25
+ pa.each do |aname, aspec|
26
+ bnames = pb.keys.sort!
27
+ if ra.include?(aname)
28
+ bnames.select! { |n| rb.include?(n) }
29
+ else
30
+ bnames.reject! { |n| rb.include?(n) }
31
+ end
32
+ matched = false
33
+ bnames.each do |bname|
34
+ bspec = pb[bname]
35
+ # Intended use is after openapi-addschemas so same should work as well.
36
+ if typesame(aspec, bspec, ignored_keys)
37
+ pb.delete(bname)
38
+ matched = true
39
+ break
40
+ end
41
+ end
42
+ return false unless matched
43
+ end
44
+ pb.empty? # All matched and nothing left in pb.
45
+ end
46
+
47
+ def keys2array(keys)
48
+ keys.to_h { |k| [ k, [] ] }
49
+ end
50
+
51
+ def gather_equivalencies(components)
52
+ order = components.items.keys.sort!
53
+ result = keys2array(order)
54
+ (order.size - 1).times do |k|
55
+ item = components.items[order[k]]
56
+ ((k + 1)...order.size).each do |n|
57
+ if same(item, components.items[order[n]], components.ignored_keys)
58
+ result[order[k]].push(order[n])
59
+ result[order[n]].push(order[k])
60
+ end
61
+ end
62
+ end
63
+ result.delete_if { |_k, v| v.empty? }
64
+ end
65
+
66
+ def gather_typematches(components)
67
+ order = components.items.keys.sort!
68
+ result = keys2array(order)
69
+ (order.size - 1).times do |k|
70
+ item = components.items[order[k]]
71
+ ((k + 1)...order.size).each do |n|
72
+ if typesame(item, components.items[order[n]], components.ignored_keys)
73
+ result[order[k]].push(order[n])
74
+ result[order[n]].push(order[k])
75
+ end
76
+ end
77
+ end
78
+ result.delete_if { |_k, v| v.empty? }
79
+ end
80
+
81
+ def gather_refs(obj, prefix, past, refs)
82
+ if obj.is_a?(Hash)
83
+ obj.each do |k, v|
84
+ if v.is_a?(String) && v.start_with?(prefix)
85
+ p = past.join('/')
86
+ if refs.key?(v)
87
+ refs[v].push(p)
88
+ else
89
+ refs[v] = [ p ]
90
+ end
91
+ else
92
+ past.push(k)
93
+ gather_refs(v, prefix, past, refs)
94
+ past.pop
95
+ end
96
+ end
97
+ elsif obj.is_a?(Array)
98
+ obj.size.times do |n|
99
+ past.push(n)
100
+ gather_refs(obj[n], prefix, past, refs)
101
+ past.pop
102
+ end
103
+ end
104
+ end
105
+
106
+ def gather_references(doc, prefix)
107
+ refs = {}
108
+ gather_refs(doc, prefix, [], refs)
109
+ out = {}
110
+ refs.keys.sort!.each do |k|
111
+ r = refs[k]
112
+ next if r.empty?
113
+ out[k] = r.sort!
114
+ end
115
+ out
116
+ end
117
+
118
+ def drop_untouched(refs, report, prefix)
119
+ touched = Set.new
120
+ %w[equivalent typematch].each do |s|
121
+ r = report[s]
122
+ next if r.nil?
123
+ r.keys.each { |k| touched.add("#{prefix}#{k}") }
124
+ end
125
+ refs.delete_if { |k, _v| !touched.member?(k) }
126
+ end
127
+
128
+ def count_refs(refs)
129
+ counts = {}
130
+ refs.each { |k, v| counts[k] = v.size }
131
+ counts
132
+ end
133
+
134
+ def main
135
+ input_name = nil
136
+ output_name = nil
137
+ path = %w[components schemas]
138
+ components = Components.new(path, '')
139
+ equivalent = true
140
+ typematch = false
141
+ references = false
142
+ count = true
143
+ keep = false
144
+
145
+ ENV['POSIXLY_CORRECT'] = '1'
146
+ parser = OptionParser.new do |opts|
147
+ opts.summary_indent = ' '
148
+ opts.summary_width = 26
149
+ opts.banner = 'Usage: openapi-checkschemas [options]'
150
+ opts.separator ''
151
+ opts.separator 'Options:'
152
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
153
+ input_name = f
154
+ end
155
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout .') do |f|
156
+ output_name = f
157
+ end
158
+ opts.on('-e', '--[no-]equivalent', "Report equivalent schemas, default #{equivalent}") do |b|
159
+ equivalent = b
160
+ end
161
+ opts.on('-t', '--[no-]typematch', "Report typematch schemas, default #{typematch}") do |b|
162
+ typematch = b
163
+ end
164
+ opts.on('-r', '--[no-]reference', "Report schema references, default #{references}") do |b|
165
+ references = b
166
+ end
167
+ opts.on('-c', '--[no-]count', "Report schema reference counts, default #{count}") do |b|
168
+ count = b
169
+ end
170
+ opts.on('-k', '--[no-]keep', "Keep all schema references/counts, default #{keep}") do |b|
171
+ keep = b
172
+ end
173
+ components.add_options(opts)
174
+ opts.on('-h', '--help', 'Print this help and exit.') do
175
+ $stdout.puts %(#{opts}
176
+
177
+ Loads API document in OpenAPI format and checks if any schemas are duplicates
178
+ of another and to what degree. Equivalency may be an exact match, or property
179
+ names differ but types match.
180
+
181
+ #{components.help}
182
+
183
+ Search is performed only at top schema level. References to equivalent types
184
+ are not considered equivalent when references themselves are not equivalent.
185
+ Any allOf, anyOf, oneOf checks merely check the refernces. Hence two different
186
+ allOf schemas may in practice result in equivalent types and that is not
187
+ detected.
188
+
189
+ Implicit expectation is the openapi-addschemas has been used to process the
190
+ input, as inlined types in requests for example are ignored.
191
+ )
192
+ exit 0
193
+ end
194
+ end
195
+ parser.parse!
196
+
197
+ doc = load_source(input_name)
198
+ return 2 if doc.nil?
199
+
200
+ components.items = doc.dig(*path) || {}
201
+ report = {}
202
+ report['equivalent'] = gather_equivalencies(components) if equivalent
203
+ report['typematch'] = gather_typematches(components) if typematch
204
+ if references || count
205
+ refs = gather_references(doc, components.path)
206
+ if equivalent || typematch # There is something to limit references to.
207
+ refs = drop_untouched(refs, report, components.path) unless keep
208
+ end
209
+ report['reference'] = refs if references
210
+ report['count'] = count_refs(refs) if count
211
+ end
212
+ # Filter reference counts that are not for type in equivalent or typematch.
213
+ # Could check for pretty-printed output format here.
214
+ dump_result(output_name, YAML.dump(report, line_width: 80), 3)
215
+ end
216
+
217
+ exit(main) if (defined? $unit_test).nil?
@@ -1,105 +1,78 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Copyright © 2021 Ismo Kärkkäinen
4
+ # Copyright © 2021-2024 Ismo Kärkkäinen
5
5
  # Licensed under Universal Permissive License. See LICENSE.txt.
6
6
 
7
- require_relative '../lib/common.rb'
7
+ require_relative '../lib/common'
8
8
  require 'optparse'
9
9
  require 'yaml'
10
- require 'json'
11
10
 
11
+ def main
12
+ input_name = nil
13
+ output_name = nil
14
+ paths_name = nil
12
15
 
13
- default_env(:in, '')
14
- default_env(:out, '')
15
- default_env(:format, 'YAML')
16
- default_env(:paths, '')
17
-
18
- parser = OptionParser.new do |opts|
19
- opts.summary_indent = ' '
20
- opts.summary_width = 24
21
- opts.banner = 'Usage: openapi-frequencies [options]'
22
- opts.separator ''
23
- opts.separator 'Options (equivalent environment variable and value in parentheses):'
24
- opts.on('-i', '--input FILE', 'Read source from FILE, not stdin (IN=FILE).') do |f|
25
- env(:in, f)
26
- end
27
- opts.on('-o', '--output FILE', 'Output result to FILE, not stdout (OUT=FILE).') do |f|
28
- env(:out, f)
29
- end
30
- opts.on('-p', '--paths FILE', 'Path log file name (PATHS=FILE).') do |f|
31
- env(:paths, f)
32
- end
33
- opts.on('--yaml', 'Output format is YAML (default) (FORMAT=YAML).') do
34
- env(:format, 'YAML')
35
- end
36
- opts.on('--json', 'Output format is JSON. (FORMAT=JSON)') do
37
- env(:format, 'JSON')
38
- end
39
- opts.on('-h', '--help', 'Print this help and exit.') do
40
- $stdout.puts %(#{opts}
16
+ parser = OptionParser.new do |opts|
17
+ opts.summary_indent = ' '
18
+ opts.summary_width = 24
19
+ opts.banner = 'Usage: openapi-frequencies [options]'
20
+ opts.separator ''
21
+ opts.separator 'Options:'
22
+ opts.on('-i', '--input FILE', 'Read source from FILE, not stdin.') do |f|
23
+ input_name = f
24
+ end
25
+ opts.on('-o', '--output FILE', 'Output result to FILE, not stdout.') do |f|
26
+ output_name = f
27
+ end
28
+ opts.on('-p', '--paths FILE', 'Path log file name.') do |f|
29
+ paths_name = f
30
+ end
31
+ opts.on('-h', '--help', 'Print this help and exit.') do
32
+ $stdout.puts %(#{opts}
41
33
 
42
- Matches given file with paths and adds frequency information.
34
+ Matches given OpenAPI document file paths with paths from file and adds
35
+ into each path object matching count as "freq".
43
36
  )
44
- exit 0
37
+ exit 0
38
+ end
45
39
  end
46
- end
47
- parser.parse!
40
+ parser.parse!
48
41
 
49
- aargh("Format neither JSON nor YAML: #{env(:format)}", 1) unless %w[JSON YAML].include? env(:format)
50
- fn = env(:paths)
51
- aargh("Path log file name must be given.") if fn.empty?
42
+ return aargh('Path log file name must be given.', 1) if paths_name.nil?
52
43
 
53
- def read_source(filename)
54
- YAML.safe_load(filename.empty? ? $stdin : File.read(filename))
55
- rescue Errno::ENOENT => e
56
- aargh("Could not read #{filename.empty? ? 'stdin' : filename}", 2)
57
- rescue StandardError => e
58
- aargh(e.to_s, 3)
59
- end
60
- doc = read_source(env(:in))
44
+ doc = load_source(input_name)
45
+ return 2 if doc.nil?
61
46
 
62
- begin
63
- # Expect one path per line with nothing else. Query is allowed and ignored.
64
- f = File.new(fn, 'rt')
65
- f.each_line do |line|
66
- p = ServerPath.new(split_path(line))
67
- doc['paths'].each_value do |info|
68
- next unless (p <=> info['parts']) == 0
69
- info['freq'] = info.fetch('freq', 0) + 1
70
- # Lookalikes are the others that can be matched, since path has no
71
- # variables so any change in fixed parts (not a lookalike) will be a
72
- # mismatch with path.
73
- info['lookalike'].each do |path|
74
- cand = doc['paths'][path]
75
- next unless (p <=> cand['parts']) == 0
76
- cand['freq'] = cand.fetch('freq', 0) + 1
47
+ begin
48
+ # Expect one path per line with nothing else. Query is allowed and ignored.
49
+ f = File.new(paths_name, 'rt')
50
+ f.each_line do |line|
51
+ p = ServerPath.new(split_path(line))
52
+ doc['paths'].each_value do |info|
53
+ next unless (p <=> info['parts']).zero?
54
+ info['freq'] = info.fetch('freq', 0) + 1
55
+ # Lookalikes are the others that can be matched, since path has no
56
+ # variables so any change in fixed parts (not a lookalike) will be a
57
+ # mismatch with path.
58
+ info['lookalike'].each do |path|
59
+ cand = doc['paths'][path]
60
+ next unless (p <=> cand['parts']).zero?
61
+ cand['freq'] = cand.fetch('freq', 0) + 1
62
+ end
63
+ break
77
64
  end
78
- break
79
65
  end
66
+ f.close
67
+ rescue Errno::ENOENT
68
+ return aargh("Could not read path log file: #{paths_name}", 4)
69
+ rescue NoMethodError
70
+ return aargh('Is input file an output of openapi-processpaths?', 5)
71
+ rescue StandardError => e
72
+ return aargh(e.to_s, 6)
80
73
  end
81
- f.close
82
- rescue Errno::ENOENT => e
83
- aargh("Could not read path log file: #{fn}", 4)
84
- rescue NoMethodError
85
- aargh('Is input file an output of openapi-processpaths?', 5)
86
- rescue StandardError => e
87
- aargh(e.to_s, 6)
88
- end
89
74
 
90
- output = env(:out)
91
- if output.empty?
92
- output = $stdout
93
- else
94
- begin
95
- output = File.open(output, 'w')
96
- rescue StandardError
97
- aargh("Failed to open for writing: #{output}", 7)
98
- end
75
+ dump_result(output_name, YAML.dump(doc), 3)
99
76
  end
100
77
 
101
- case env(:format)
102
- when 'JSON' then output.puts JSON.generate(doc)
103
- when 'YAML' then output.puts YAML.dump(doc)
104
- end
105
- output.close
78
+ exit(main) if (defined? $unit_test).nil?