file-digests 0.0.30 → 0.0.35

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/file-digests +14 -0
  3. data/lib/file-digests.rb +162 -92
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99b58aede8267994cc69da0ac1fd2e35d6661fd666257e9f3dfcde5054b3b6f8
4
- data.tar.gz: 4b733dcf4be1f14b4a08931d15d0531c18e161f3d6d3af3e3025b36266f67e9d
3
+ metadata.gz: 42e870b2a0e4e711e84a2748d09d8a378fc6964d78706d573ab22737d0f2519e
4
+ data.tar.gz: e14bef4ccd63fdc07950a257654c6594b793eabf44bbdf26cae59e621838e5c3
5
5
  SHA512:
6
- metadata.gz: 9cf4c3df2b8f206b54689f2506de474ca3b021c572869765caf6e1424dac73a7c6dca3b67918ba5cb88f712353febe9aebe5fc40f7da24f6dd4019300b052436
7
- data.tar.gz: cb0904f141e8861c9923647214c92bb8ce022952c53e8a96cee9819a43f4192bf550b47c927cc0ea8f5a16f2c58c3013561555c2afde9c093dd6cd7997f25c94
6
+ metadata.gz: b6b9eb636258ba06b29bd21736b477c0d225ba119ad3b3ccb82612c941bf892109876aa4e8da7402f580f4167f84000a1b2b4ae91e568ac50f223325fab856ef
7
+ data.tar.gz: afcdffefe0dcb346afcdf37ce6f3f523d4862a45a455838d8b7cbf603cead2c8fadbac185640088c79bd7e8c412b5da932bf301d90dd4337171b675a65f34388
@@ -1,5 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
3
17
  require 'file-digests'
4
18
 
5
19
  FileDigests.run_cli_utility
@@ -1,3 +1,17 @@
1
+ # Copyright 2020 Stanislav Senotrusov <stan@senotrusov.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
1
15
  require "date"
2
16
  require "digest"
3
17
  require "fileutils"
@@ -8,7 +22,8 @@ require "set"
8
22
  require "sqlite3"
9
23
 
10
24
  class FileDigests
11
- DIGEST_ALGORITHMS=["BLAKE2b512", "SHA3-256", "SHA512-256"]
25
+ VERSION = Gem.loaded_specs["file-digests"]&.version&.to_s
26
+ DIGEST_ALGORITHMS = ["BLAKE2b512", "SHA3-256", "SHA512-256"]
12
27
  LEGACY_DIGEST_ALGORITHMS = ["SHA512", "SHA256"]
13
28
 
14
29
  def self.canonical_digest_algorithm_name(string)
@@ -75,7 +90,12 @@ class FileDigests
75
90
  options[:quiet] = true
76
91
  end
77
92
 
78
- opts.on("-t", "--test", "Perform only the test, do not modify the digest database.") do
93
+ opts.on(
94
+ "-t", "--test",
95
+ "Perform a test to verify directory contents.",
96
+ "Compare actual files with the stored digests, check if any files are missing.",
97
+ "Digest database will not be modified."
98
+ ) do
79
99
  options[:test_only] = true
80
100
  end
81
101
 
@@ -96,6 +116,7 @@ class FileDigests
96
116
 
97
117
  def initialize files_path, digest_database_path, options = {}
98
118
  @options = options
119
+ @user_input_wait_time = 0
99
120
 
100
121
  initialize_paths files_path, digest_database_path
101
122
  initialize_database
@@ -139,8 +160,6 @@ class FileDigests
139
160
  @db.results_as_hash = true
140
161
  @db.busy_timeout = 5000
141
162
 
142
- file_digests_gem_version = Gem.loaded_specs["file-digests"]&.version&.to_s
143
-
144
163
  execute "PRAGMA encoding = 'UTF-8'"
145
164
  execute "PRAGMA locking_mode = 'EXCLUSIVE'"
146
165
  execute "PRAGMA journal_mode = 'WAL'"
@@ -155,14 +174,13 @@ class FileDigests
155
174
  execute "CREATE TABLE metadata (
156
175
  key TEXT NOT NULL PRIMARY KEY,
157
176
  value TEXT)"
158
- execute "CREATE UNIQUE INDEX metadata_key ON metadata(key)"
159
177
  metadata_table_was_created = true
160
178
  end
161
179
 
162
180
  prepare_method :set_metadata_query, "INSERT INTO metadata (key, value) VALUES (?, ?) ON CONFLICT (key) DO UPDATE SET value=excluded.value"
163
181
  prepare_method :get_metadata_query, "SELECT value FROM metadata WHERE key = ?"
164
182
 
165
- set_metadata("metadata_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version && metadata_table_was_created
183
+ set_metadata("metadata_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION && metadata_table_was_created
166
184
 
167
185
  # Heuristic to detect database version 1 (metadata was not stored back then)
168
186
  unless get_metadata("database_version")
@@ -179,20 +197,19 @@ class FileDigests
179
197
  digest TEXT NOT NULL,
180
198
  digest_check_time TEXT NOT NULL)"
181
199
  execute "CREATE UNIQUE INDEX digests_filename ON digests(filename)"
182
- set_metadata("digests_table_created_by_gem_version", file_digests_gem_version) if file_digests_gem_version
200
+ execute "CREATE INDEX digests_digest ON digests(digest)"
201
+ set_metadata("digests_table_created_by_gem_version", FileDigests::VERSION) if FileDigests::VERSION
183
202
  end
184
203
 
185
- prepare_method :insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
186
- prepare_method :find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
187
- prepare_method :touch_digest_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
188
- prepare_method :update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
189
- prepare_method :update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
190
- prepare_method :delete_by_filename, "DELETE FROM digests WHERE filename = ?"
191
- prepare_method :query_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
192
- prepare_method :update_digest_to_new_digest, "UPDATE digests SET digest = ? WHERE digest = ?"
204
+ prepare_method :digests_insert, "INSERT INTO digests (filename, mtime, digest, digest_check_time) VALUES (?, ?, ?, datetime('now'))"
205
+ prepare_method :digests_find_by_filename_query, "SELECT id, mtime, digest FROM digests WHERE filename = ?"
206
+ prepare_method :digests_touch_check_time, "UPDATE digests SET digest_check_time = datetime('now') WHERE id = ?"
207
+ prepare_method :digests_update_mtime_and_digest, "UPDATE digests SET mtime = ?, digest = ?, digest_check_time = datetime('now') WHERE id = ?"
208
+ prepare_method :digests_update_mtime, "UPDATE digests SET mtime = ?, digest_check_time = datetime('now') WHERE id = ?"
209
+ prepare_method :digests_select_duplicates, "SELECT digest, filename FROM digests WHERE digest IN (SELECT digest FROM digests GROUP BY digest HAVING count(*) > 1) ORDER BY digest, filename;"
193
210
 
194
211
  unless get_metadata("database_version")
195
- set_metadata "database_version", "2"
212
+ set_metadata "database_version", "3"
196
213
  end
197
214
 
198
215
  # Convert database from 1st to 2nd version
@@ -207,74 +224,110 @@ class FileDigests
207
224
  end
208
225
  end
209
226
 
210
- if get_metadata("database_version") != "2"
211
- STDERR.puts "This version of file-digests (#{file_digests_gem_version || "unknown"}) is only compartible with the database version 2. Current database version is #{get_metadata("database_version")}. To use this database, please install appropriate version if file-digest."
212
- raise "Incompatible database version"
227
+ if get_metadata("database_version") == "2"
228
+ execute "CREATE INDEX digests_digest ON digests(digest)"
229
+ set_metadata "database_version", "3"
213
230
  end
231
+
232
+ check_if_database_is_at_certain_version "3"
233
+
234
+ create_temporary_tables
214
235
  end
215
236
  end
216
237
 
217
- def perform_check
218
- perhaps_transaction(@new_digest_algorithm, :exclusive) do
219
- @counters = {good: 0, updated: 0, new: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
220
- @new_files = {}
221
- @new_digests = {}
238
+ def create_temporary_tables
239
+ execute "CREATE TEMPORARY TABLE new_files (
240
+ filename TEXT NOT NULL PRIMARY KEY,
241
+ digest TEXT NOT NULL)"
242
+ execute "CREATE INDEX new_files_digest ON new_files(digest)"
243
+
244
+ prepare_method :new_files_insert, "INSERT INTO new_files (filename, digest) VALUES (?, ?)"
245
+ prepare_method :new_files_count_query, "SELECT count(*) FROM new_files"
246
+
247
+ execute "CREATE TEMPORARY TABLE missing_files (
248
+ filename TEXT NOT NULL PRIMARY KEY,
249
+ digest TEXT NOT NULL)"
250
+ execute "CREATE INDEX missing_files_digest ON missing_files(digest)"
251
+
252
+ execute "INSERT INTO missing_files (filename, digest) SELECT filename, digest FROM digests"
253
+
254
+ prepare_method :missing_files_delete, "DELETE FROM missing_files WHERE filename = ?"
255
+ prepare_method :missing_files_delete_renamed_files, "DELETE FROM missing_files WHERE digest IN (SELECT digest FROM new_files)"
256
+ prepare_method :missing_files_select_all_filenames, "SELECT filename FROM missing_files ORDER BY filename"
257
+ prepare_method :missing_files_delete_all, "DELETE FROM missing_files"
258
+ prepare_method :missing_files_count_query, "SELECT count(*) FROM missing_files"
259
+
260
+ prepare_method :digests_delete_renamed_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files WHERE digest IN (SELECT digest FROM new_files))"
261
+ prepare_method :digests_delete_all_missing_files, "DELETE FROM digests WHERE filename IN (SELECT filename FROM missing_files)"
262
+
263
+ execute "CREATE TEMPORARY TABLE new_digests (
264
+ filename TEXT NOT NULL PRIMARY KEY,
265
+ digest TEXT NOT NULL)"
266
+
267
+ prepare_method :new_digests_insert, "INSERT INTO new_digests (filename, digest) VALUES (?, ?)"
268
+ prepare_method :digests_update_digests_to_new_digests, "INSERT INTO digests (filename, digest, digest_check_time) SELECT filename, digest, false FROM new_digests WHERE true ON CONFLICT (filename) DO UPDATE SET digest=excluded.digest"
269
+ end
222
270
 
223
- @missing_files = Hash[@db.prepare("SELECT filename, digest FROM digests").execute!]
271
+ def perform_check
272
+ measure_time do
273
+ perhaps_transaction(@new_digest_algorithm, :exclusive) do
274
+ @counters = {good: 0, updated: 0, renamed: 0, likely_damaged: 0, exceptions: 0}
224
275
 
225
- measure_time do
226
276
  walk_files do |filename|
227
277
  process_file filename
228
278
  end
229
- end
230
279
 
231
- nested_transaction do
232
- track_renames
233
- end
280
+ nested_transaction do
281
+ puts "Tracking renames..." if @options[:verbose]
282
+ track_renames
283
+ end
234
284
 
235
- if any_missing_files?
236
- if any_exceptions?
237
- STDERR.puts "Due to previously occurred errors, database cleanup from missing files will be skipped this time."
238
- else
239
- print_missing_files
240
- if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
241
- nested_transaction do
242
- remove_missing_files
285
+ if any_missing_files?
286
+ if any_exceptions?
287
+ STDERR.puts "Due to previously occurred errors, missing files will not removed from the database."
288
+ else
289
+ print_missing_files
290
+ if !@options[:test_only] && (@options[:auto] || confirm("Remove missing files from the database"))
291
+ nested_transaction do
292
+ puts "Removing missing files..." if @options[:verbose]
293
+ remove_missing_files
294
+ end
243
295
  end
244
296
  end
245
297
  end
246
- end
247
298
 
248
- if @new_digest_algorithm && !@options[:test_only]
249
- if any_missing_files? || any_likely_damaged? || any_exceptions?
250
- STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
251
- else
252
- @new_digests.each do |old_digest, new_digest|
253
- update_digest_to_new_digest new_digest, old_digest
299
+ if @new_digest_algorithm && !@options[:test_only]
300
+ if any_missing_files? || any_likely_damaged? || any_exceptions?
301
+ STDERR.puts "ERROR: New digest algorithm will not be in effect until there are files that are missing, likely damaged, or processed with an exception."
302
+ else
303
+ puts "Updating database to a new digest algorithm..." if @options[:verbose]
304
+ digests_update_digests_to_new_digests
305
+ set_metadata "digest_algorithm", @new_digest_algorithm
306
+ puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
254
307
  end
255
- set_metadata "digest_algorithm", @new_digest_algorithm
256
- puts "Transition to a new digest algorithm complete: #{@new_digest_algorithm}"
257
308
  end
258
- end
259
309
 
260
- if any_likely_damaged? || any_exceptions?
261
- STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
262
- end
310
+ if any_likely_damaged? || any_exceptions?
311
+ STDERR.puts "PLEASE REVIEW ERRORS THAT WERE OCCURRED!"
312
+ end
263
313
 
264
- set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
314
+ set_metadata(@options[:test_only] ? "latest_test_only_check_time" : "latest_complete_check_time", time_to_database(Time.now))
265
315
 
266
- print_counters
267
- end
268
- execute "PRAGMA optimize"
269
- execute "VACUUM"
270
- execute "PRAGMA wal_checkpoint(TRUNCATE)"
316
+ print_counters
317
+ end
318
+
319
+ puts "Performing database maintenance..." if @options[:verbose]
320
+ execute "PRAGMA optimize"
321
+ execute "VACUUM"
322
+ execute "PRAGMA wal_checkpoint(TRUNCATE)"
271
323
 
272
- hide_database_files
324
+ hide_database_files
325
+ end
273
326
  end
274
327
 
275
328
  def show_duplicates
276
329
  current_digest = nil
277
- query_duplicates.each do |found|
330
+ digests_select_duplicates.each do |found|
278
331
  if current_digest != found["digest"]
279
332
  puts "" if current_digest
280
333
  current_digest = found["digest"]
@@ -306,9 +359,10 @@ class FileDigests
306
359
 
307
360
  normalized_filename = filename.delete_prefix("#{@files_path.to_s}/").encode("utf-8", universal_newline: true).unicode_normalize(:nfkc)
308
361
  mtime_string = time_to_database stat.mtime
309
- digest = get_file_digest(filename)
362
+ digest, new_digest = get_file_digest(filename)
310
363
 
311
364
  nested_transaction do
365
+ new_digests_insert(normalized_filename, new_digest) if new_digest
312
366
  process_file_indeed normalized_filename, mtime_string, digest
313
367
  end
314
368
 
@@ -326,15 +380,15 @@ class FileDigests
326
380
  end
327
381
 
328
382
  def process_previously_seen_file found, filename, mtime, digest
329
- @missing_files.delete(filename)
383
+ missing_files_delete filename
330
384
  if found["digest"] == digest
331
385
  @counters[:good] += 1
332
386
  puts "GOOD: #{filename}" if @options[:verbose]
333
387
  unless @options[:test_only]
334
388
  if found["mtime"] == mtime
335
- touch_digest_check_time found["id"]
389
+ digests_touch_check_time found["id"]
336
390
  else
337
- update_mtime mtime, found["id"]
391
+ digests_update_mtime mtime, found["id"]
338
392
  end
339
393
  end
340
394
  else
@@ -345,18 +399,17 @@ class FileDigests
345
399
  @counters[:updated] += 1
346
400
  puts "UPDATED#{" (FATE ACCEPTED)" if found["mtime"] == mtime && @options[:accept_fate]}: #{filename}" unless @options[:quiet]
347
401
  unless @options[:test_only]
348
- update_mtime_and_digest mtime, digest, found["id"]
402
+ digests_update_mtime_and_digest mtime, digest, found["id"]
349
403
  end
350
404
  end
351
405
  end
352
406
  end
353
407
 
354
408
  def process_new_file filename, mtime, digest
355
- @counters[:new] += 1
356
409
  puts "NEW: #{filename}" unless @options[:quiet]
410
+ new_files_insert filename, digest
357
411
  unless @options[:test_only]
358
- @new_files[filename] = digest
359
- insert filename, mtime, digest
412
+ digests_insert filename, mtime, digest
360
413
  end
361
414
  end
362
415
 
@@ -364,29 +417,31 @@ class FileDigests
364
417
  # Renames and missing files
365
418
 
366
419
  def track_renames
367
- @missing_files.delete_if do |filename, digest|
368
- if @new_files.value?(digest)
369
- @counters[:renamed] += 1
370
- unless @options[:test_only]
371
- delete_by_filename filename
372
- end
373
- true
374
- end
420
+ unless @options[:test_only]
421
+ digests_delete_renamed_files
375
422
  end
423
+ missing_files_delete_renamed_files
424
+ @counters[:renamed] = @db.changes
376
425
  end
377
426
 
378
427
  def print_missing_files
379
428
  puts "\nMISSING FILES:"
380
- @missing_files.sort.to_h.each do |filename, digest|
381
- puts filename
429
+ missing_files_select_all_filenames.each do |record|
430
+ puts record["filename"]
382
431
  end
383
432
  end
384
433
 
385
434
  def remove_missing_files
386
- @missing_files.each do |filename, digest|
387
- delete_by_filename filename
388
- end
389
- @missing_files = {}
435
+ digests_delete_all_missing_files
436
+ missing_files_delete_all
437
+ end
438
+
439
+ def missing_files_count
440
+ missing_files_count_query!&.first&.first
441
+ end
442
+
443
+ def any_missing_files?
444
+ missing_files_count > 0
390
445
  end
391
446
 
392
447
 
@@ -397,6 +452,7 @@ class FileDigests
397
452
  end
398
453
 
399
454
  def integrity_check
455
+ puts "Checking database integrity..." if @options[:verbose]
400
456
  if execute("PRAGMA integrity_check")&.first&.fetch("integrity_check") != "ok"
401
457
  raise "Database integrity check failed"
402
458
  end
@@ -451,7 +507,7 @@ class FileDigests
451
507
  end
452
508
 
453
509
  def find_by_filename filename
454
- result = find_by_filename_query filename
510
+ result = digests_find_by_filename_query filename
455
511
  found = result.next
456
512
  raise "Multiple records found" if result.next
457
513
  found
@@ -471,6 +527,18 @@ class FileDigests
471
527
  end
472
528
  end
473
529
 
530
+ def check_if_database_is_at_certain_version target_version
531
+ current_version = get_metadata("database_version")
532
+ if current_version != target_version
533
+ STDERR.puts "This version of file-digests (#{FileDigests::VERSION || "unknown"}) is only compartible with the database version #{target_version}. Current database version is #{current_version}. To use this database, please install appropriate version if file-digest."
534
+ raise "Incompatible database version"
535
+ end
536
+ end
537
+
538
+ def new_files_count
539
+ new_files_count_query!&.first&.first
540
+ end
541
+
474
542
 
475
543
  # Filesystem-related helpers
476
544
 
@@ -493,6 +561,7 @@ class FileDigests
493
561
  end
494
562
 
495
563
  def walk_files
564
+ puts "Gathering the list of files..." if @options[:verbose]
496
565
  Dir.glob(@files_path + "**" + "*", File::FNM_DOTMATCH) do |filename|
497
566
  yield filename
498
567
  end
@@ -508,18 +577,13 @@ class FileDigests
508
577
  digest.update(buffer)
509
578
  new_digest.update(buffer) if @new_digest_algorithm
510
579
  end
511
- @new_digests[digest.hexdigest] = new_digest.hexdigest if @new_digest_algorithm
512
- return digest.hexdigest
580
+ return [digest.hexdigest, (new_digest.hexdigest if @new_digest_algorithm)]
513
581
  end
514
582
  end
515
583
 
516
584
 
517
585
  # Runtime state helpers
518
586
 
519
- def any_missing_files?
520
- @missing_files.length > 0
521
- end
522
-
523
587
  def any_exceptions?
524
588
  @counters[:exceptions] > 0
525
589
  end
@@ -533,14 +597,17 @@ class FileDigests
533
597
  def confirm text
534
598
  if STDIN.tty? && STDOUT.tty?
535
599
  puts "#{text} (y/n)?"
536
- STDIN.gets.strip.downcase == "y"
600
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
601
+ result = (STDIN.gets.strip.downcase == "y")
602
+ @user_input_wait_time += (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
603
+ result
537
604
  end
538
605
  end
539
606
 
540
607
  def measure_time
541
608
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
542
609
  yield
543
- elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
610
+ elapsed = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) - @user_input_wait_time
544
611
  puts "Elapsed time: #{elapsed.to_i / 3600}h #{(elapsed.to_i % 3600) / 60}m #{"%.3f" % (elapsed % 60)}s" unless @options[:quiet]
545
612
  end
546
613
 
@@ -558,11 +625,14 @@ class FileDigests
558
625
  end
559
626
 
560
627
  def print_counters
628
+ missing_files_count_result = missing_files_count
629
+ new_files_count_result = new_files_count - @counters[:renamed]
630
+
561
631
  puts "#{@counters[:good]} file(s) passes digest check" if @counters[:good] > 0
562
632
  puts "#{@counters[:updated]} file(s) are updated" if @counters[:updated] > 0
563
- puts "#{@counters[:new]} file(s) are new" if @counters[:new] > 0
633
+ puts "#{new_files_count_result} file(s) are new" if new_files_count_result > 0
564
634
  puts "#{@counters[:renamed]} file(s) are renamed" if @counters[:renamed] > 0
565
- puts "#{@missing_files.length} file(s) are missing" if @missing_files.length > 0
635
+ puts "#{missing_files_count_result} file(s) are missing" if missing_files_count_result > 0
566
636
  puts "#{@counters[:likely_damaged]} file(s) are likely damaged (!)" if @counters[:likely_damaged] > 0
567
637
  puts "#{@counters[:exceptions]} file(s) had exceptions occured during processing (!)" if @counters[:exceptions] > 0
568
638
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-digests
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.30
4
+ version: 0.0.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stanislav Senotrusov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-15 00:00:00.000000000 Z
11
+ date: 2020-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: openssl