Skip to content

Commit 3cda14f

Browse files
committed
Move StringsFileValidationHelper to the Ios namespace
1 parent 65f6507 commit 3cda14f

File tree

3 files changed

+125
-123
lines changed

3 files changed

+125
-123
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
module Fastlane
2+
module Helper
3+
module Ios
4+
class StringsFileValidationHelper
5+
# context can be one of:
6+
# :root, :maybe_comment_start, :in_line_comment, :in_block_comment,
7+
# :maybe_block_comment_end, :in_quoted_key,
8+
# :after_quoted_key_before_eq, :after_quoted_key_and_equal,
9+
# :in_quoted_value, :after_quoted_value
10+
State = Struct.new(:context, :buffer, :in_escaped_ctx, :found_key, keyword_init: true)
11+
12+
TRANSITIONS = {
13+
root: {
14+
/\s/ => :root,
15+
'/' => :maybe_comment_start,
16+
'"' => :in_quoted_key
17+
},
18+
maybe_comment_start: {
19+
'/' => :in_line_comment,
20+
/\*/ => :in_block_comment
21+
},
22+
in_line_comment: {
23+
"\n" => :root,
24+
/./ => :in_line_comment
25+
},
26+
in_block_comment: {
27+
/\*/ => :maybe_block_comment_end,
28+
/./m => :in_block_comment
29+
},
30+
maybe_block_comment_end: {
31+
'/' => :root,
32+
/./m => :in_block_comment
33+
},
34+
in_quoted_key: {
35+
'"' => lambda do |state, _|
36+
state.found_key = state.buffer.string.dup
37+
state.buffer.string = ''
38+
:after_quoted_key_before_eq
39+
end,
40+
/./ => lambda do |state, c|
41+
state.buffer.write(c)
42+
:in_quoted_key
43+
end
44+
},
45+
after_quoted_key_before_eq: {
46+
/\s/ => :after_quoted_key_before_eq,
47+
'=' => :after_quoted_key_and_eq
48+
},
49+
after_quoted_key_and_eq: {
50+
/\s/ => :after_quoted_key_and_eq,
51+
'"' => :in_quoted_value
52+
},
53+
in_quoted_value: {
54+
'"' => :after_quoted_value,
55+
/./m => :in_quoted_value
56+
},
57+
after_quoted_value: {
58+
/\s/ => :after_quoted_value,
59+
';' => :root
60+
}
61+
}.freeze
62+
63+
# Returns all the keys in the given `.strings` file, and the line(s) at
64+
# which they are defined.
65+
#
66+
# @param [String] file The path to the file to inspect.
67+
# @return [<Hash>] A `Hash` with keys the key values read from the
68+
# `.strings` file. The value of each entry is an array with the
69+
# line numbers where the key occurs.
70+
def self.get_keys_from_strings(file:)
71+
keys_with_lines = Hash.new([])
72+
73+
state = State.new(context: :root, buffer: StringIO.new, in_escaped_ctx: false, found_key: nil)
74+
75+
File.readlines(file).each_with_index do |line, line_no|
76+
line.chars.each_with_index do |c, col_no|
77+
# Handle escaped characters at a global level. This is more
78+
# straightforward than having a `TRANSITIONS` table that account
79+
# for it.
80+
if state.in_escaped_ctx || c == '\\'
81+
# Just because we check for escaped characters at the global
82+
# level, it doesn't mean we allow them in every context.
83+
allowed_contexts_for_escaped_characters = %i[in_quoted_key in_quoted_value in_block_comment in_line_comment]
84+
raise "Found escaped character outside of allowed contexts on line #{line_no + 1} (current context: #{state.context})" unless allowed_contexts_for_escaped_characters.include?(state.context)
85+
86+
state.buffer.write(c) if state.context == :in_quoted_key
87+
state.in_escaped_ctx = !state.in_escaped_ctx
88+
next
89+
end
90+
91+
# Look at the transitions table for the current context, and find
92+
# the first transition matching the current character
93+
(_, next_context) = TRANSITIONS[state.context].find { |regex, _| c.match?(regex) } || [nil, nil]
94+
raise "Invalid character `#{c}` found on line #{line_no + 1}, col #{col_no + 1}" if next_context.nil?
95+
96+
state.context = next_context.is_a?(Proc) ? next_context.call(state, c) : next_context
97+
next unless state.found_key
98+
99+
# If we just exited the :in_quoted_key context and thus have found
100+
# a new key, process it
101+
key = state.found_key.dup
102+
state.found_key = nil
103+
104+
keys_with_lines[key] += [line_no + 1]
105+
end
106+
end
107+
108+
keys_with_lines
109+
end
110+
111+
# Inspects the given `.strings` file for duplicated keys, returning them
112+
# if any.
113+
#
114+
# @param [String] file The path to the file to inspect.
115+
# @return [Array<Hash>] Array of all the duplicated keys. Each entry is a
116+
# a `Hash` with keys: `key` for the key value and `lines` with an
117+
# array of line numbers at which the key occurs.
118+
def self.find_duplicated_keys(file:)
119+
get_keys_from_strings(file: file).keep_if { |_, lines| lines.count > 1 }
120+
end
121+
end
122+
end
123+
end
124+
end

lib/fastlane/plugin/wpmreleasetoolkit/helper/strings_file_validation_helper.rb

Lines changed: 0 additions & 122 deletions
This file was deleted.

spec/strings_file_validation_helper_spec.rb renamed to spec/ios_strings_file_validation_helper_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'spec_helper'
22

3-
describe Fastlane::Helper::StringsFileValidationHelper do
3+
describe Fastlane::Helper::Ios::StringsFileValidationHelper do
44
let(:test_data_dir) { File.join(File.dirname(__FILE__), 'test-data', 'translations') }
55

66
context 'when there is an escape character in the root context' do

0 commit comments

Comments
 (0)