Module: AnnFlavorCore::Parser

Defined in:
lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb

Overview

Single source of truth for parsing annspec.yaml.

Two entry points:

AnnFlavorCore::Parser.parse(yaml_string)   — from raw YAML text
AnnFlavorCore::Parser.parse_file(path)     — from a file path
AnnFlavorCore::Parser.from_hash(hash)      — from a pre-parsed Hash
                                            (e.g. in-memory UI data)

After parsing, pass the AnnSpec to AnnFlavorCore::Resolver.resolve_all to obtain all resolved (effective) values per flavor × build-type.

Constant Summary collapse

DART_DEFINES_ACTION_KEYS =
%w[compile run deploy].freeze

Class Method Summary collapse

Class Method Details

.from_hash(raw) ⇒ Object



27
28
29
30
31
32
33
34
35
36
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 27

def self.from_hash(raw)
  app_hash = h(raw, 'app')
  tooling_hash = h(raw, 'tooling')
  AnnSpec.new(
    app:    parse_ann_app(app_hash),
    enabled:      raw.fetch('enabled', true),
    debug_config: parse_debug_config(h(raw, 'debug')),
    tooling:      (parse_tooling(tooling_hash) unless tooling_hash.empty?),
  )
end

.h(map, key) ⇒ Object

Safe Hash accessor — returns {} when key is missing or value is not a Hash



352
353
354
355
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 352

def self.h(map, key)
  val = map.is_a?(Hash) ? map[key] : nil
  val.is_a?(Hash) ? val : {}
end

.normalize_custom_value(v) ⇒ Object



46
47
48
49
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 46

def self.normalize_custom_value(v)
  return v.map { |e| e.nil? ? '' : e.to_s } if v.is_a?(Array)
  v
end

.parse(yaml_string) ⇒ Object



18
19
20
21
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 18

def self.parse(yaml_string)
  raw = YAML.safe_load(yaml_string, aliases: true) || {}
  from_hash(raw)
end

.parse_android(m) ⇒ Object

── Android ───────────────────────────────────────────────────────────────



82
83
84
85
86
87
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 82

def self.parse_android(m)
  AndroidPlatform.new(
    default: parse_android_default(h(m, 'default')),
    flavors: parse_flavors(h(m, 'flavor')) { |v| parse_android_flavor(v) },
  )
end

.parse_android_credentials(m) ⇒ Object



131
132
133
134
135
136
137
138
139
140
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 131

def self.parse_android_credentials(m)
  AndroidCredentials.new(
    signing:        (AndroidSigning.new(key_file: h(m, 'signing')['key_file'])                             unless h(m, 'signing').empty?),
    google_play:    (GooglePlayCredentials.new(api_key: h(m, 'google_play')['api_key'])                    unless h(m, 'google_play').empty?),
    samsung_galaxy: (SamsungGalaxyCredentials.new(seller_id: h(m, 'samsung_galaxy')['seller_id'],
                       api_key: h(m, 'samsung_galaxy')['api_key'])                                        unless h(m, 'samsung_galaxy').empty?),
    amazon:         (AmazonCredentials.new(client_id: h(m, 'amazon')['client_id'],
                       client_secret: h(m, 'amazon')['client_secret'])                                    unless h(m, 'amazon').empty?),
  )
end

.parse_android_default(m) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 89

def self.parse_android_default(m)
  AndroidDefault.new(
    id:           m['id'],
    name:         m['name'],
    version_name: m['version_name']&.to_s,
    version_code: m['version_code']&.to_i,
    main_file:    m['main_file'],
    admob:        (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
    icon:         m['icon'],
    sdk:          (parse_android_sdk(h(m, 'sdk'))                        unless h(m, 'sdk').empty?),
    credentials:  (parse_android_credentials(h(m, 'credentials'))        unless h(m, 'credentials').empty?),
    firebase:     (parse_firebase_config(h(m, 'firebase'))               unless h(m, 'firebase').empty?),
    build_types:  parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end

.parse_android_flavor(m) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 107

def self.parse_android_flavor(m)
  AndroidFlavor.new(
    id:           m['id'],
    id_suffix:    m.fetch('id_suffix', '').to_s,
    name:         m['name'],
    name_suffix:  m.fetch('name_suffix', '').to_s,
    version_name: m['version_name']&.to_s,
    version_code: m['version_code']&.to_i,
    main_file:    m['main_file'],
    admob:        (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
    icon:         m['icon'],
    firebase:     (parse_firebase_config(h(m, 'firebase'))               unless h(m, 'firebase').empty?),
    stores:       (parse_flavor_stores(h(m, 'stores'))             unless h(m, 'stores').empty?),
    credentials:  (parse_android_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
    build_types:  parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end

.parse_android_sdk(m) ⇒ Object



127
128
129
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 127

def self.parse_android_sdk(m)
  AndroidSdk.new(min_sdk: m['minSdk'], compile_sdk: m['compileSdk'], target_sdk: m['targetSdk'])
end

.parse_ann_app(m) ⇒ Object

── AnnApp ────────────────────────────────────────────────────────────────



61
62
63
64
65
66
67
68
69
70
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 61

def self.parse_ann_app(m)
  AnnApp.new(
    android:      (parse_android(h(m, 'android'))            unless h(m, 'android').empty?),
    ios:          (parse_ios(h(m, 'ios'))                    unless h(m, 'ios').empty?),
    web:          (parse_web(h(m, 'web'))                    unless h(m, 'web').empty?),
    windows:      (parse_windows(h(m, 'windows'))            unless h(m, 'windows').empty?),
    general:      (parse_general(h(m, 'general'))            unless h(m, 'general').empty?),
    integrations: (parse_integrations(h(m, 'integrations'))  unless h(m, 'integrations').empty?),
  )
end

.parse_auth_config(m) ⇒ Object



293
294
295
296
297
298
299
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 293

def self.parse_auth_config(m)
  AuthConfig.new(
    client_id:          m['clientId'],
    reversed_client_id: m['reversedClientId'],
    others:             h(m, 'others'),
  )
end

.parse_build_type_configs(m) ⇒ Object



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 301

def self.parse_build_type_configs(m)
  m.transform_values do |v|
    v = v.is_a?(Hash) ? v : {}
    BuildTypeConfig.new(
      id_suffix:                  v.fetch('id_suffix', '').to_s,
      name_suffix:                v.fetch('name_suffix', '').to_s,
      firebase:                   (parse_firebase_config(h(v, 'firebase'))              unless h(v, 'firebase').empty?),
      auth:                       (parse_auth_config(h(v, 'auth'))                      unless h(v, 'auth').empty?),
      admob:                      (AdmobConfig.new(gms_ads_id: h(v, 'admob')['gms_ads_id']) unless h(v, 'admob').empty?),
      minify_enabled:             v['minifyEnabled'],
      shrink_resources:           v['shrinkResources'],
      lint_check_release_builds:  v['lintCheckReleaseBuilds'],
      ndk_version:                v['ndkVersion'],
      ndk_debug_symbol_level:     v['ndkDebugSymbolLevel'],
      ndk_abi_filters:            (v['ndkAbiFilters'].is_a?(Array) ? v['ndkAbiFilters'] : []),
      custom:                     parse_custom_config(h(v, 'custom')),
      dart_defines:               parse_dart_defines(h(v, 'dart_defines')),
      info_plist:                 v['info_plist'],
    )
  end
end

.parse_cloudflare(m) ⇒ Object



238
239
240
241
242
243
244
245
246
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 238

def self.parse_cloudflare(m)
  return nil if m.empty?
  CloudflareConfig.new(
    project_name: m['project_name'],
    account_id:   m['account_id'],
    branch:       m['branch'],
    mode:         m['mode'],
  )
end

.parse_custom_config(m) ⇒ Object



38
39
40
41
42
43
44
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 38

def self.parse_custom_config(m)
  return {} unless m.is_a?(Hash)
  m.transform_values do |group_val|
    next {} unless group_val.is_a?(Hash)
    group_val.transform_values { |v| normalize_custom_value(v) }
  end
end

.parse_dart_defines(m) ⇒ Object

Parses dart_defines: — plain string entries are the common map; the three reserved sub-keys compile/run/deploy (each a nested map) become the corresponding action scope. Values are coerced to strings.



362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 362

def self.parse_dart_defines(m)
  return DartDefines.defaults unless m.is_a?(Hash)

  string_map = ->(raw) {
    return {} unless raw.is_a?(Hash)
    raw.transform_values { |v| v.nil? ? '' : v.to_s }
  }

  common = m.reject { |k, _| DART_DEFINES_ACTION_KEYS.include?(k) }
            .transform_values { |v| v.nil? ? '' : v.to_s }

  DartDefines.new(
    common:  common,
    compile: string_map.call(m['compile']),
    run:     string_map.call(m['run']),
    deploy:  string_map.call(m['deploy']),
  )
end

.parse_debug_config(m) ⇒ Object



337
338
339
340
341
342
343
344
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 337

def self.parse_debug_config(m)
  DebugConfig.new(
    print_debug:                    m.fetch('printDebug', false),
    print_build_and_flavor_info:    m.fetch('printBuildAndFlavorInfo', true),
    print_sdk_versions:             m.fetch('printSdkVersions', true),
    print_release_build_type_info:  m.fetch('printReleaseBuildTypeInfo', false),
  )
end

.parse_file(path) ⇒ Object



23
24
25
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 23

def self.parse_file(path)
  parse(File.read(path))
end

.parse_firebase_config(m) ⇒ Object

── Shared parsers ────────────────────────────────────────────────────────



284
285
286
287
288
289
290
291
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 284

def self.parse_firebase_config(m)
  FirebaseConfig.new(
    project_id:      m['project_id'],
    config_file:     m['config_file'],
    service_account: m['service_account'],
    target:          m['target'],
  )
end

.parse_flavor_stores(m) ⇒ Object



323
324
325
326
327
328
329
330
331
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 323

def self.parse_flavor_stores(m)
  FlavorStores.new(
    google_play:    (GooglePlayFlavorStore.new(priority: h(m, 'google_play')['priority'])            unless h(m, 'google_play').empty?),
    samsung_galaxy: (SamsungGalaxyFlavorStore.new(app_id: h(m, 'samsung_galaxy')['app_id'])         unless h(m, 'samsung_galaxy').empty?),
    amazon:         (AmazonFlavorStore.new(app_id: h(m, 'amazon')['app_id'])                        unless h(m, 'amazon').empty?),
    app_store:      (AppStoreFlavorStore.new(apple_id: h(m, 'app_store')['apple_id'],
                      team_id: h(m, 'app_store')['team_id'])                                        unless h(m, 'app_store').empty?),
  )
end

.parse_flavors(flavor_map, &parse_fn) ⇒ Object



346
347
348
349
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 346

def self.parse_flavors(flavor_map, &parse_fn)
  return {} unless flavor_map.is_a?(Hash)
  flavor_map.transform_values { |v| parse_fn.call(v.is_a?(Hash) ? v : {}) }
end

.parse_general(_m) ⇒ Object



333
334
335
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 333

def self.parse_general(_m)
  GeneralConfig.new
end

.parse_integrations(m) ⇒ Object



72
73
74
75
76
77
78
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 72

def self.parse_integrations(m)
  AnnIntegrations.new(
    fastlane: m.fetch('fastlane', false),
    melos:    m.fetch('melos',    false),
    firebase: m.fetch('firebase', false),
  )
end

.parse_ios(m) ⇒ Object

── iOS ───────────────────────────────────────────────────────────────────



144
145
146
147
148
149
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 144

def self.parse_ios(m)
  IosPlatform.new(
    default: parse_ios_default(h(m, 'default')),
    flavors: parse_flavors(h(m, 'flavor')) { |v| parse_ios_flavor(v) },
  )
end

.parse_ios_credentials(m) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 190

def self.parse_ios_credentials(m)
  IosCredentials.new(
    signing:   (IosSigning.new(team_id: h(m, 'signing')['team_id'])                                       unless h(m, 'signing').empty?),
    app_store: (AppStoreCredentials.new(
                 api_key:                          h(m, 'app_store')['api_key'],
                 export_options_plist:             h(m, 'app_store')['export_options_plist'],
                 export_options_team_id:           h(m, 'app_store')['export_options_team_id'],
                 export_options_signing_certificate: h(m, 'app_store')['export_options_signing_certificate'],
               )                                                                                            unless h(m, 'app_store').empty?),
  )
end

.parse_ios_default(m) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 151

def self.parse_ios_default(m)
  IosDefault.new(
    id:           m['id'],
    name:         m['name'],
    version_name: m['version_name']&.to_s,
    version_code: m['version_code']&.to_i,
    main_file:    m['main_file'],
    admob:        (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
    icon:         m['icon'],
    credentials:  (parse_ios_credentials(h(m, 'credentials')) unless h(m, 'credentials').empty?),
    firebase:     (parse_firebase_config(h(m, 'firebase'))    unless h(m, 'firebase').empty?),
    build_types:  parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
    info_plist:   m['info_plist'],
  )
end

.parse_ios_flavor(m) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 169

def self.parse_ios_flavor(m)
  IosFlavor.new(
    id:           m['id'],
    id_suffix:    m.fetch('id_suffix', '').to_s,
    name:         m['name'],
    name_suffix:  m.fetch('name_suffix', '').to_s,
    version_name: m['version_name']&.to_s,
    version_code: m['version_code']&.to_i,
    main_file:    m['main_file'],
    admob:        (AdmobConfig.new(gms_ads_id: h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']) unless (h(m, 'admob')['gms_ads_id'] || m['gms_ads_id']).nil?),
    icon:         m['icon'],
    firebase:     (parse_firebase_config(h(m, 'firebase'))               unless h(m, 'firebase').empty?),
    stores:       (parse_flavor_stores(h(m, 'stores'))           unless h(m, 'stores').empty?),
    credentials:  (parse_ios_credentials(h(m, 'credentials'))    unless h(m, 'credentials').empty?),
    build_types:  parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
    info_plist:   m['info_plist'],
  )
end

.parse_tooling(m) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 51

def self.parse_tooling(m)
  ToolingConfig.new(
    gradle_plugin:    m['gradle_plugin'],
    cocoapods_plugin: m['cocoapods_plugin'],
    fastlane_plugin:  m['fastlane_plugin'],
  )
end

.parse_web(m) ⇒ Object

── Web ───────────────────────────────────────────────────────────────────



204
205
206
207
208
209
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 204

def self.parse_web(m)
  WebPlatform.new(
    default: parse_web_default(h(m, 'default')),
    flavors: parse_flavors(h(m, 'flavor')) { |v| parse_web_flavor(v) },
  )
end

.parse_web_default(m) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 211

def self.parse_web_default(m)
  WebDefault.new(
    id: m['id'], name: m['name'],
    version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
    main_file: m['main_file'], icon: m['icon'],
    cloudflare: parse_cloudflare(h(m, 'cloudflare')),
    firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
    build_types: parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end

.parse_web_flavor(m) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 224

def self.parse_web_flavor(m)
  WebFlavor.new(
    id: m['id'], id_suffix: m.fetch('id_suffix', '').to_s,
    name: m['name'], name_suffix: m.fetch('name_suffix', '').to_s,
    version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
    main_file: m['main_file'], icon: m['icon'],
    cloudflare: parse_cloudflare(h(m, 'cloudflare')),
    firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
    build_types: parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end

.parse_windows(m) ⇒ Object

── Windows ───────────────────────────────────────────────────────────────



250
251
252
253
254
255
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 250

def self.parse_windows(m)
  WindowsPlatform.new(
    default: parse_windows_default(h(m, 'default')),
    flavors: parse_flavors(h(m, 'flavor')) { |v| parse_windows_flavor(v) },
  )
end

.parse_windows_default(m) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 257

def self.parse_windows_default(m)
  WindowsDefault.new(
    id: m['id'], name: m['name'],
    version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
    main_file: m['main_file'],
    firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
    build_types: parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end

.parse_windows_flavor(m) ⇒ Object



269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/vendor/ann_flavor_core/lib/ann_flavor_core/parser.rb', line 269

def self.parse_windows_flavor(m)
  WindowsFlavor.new(
    id: m['id'], id_suffix: m.fetch('id_suffix', '').to_s,
    name: m['name'], name_suffix: m.fetch('name_suffix', '').to_s,
    version_name: m['version_name']&.to_s, version_code: m['version_code']&.to_i,
    main_file: m['main_file'],
    firebase: (parse_firebase_config(h(m, 'firebase')) unless h(m, 'firebase').empty?),
    build_types: parse_build_type_configs(h(m, 'build_types')),
    custom: parse_custom_config(h(m, 'custom')),
    dart_defines: parse_dart_defines(h(m, 'dart_defines')),
  )
end