goodcheck 2.4.5 → 2.6.1
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 +4 -4
- data/CHANGELOG.md +29 -2
- data/README.md +7 -393
- data/lib/goodcheck.rb +4 -2
- data/lib/goodcheck/buffer.rb +44 -1
- data/lib/goodcheck/cli.rb +78 -54
- data/lib/goodcheck/commands/check.rb +20 -1
- data/lib/goodcheck/commands/config_loading.rb +19 -2
- data/lib/goodcheck/commands/init.rb +4 -2
- data/lib/goodcheck/commands/pattern.rb +2 -1
- data/lib/goodcheck/commands/test.rb +5 -4
- data/lib/goodcheck/config_loader.rb +5 -4
- data/lib/goodcheck/error.rb +3 -0
- data/lib/goodcheck/exit_status.rb +6 -0
- data/lib/goodcheck/glob.rb +14 -3
- data/lib/goodcheck/import_loader.rb +42 -10
- data/lib/goodcheck/version.rb +1 -1
- metadata +8 -70
- data/.gitignore +0 -13
- data/.rubocop.yml +0 -5
- data/.travis.yml +0 -11
- data/Dockerfile +0 -13
- data/Gemfile +0 -6
- data/Rakefile +0 -50
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/cheatsheet.pdf +0 -0
- data/docusaurus/.dockerignore +0 -2
- data/docusaurus/.gitignore +0 -12
- data/docusaurus/Dockerfile +0 -10
- data/docusaurus/docker-compose.yml +0 -18
- data/docusaurus/docs/commands.md +0 -69
- data/docusaurus/docs/configuration.md +0 -300
- data/docusaurus/docs/development.md +0 -15
- data/docusaurus/docs/getstarted.md +0 -46
- data/docusaurus/docs/rules.md +0 -79
- data/docusaurus/website/README.md +0 -193
- data/docusaurus/website/core/Footer.js +0 -100
- data/docusaurus/website/package.json +0 -14
- data/docusaurus/website/pages/en/index.js +0 -207
- data/docusaurus/website/pages/en/versions.js +0 -118
- data/docusaurus/website/sidebars.json +0 -11
- data/docusaurus/website/siteConfig.js +0 -171
- data/docusaurus/website/static/css/code-block-buttons.css +0 -39
- data/docusaurus/website/static/css/custom.css +0 -245
- data/docusaurus/website/static/img/favicon.ico +0 -0
- data/docusaurus/website/static/js/code-block-buttons.js +0 -47
- data/docusaurus/website/versioned_docs/version-1.0.0/commands.md +0 -70
- data/docusaurus/website/versioned_docs/version-1.0.0/configuration.md +0 -296
- data/docusaurus/website/versioned_docs/version-1.0.0/development.md +0 -16
- data/docusaurus/website/versioned_docs/version-1.0.0/getstarted.md +0 -47
- data/docusaurus/website/versioned_docs/version-1.0.0/rules.md +0 -81
- data/docusaurus/website/versioned_docs/version-1.0.2/rules.md +0 -79
- data/docusaurus/website/versioned_docs/version-2.4.0/configuration.md +0 -301
- data/docusaurus/website/versioned_docs/version-2.4.3/rules.md +0 -80
- data/docusaurus/website/versioned_sidebars/version-1.0.0-sidebars.json +0 -11
- data/docusaurus/website/versioned_sidebars/version-1.0.2-sidebars.json +0 -11
- data/docusaurus/website/versioned_sidebars/version-2.4.0-sidebars.json +0 -11
- data/docusaurus/website/versions.json +0 -9
- data/docusaurus/website/yarn.lock +0 -6712
- data/goodcheck.gemspec +0 -36
- data/goodcheck.yml +0 -10
- data/logo/GoodCheck Horizontal.pdf +0 -899
- data/logo/GoodCheck Horizontal.png +0 -0
- data/logo/GoodCheck Horizontal.svg +0 -55
- data/logo/GoodCheck logo.png +0 -0
- data/logo/GoodCheck vertical.png +0 -0
- data/sample.yml +0 -57
data/lib/goodcheck.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "strscan"
|
2
2
|
require "pathname"
|
3
|
+
require "set"
|
3
4
|
require "strong_json"
|
4
5
|
require "yaml"
|
5
6
|
require "json"
|
@@ -8,12 +9,13 @@ require "active_support/core_ext/integer/inflections"
|
|
8
9
|
require "active_support/tagged_logging"
|
9
10
|
require "rainbow"
|
10
11
|
require "digest/sha2"
|
11
|
-
require "
|
12
|
+
require "net/http"
|
12
13
|
|
13
14
|
require "goodcheck/version"
|
15
|
+
require "goodcheck/error"
|
14
16
|
require "goodcheck/logger"
|
15
17
|
require "goodcheck/home_path"
|
16
|
-
|
18
|
+
require "goodcheck/exit_status"
|
17
19
|
require "goodcheck/glob"
|
18
20
|
require "goodcheck/buffer"
|
19
21
|
require "goodcheck/location"
|
data/lib/goodcheck/buffer.rb
CHANGED
@@ -3,6 +3,33 @@ module Goodcheck
|
|
3
3
|
attr_reader :path
|
4
4
|
attr_reader :content
|
5
5
|
|
6
|
+
DISABLE_LINE_PATTERNS = [
|
7
|
+
/\/\/ goodcheck-disable-line$/, #JS, Java, C, ...
|
8
|
+
/# goodcheck-disable-line$/, # Ruby, Python, PHP, ...
|
9
|
+
/-- goodcheck-disable-line$/, # Haskel, SQL, ...
|
10
|
+
/<!-- goodcheck-disable-line -->$/, # HTML, Markdown, ...
|
11
|
+
/\/\* goodcheck-disable-line \*\/$/, # CSS, SCSS,
|
12
|
+
/\{\s*\/\* goodcheck-disable-line \*\/\s*\}$/, # JSX, ...
|
13
|
+
/<%# goodcheck-disable-line %>$/, # ERB, ...
|
14
|
+
/' goodcheck-disable-line$/, # VB
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
DISABLE_NEXT_LINE_PATTERNS = [
|
18
|
+
/\/\/ goodcheck-disable-next-line$/, #JS, Java, C, ...
|
19
|
+
/# goodcheck-disable-next-line$/, # Ruby, Python, PHP, ...
|
20
|
+
/-- goodcheck-disable-next-line$/, # Haskel, SQL, ...
|
21
|
+
/<!-- goodcheck-disable-next-line -->$/, # HTML, Markdown, ...
|
22
|
+
/\/\* goodcheck-disable-next-line \*\/$/, # CSS, SCSS,
|
23
|
+
/\{\s*\/\* goodcheck-disable-next-line \*\/\s*\}$/, # JSX, ...
|
24
|
+
/<%# goodcheck-disable-next-line %>$/, # ERB, ...
|
25
|
+
/' goodcheck-disable-next-line$/, # VB
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
class << self
|
29
|
+
attr_accessor :DISABLE_LINE_PATTERNS
|
30
|
+
attr_accessor :DISABLE_NEXT_LINE_PATTERNS
|
31
|
+
end
|
32
|
+
|
6
33
|
def initialize(path:, content:)
|
7
34
|
@path = path
|
8
35
|
@content = content
|
@@ -24,6 +51,18 @@ module Goodcheck
|
|
24
51
|
|
25
52
|
@line_ranges
|
26
53
|
end
|
54
|
+
|
55
|
+
def line_disabled?(line_number)
|
56
|
+
if line_number > 1
|
57
|
+
return true if DISABLE_NEXT_LINE_PATTERNS.any? { |pattern| line(line_number - 1).match?(pattern) }
|
58
|
+
end
|
59
|
+
|
60
|
+
if line_number <= lines.length
|
61
|
+
return DISABLE_LINE_PATTERNS.any? { |pattern| line(line_number).match?(pattern) }
|
62
|
+
end
|
63
|
+
|
64
|
+
return false
|
65
|
+
end
|
27
66
|
|
28
67
|
def location_for_position(position)
|
29
68
|
line_index = line_ranges.bsearch_index do |range|
|
@@ -35,8 +74,12 @@ module Goodcheck
|
|
35
74
|
end
|
36
75
|
end
|
37
76
|
|
77
|
+
def lines
|
78
|
+
@lines ||= content.lines
|
79
|
+
end
|
80
|
+
|
38
81
|
def line(line_number)
|
39
|
-
|
82
|
+
lines[line_number-1]
|
40
83
|
end
|
41
84
|
|
42
85
|
def position_for_location(line, column)
|
data/lib/goodcheck/cli.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "optparse"
|
2
2
|
|
3
|
-
Version = Goodcheck::VERSION
|
4
|
-
|
5
3
|
module Goodcheck
|
6
4
|
class CLI
|
5
|
+
include ExitStatus
|
6
|
+
|
7
7
|
attr_reader :stdout
|
8
8
|
attr_reader :stderr
|
9
9
|
|
@@ -19,8 +19,9 @@ module Goodcheck
|
|
19
19
|
pattern: "Print regexp for rules",
|
20
20
|
version: "Print version",
|
21
21
|
help: "Show help and quit"
|
22
|
-
}
|
22
|
+
}.freeze
|
23
23
|
|
24
|
+
DEFAULT_CONFIG_FILE = Pathname("goodcheck.yml").freeze
|
24
25
|
|
25
26
|
def run(args)
|
26
27
|
command = args.shift&.to_sym
|
@@ -30,16 +31,22 @@ module Goodcheck
|
|
30
31
|
elsif command == :"--version"
|
31
32
|
version(args)
|
32
33
|
else
|
33
|
-
|
34
|
+
if command
|
35
|
+
stderr.puts "invalid command: #{command}"
|
36
|
+
stderr.puts ""
|
37
|
+
end
|
34
38
|
help(args)
|
35
|
-
|
39
|
+
EXIT_ERROR
|
36
40
|
end
|
41
|
+
rescue OptionParser::ParseError => exn
|
42
|
+
stderr.puts exn
|
43
|
+
EXIT_ERROR
|
37
44
|
rescue => exn
|
38
45
|
stderr.puts exn.inspect
|
39
46
|
exn.backtrace.each do |bt|
|
40
47
|
stderr.puts " #{bt}"
|
41
48
|
end
|
42
|
-
|
49
|
+
EXIT_ERROR
|
43
50
|
end
|
44
51
|
|
45
52
|
def home_path
|
@@ -51,32 +58,28 @@ module Goodcheck
|
|
51
58
|
end
|
52
59
|
|
53
60
|
def check(args)
|
54
|
-
config_path =
|
61
|
+
config_path = DEFAULT_CONFIG_FILE
|
55
62
|
targets = []
|
56
63
|
rules = []
|
57
|
-
|
64
|
+
formats = [:text, :json]
|
65
|
+
format = :text
|
58
66
|
loglevel = Logger::ERROR
|
59
67
|
force_download = false
|
60
68
|
|
61
|
-
OptionParser.new("Usage: goodcheck check [options]
|
62
|
-
opts
|
63
|
-
|
64
|
-
|
65
|
-
opts
|
69
|
+
OptionParser.new("Usage: goodcheck check [options] paths...") do |opts|
|
70
|
+
config_option(opts) { |config| config_path = config }
|
71
|
+
verbose_option(opts) { |level| loglevel = level }
|
72
|
+
debug_option(opts) { |level| loglevel = level }
|
73
|
+
force_download_option(opts) { force_download = true }
|
74
|
+
common_options(opts)
|
75
|
+
|
76
|
+
opts.on("-R RULE", "--rule=RULE", "Only rule(s) to check") do |rule|
|
66
77
|
rules << rule
|
67
78
|
end
|
68
|
-
|
79
|
+
|
80
|
+
opts.on("--format=<#{formats.join('|')}>", formats, "Output format [default: '#{format}']") do |f|
|
69
81
|
format = f
|
70
82
|
end
|
71
|
-
opts.on("-v", "--verbose") do
|
72
|
-
loglevel = Logger::INFO
|
73
|
-
end
|
74
|
-
opts.on("-d", "--debug") do
|
75
|
-
loglevel = Logger::DEBUG
|
76
|
-
end
|
77
|
-
opts.on("--force") do
|
78
|
-
force_download = true
|
79
|
-
end
|
80
83
|
end.parse!(args)
|
81
84
|
|
82
85
|
Goodcheck.logger.level = loglevel
|
@@ -88,13 +91,12 @@ module Goodcheck
|
|
88
91
|
end
|
89
92
|
|
90
93
|
reporter = case format
|
91
|
-
when
|
94
|
+
when :text
|
92
95
|
Reporters::Text.new(stdout: stdout)
|
93
|
-
when
|
96
|
+
when :json
|
94
97
|
Reporters::JSON.new(stdout: stdout, stderr: stderr)
|
95
98
|
else
|
96
|
-
|
97
|
-
return 1
|
99
|
+
raise ArgumentError, format.inspect
|
98
100
|
end
|
99
101
|
|
100
102
|
Goodcheck.logger.info "Configuration = #{config_path}"
|
@@ -108,23 +110,16 @@ module Goodcheck
|
|
108
110
|
end
|
109
111
|
|
110
112
|
def test(args)
|
111
|
-
config_path =
|
112
|
-
loglevel = Logger::ERROR
|
113
|
+
config_path = DEFAULT_CONFIG_FILE
|
114
|
+
loglevel = ::Logger::ERROR
|
113
115
|
force_download = false
|
114
116
|
|
115
117
|
OptionParser.new("Usage: goodcheck test [options]") do |opts|
|
116
|
-
opts
|
117
|
-
|
118
|
-
|
119
|
-
opts
|
120
|
-
|
121
|
-
end
|
122
|
-
opts.on("-d", "--debug") do
|
123
|
-
loglevel = Logger::DEBUG
|
124
|
-
end
|
125
|
-
opts.on("--force") do
|
126
|
-
force_download = true
|
127
|
-
end
|
118
|
+
config_option(opts) { |config| config_path = config }
|
119
|
+
verbose_option(opts) { |level| loglevel = level }
|
120
|
+
debug_option(opts) { |level| loglevel = level }
|
121
|
+
force_download_option(opts) { force_download = true }
|
122
|
+
common_options(opts)
|
128
123
|
end.parse!(args)
|
129
124
|
|
130
125
|
Goodcheck.logger.level = loglevel
|
@@ -137,14 +132,14 @@ module Goodcheck
|
|
137
132
|
end
|
138
133
|
|
139
134
|
def init(args)
|
140
|
-
config_path =
|
135
|
+
config_path = DEFAULT_CONFIG_FILE
|
141
136
|
force = false
|
142
137
|
|
143
138
|
OptionParser.new("Usage: goodcheck init [options]") do |opts|
|
144
|
-
opts
|
145
|
-
|
146
|
-
|
147
|
-
opts.on("--force") do
|
139
|
+
config_option(opts) { |config| config_path = config }
|
140
|
+
common_options(opts)
|
141
|
+
|
142
|
+
opts.on("--force", "Overwrite an existing file") do
|
148
143
|
force = true
|
149
144
|
end
|
150
145
|
end.parse!(args)
|
@@ -152,9 +147,9 @@ module Goodcheck
|
|
152
147
|
Commands::Init.new(stdout: stdout, stderr: stderr, path: config_path, force: force).run
|
153
148
|
end
|
154
149
|
|
155
|
-
def version(
|
150
|
+
def version(_args = nil)
|
156
151
|
stdout.puts "goodcheck #{VERSION}"
|
157
|
-
|
152
|
+
EXIT_SUCCESS
|
158
153
|
end
|
159
154
|
|
160
155
|
def help(args)
|
@@ -164,19 +159,48 @@ module Goodcheck
|
|
164
159
|
COMMANDS.each do |c, msg|
|
165
160
|
stdout.puts " goodcheck #{c}\t#{msg}"
|
166
161
|
end
|
167
|
-
|
162
|
+
EXIT_SUCCESS
|
168
163
|
end
|
169
164
|
|
170
165
|
def pattern(args)
|
171
|
-
config_path =
|
166
|
+
config_path = DEFAULT_CONFIG_FILE
|
172
167
|
|
173
|
-
OptionParser.new
|
174
|
-
opts.
|
175
|
-
|
176
|
-
|
168
|
+
OptionParser.new do |opts|
|
169
|
+
opts.banner = "Usage: goodcheck pattern [options] ids..."
|
170
|
+
config_option(opts) { |config| config_path = config }
|
171
|
+
common_options(opts)
|
177
172
|
end.parse!(args)
|
178
173
|
|
179
174
|
Commands::Pattern.new(stdout: stdout, stderr: stderr, path: config_path, ids: Set.new(args), home_path: home_path).run
|
180
175
|
end
|
176
|
+
|
177
|
+
def config_option(opts)
|
178
|
+
opts.on("-c CONFIG", "--config=CONFIG", "Configuration file path [default: '#{DEFAULT_CONFIG_FILE}']") do |config|
|
179
|
+
yield Pathname(config)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def verbose_option(opts)
|
184
|
+
opts.on("-v", "--verbose", "Set log level to verbose") { yield ::Logger::INFO }
|
185
|
+
end
|
186
|
+
|
187
|
+
def debug_option(opts)
|
188
|
+
opts.on("-d", "--debug", "Set log level to debug") { yield ::Logger::DEBUG }
|
189
|
+
end
|
190
|
+
|
191
|
+
def force_download_option(opts, &block)
|
192
|
+
opts.on("--force", "Download importing files always", &block)
|
193
|
+
end
|
194
|
+
|
195
|
+
def common_options(opts)
|
196
|
+
opts.on_tail("--version", COMMANDS.fetch(:version)) do
|
197
|
+
version
|
198
|
+
exit EXIT_SUCCESS
|
199
|
+
end
|
200
|
+
opts.on_tail("-h", "--help", COMMANDS.fetch(:help)) do
|
201
|
+
stdout.puts opts.help
|
202
|
+
exit EXIT_SUCCESS
|
203
|
+
end
|
204
|
+
end
|
181
205
|
end
|
182
206
|
end
|
@@ -13,6 +13,9 @@ module Goodcheck
|
|
13
13
|
|
14
14
|
include ConfigLoading
|
15
15
|
include HomePath
|
16
|
+
include ExitStatus
|
17
|
+
|
18
|
+
EXIT_MATCH = 2
|
16
19
|
|
17
20
|
def initialize(config_path:, rules:, targets:, reporter:, stderr:, home_path:, force_download:)
|
18
21
|
@config_path = config_path
|
@@ -30,12 +33,21 @@ module Goodcheck
|
|
30
33
|
|
31
34
|
reporter.analysis do
|
32
35
|
load_config!(force_download: force_download, cache_path: cache_dir_path)
|
36
|
+
|
37
|
+
unless missing_rules.empty?
|
38
|
+
missing_rules.each do |rule|
|
39
|
+
stderr.puts "missing rule: #{rule}"
|
40
|
+
end
|
41
|
+
return EXIT_ERROR
|
42
|
+
end
|
43
|
+
|
33
44
|
each_check do |buffer, rule, trigger|
|
34
45
|
reported_issues = Set[]
|
35
46
|
|
36
47
|
reporter.rule(rule) do
|
37
48
|
analyzer = Analyzer.new(rule: rule, buffer: buffer, trigger: trigger)
|
38
49
|
analyzer.scan do |issue|
|
50
|
+
next if issue.location && buffer.line_disabled?(issue.location.start_line)
|
39
51
|
if reported_issues.add?(issue)
|
40
52
|
issue_reported = true
|
41
53
|
reporter.issue(issue)
|
@@ -45,7 +57,14 @@ module Goodcheck
|
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
48
|
-
issue_reported ?
|
60
|
+
issue_reported ? EXIT_MATCH : EXIT_SUCCESS
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def missing_rules
|
65
|
+
@missing_rules ||= begin
|
66
|
+
config_rule_ids = config.rules.map(&:id)
|
67
|
+
rules.reject { |rule| config_rule_ids.include?(rule) }
|
49
68
|
end
|
50
69
|
end
|
51
70
|
|
@@ -1,11 +1,26 @@
|
|
1
1
|
module Goodcheck
|
2
2
|
module Commands
|
3
3
|
module ConfigLoading
|
4
|
+
class ConfigFileNotFound < Error
|
5
|
+
attr_reader :path
|
6
|
+
|
7
|
+
def initialize(path:)
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
4
12
|
attr_reader :config
|
5
13
|
|
6
14
|
def load_config!(force_download:, cache_path:)
|
15
|
+
config_content =
|
16
|
+
begin
|
17
|
+
config_path.read
|
18
|
+
rescue Errno::ENOENT
|
19
|
+
raise ConfigFileNotFound.new(path: config_path)
|
20
|
+
end
|
21
|
+
|
7
22
|
import_loader = ImportLoader.new(cache_path: cache_path, force_download: force_download, config_path: config_path)
|
8
|
-
content = JSON.parse(JSON.dump(YAML.load(
|
23
|
+
content = JSON.parse(JSON.dump(YAML.load(config_content, filename: config_path.to_s)), symbolize_names: true)
|
9
24
|
loader = ConfigLoader.new(path: config_path, content: content, stderr: stderr, import_loader: import_loader)
|
10
25
|
@config = loader.load
|
11
26
|
end
|
@@ -13,7 +28,9 @@ module Goodcheck
|
|
13
28
|
def handle_config_errors(stderr)
|
14
29
|
begin
|
15
30
|
yield
|
16
|
-
|
31
|
+
rescue ConfigFileNotFound => exn
|
32
|
+
stderr.puts "Configuration file not found: #{exn.path}"
|
33
|
+
1
|
17
34
|
rescue Psych::Exception => exn
|
18
35
|
stderr.puts "Unexpected error happens while loading YAML file: #{exn.inspect}"
|
19
36
|
exn.backtrace.each do |trace_loc|
|
@@ -58,6 +58,8 @@ rules:
|
|
58
58
|
# - vendor
|
59
59
|
EOC
|
60
60
|
|
61
|
+
include ExitStatus
|
62
|
+
|
61
63
|
attr_reader :stdout
|
62
64
|
attr_reader :stderr
|
63
65
|
attr_reader :path
|
@@ -73,7 +75,7 @@ rules:
|
|
73
75
|
def run
|
74
76
|
if path.file? && !force
|
75
77
|
stderr.puts "#{path} already exists. Try --force option to overwrite the file."
|
76
|
-
return
|
78
|
+
return EXIT_ERROR
|
77
79
|
end
|
78
80
|
|
79
81
|
path.open("w") do |io|
|
@@ -82,7 +84,7 @@ rules:
|
|
82
84
|
|
83
85
|
stdout.puts "Wrote #{path}. ✍️"
|
84
86
|
|
85
|
-
|
87
|
+
EXIT_SUCCESS
|
86
88
|
end
|
87
89
|
end
|
88
90
|
end
|
@@ -9,6 +9,7 @@ module Goodcheck
|
|
9
9
|
|
10
10
|
include ConfigLoading
|
11
11
|
include HomePath
|
12
|
+
include ExitStatus
|
12
13
|
|
13
14
|
def initialize(stdout:, stderr:, path:, ids:, home_path:)
|
14
15
|
@stdout = stdout
|
@@ -34,7 +35,7 @@ module Goodcheck
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
|
38
|
+
EXIT_SUCCESS
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
@@ -3,6 +3,7 @@ module Goodcheck
|
|
3
3
|
class Test
|
4
4
|
include ConfigLoading
|
5
5
|
include HomePath
|
6
|
+
include ExitStatus
|
6
7
|
|
7
8
|
attr_reader :stdout
|
8
9
|
attr_reader :stderr
|
@@ -24,13 +25,13 @@ module Goodcheck
|
|
24
25
|
|
25
26
|
if config.rules.empty?
|
26
27
|
stdout.puts "No rules."
|
27
|
-
return
|
28
|
+
return EXIT_SUCCESS
|
28
29
|
end
|
29
30
|
|
30
|
-
validate_rule_uniqueness or return
|
31
|
-
validate_rules or return
|
31
|
+
validate_rule_uniqueness or return EXIT_ERROR
|
32
|
+
validate_rules or return EXIT_ERROR
|
32
33
|
|
33
|
-
|
34
|
+
EXIT_SUCCESS
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|