• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / ffigen / 5305585135

18 Jun 2023 08:33PM CUT coverage: 85.041% (-6.8%) from 91.879%
5305585135

Pull #586

github

web-flow
Merge 27e2ea8df into 9030eb512
Pull Request #586: Add ffigen schema and refer them in example config yaml using modeline

603 of 603 new or added lines in 3 files covered. (100.0%)

3462 of 4071 relevant lines covered (85.04%)

25.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

41.44
/lib/src/config_provider/spec_utils.dart
1
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'dart:io';
6

7
import 'package:ffigen/src/code_generator.dart';
8
import 'package:ffigen/src/code_generator/utils.dart';
9
import 'package:ffigen/src/header_parser/type_extractor/cxtypekindmap.dart';
10
import 'package:file/local.dart';
11
import 'package:glob/glob.dart';
12
import 'package:logging/logging.dart';
13
import 'package:package_config/package_config.dart';
14
import 'package:path/path.dart' as p;
15
import 'package:quiver/pattern.dart' as quiver;
16
import 'package:yaml/yaml.dart';
17

18
import '../strings.dart' as strings;
19
import 'config_types.dart';
20

21
final _logger = Logger('ffigen.config_provider.spec_utils');
105✔
22

23
/// Replaces the path separators according to current platform.
24
String _replaceSeparators(String path) {
50✔
25
  if (Platform.isWindows) {
50✔
26
    return path.replaceAll(p.posix.separator, p.windows.separator);
×
27
  } else {
28
    return path.replaceAll(p.windows.separator, p.posix.separator);
250✔
29
  }
30
}
31

32
/// Replaces the path separators according to current platform. If a relative
33
/// path is passed in, it is resolved relative to the config path, and the
34
/// absolute path is returned.
35
String _normalizePath(String path, String? configFilename) {
50✔
36
  final skipNormalization =
37
      (configFilename == null) || p.isAbsolute(path) || path.startsWith("**");
16✔
38
  return _replaceSeparators(
50✔
39
      skipNormalization ? path : p.join(p.dirname(configFilename), path));
16✔
40
}
41

42
/// Checks if type of value is [T], logs an error if it's not.
43
///
44
/// [key] is printed as `'item1 -> item2 => item3'` in log message.
45
bool checkType<T>(List<String> keys, dynamic value) {
×
46
  if (value is! T) {
×
47
    _logger.severe(
×
48
        "Expected value of key '${keys.join(' -> ')}' to be of type '$T'.");
×
49
    return false;
50
  }
51
  return true;
52
}
53

54
/// Checks if there are nested [key] in [map].
55
bool checkKeyInYaml(List<String> key, YamlMap map) {
1✔
56
  dynamic last = map;
57
  for (final k in key) {
2✔
58
    if (last is YamlMap) {
1✔
59
      if (!last.containsKey(k)) return false;
1✔
60
      last = last[k];
1✔
61
    } else {
62
      return false;
63
    }
64
  }
65
  // The entry for the last key may be null.
66
  return true;
67
}
68

69
/// Extracts value of nested [key] from [map].
70
dynamic getKeyValueFromYaml(List<String> key, YamlMap map) {
1✔
71
  if (checkKeyInYaml(key, map)) {
1✔
72
    dynamic last = map;
73
    for (final k in key) {
2✔
74
      last = last[k];
1✔
75
    }
76
    return last;
77
  }
78

79
  return null;
80
}
81

82
/// Recursively checks the keys in [configKeyMap] from [allowedKeyList].
83
void warnUnknownKeys(List<List<String>> allowedKeyList, YamlMap configKeyMap) {
×
84
  final allowedKeyMap = <String, dynamic>{};
×
85
  for (final specKeys in allowedKeyList) {
×
86
    var item = allowedKeyMap;
87
    for (final specSubKey in specKeys) {
×
88
      item.putIfAbsent(specSubKey, () => <String, dynamic>{});
×
89
      item = item[specSubKey] as Map<String, dynamic>;
×
90
    }
91
    // Add empty key to mark that any sub-keys of this key are allowed.
92
    item[''] = <String, dynamic>{};
×
93
  }
94
  _warnUnknownKeysInMap(allowedKeyMap, configKeyMap, <dynamic>[]);
×
95
}
96

97
/// Recursive function to check a key set in a configKeyMap.
98
void _warnUnknownKeysInMap(Map<String, dynamic> allowedKeyMap,
×
99
    dynamic configKeyMap, List<dynamic> prev) {
100
  if (allowedKeyMap.containsKey('') || configKeyMap is! YamlMap) {
×
101
    return;
102
  }
103
  for (final key in configKeyMap.keys) {
×
104
    if (allowedKeyMap.containsKey(key)) {
×
105
      prev.add(key);
×
106
      _warnUnknownKeysInMap(
×
107
          allowedKeyMap[key] as Map<String, dynamic>, configKeyMap[key], prev);
×
108
      prev.removeLast();
×
109
    } else {
110
      prev.add(key);
×
111
      _logger.warning('Unknown key - ${prev.join(' -> ')}.');
×
112
      prev.removeLast();
×
113
    }
114
  }
115
}
116

117
bool booleanExtractor(dynamic value) => value as bool;
×
118

119
bool booleanValidator(List<String> name, dynamic value) =>
×
120
    checkType<bool>(name, value);
×
121

122
Map<String, LibraryImport> libraryImportsExtractor(dynamic yamlConfig) {
1✔
123
  final resultMap = <String, LibraryImport>{};
1✔
124
  final typeMap = yamlConfig as YamlMap?;
125
  if (typeMap != null) {
126
    for (final typeName in typeMap.keys) {
2✔
127
      resultMap[typeName as String] =
1✔
128
          LibraryImport(typeName, typeMap[typeName] as String);
2✔
129
    }
130
  }
131
  return resultMap;
132
}
133

134
bool libraryImportsValidator(List<String> name, dynamic yamlConfig) {
×
135
  if (!checkType<YamlMap>(name, yamlConfig)) {
×
136
    return false;
137
  }
138
  for (final key in (yamlConfig as YamlMap).keys) {
×
139
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
×
140
      return false;
141
    }
142
    if (strings.predefinedLibraryImports.containsKey(key)) {
×
143
      _logger.severe(
×
144
          'library-import -> $key should not collide with any predefined imports - ${strings.predefinedLibraryImports.keys}.');
×
145
      return false;
146
    }
147
  }
148
  return true;
149
}
150

151
void loadImportedTypes(YamlMap fileConfig,
1✔
152
    Map<String, ImportedType> usrTypeMappings, LibraryImport libraryImport) {
153
  final symbols = fileConfig['symbols'] as YamlMap;
1✔
154
  for (final key in symbols.keys) {
2✔
155
    final usr = key as String;
156
    final value = symbols[usr]! as YamlMap;
1✔
157
    usrTypeMappings[usr] = ImportedType(
2✔
158
        libraryImport, value['name'] as String, value['name'] as String);
2✔
159
  }
160
}
161

162
YamlMap loadSymbolFile(String symbolFilePath, String? configFileName,
1✔
163
    PackageConfig? packageConfig) {
164
  final path = symbolFilePath.startsWith('package:')
1✔
165
      ? packageConfig!.resolve(Uri.parse(symbolFilePath))!.toFilePath()
3✔
166
      : _normalizePath(symbolFilePath, configFileName);
×
167

168
  return loadYaml(File(path).readAsStringSync()) as YamlMap;
3✔
169
}
170

171
Map<String, ImportedType> symbolFileImportExtractor(
1✔
172
    dynamic yamlConfig,
173
    Map<String, LibraryImport> libraryImports,
174
    String? configFileName,
175
    PackageConfig? packageConfig) {
176
  final resultMap = <String, ImportedType>{};
1✔
177
  for (final item in (yamlConfig as YamlList)) {
2✔
178
    String symbolFilePath;
179
    if (item is String) {
1✔
180
      symbolFilePath = item;
181
    } else {
182
      symbolFilePath = item[strings.symbolFile] as String;
×
183
    }
184
    final symbolFile =
185
        loadSymbolFile(symbolFilePath, configFileName, packageConfig);
1✔
186
    final formatVersion = symbolFile[strings.formatVersion] as String;
1✔
187
    if (formatVersion.split('.')[0] !=
3✔
188
        strings.symbolFileFormatVersion.split('.')[0]) {
2✔
189
      _logger.severe(
×
190
          'Incompatible format versions for file $symbolFilePath: ${strings.symbolFileFormatVersion}(ours), $formatVersion(theirs).');
×
191
      exit(1);
×
192
    }
193
    final uniqueNamer = UniqueNamer(libraryImports.keys
2✔
194
        .followedBy([strings.defaultSymbolFileImportPrefix]).toSet());
3✔
195
    for (final file in (symbolFile[strings.files] as YamlMap).keys) {
3✔
196
      final existingImports =
197
          libraryImports.values.where((element) => element.importPath == file);
2✔
198
      if (existingImports.isEmpty) {
1✔
199
        final name =
200
            uniqueNamer.makeUnique(strings.defaultSymbolFileImportPrefix);
1✔
201
        libraryImports[name] = LibraryImport(name, file as String);
2✔
202
      }
203
      final libraryImport = libraryImports.values.firstWhere(
2✔
204
        (element) => element.importPath == file,
3✔
205
      );
206
      loadImportedTypes(
1✔
207
          symbolFile[strings.files][file] as YamlMap, resultMap, libraryImport);
2✔
208
    }
209
  }
210
  return resultMap;
211
}
212

213
bool symbolFileImportValidator(List<String> name, dynamic yamlConfig) {
×
214
  if (!checkType<YamlList>(name, yamlConfig)) {
×
215
    return false;
216
  }
217
  var result = true;
218
  (yamlConfig as YamlList).asMap().forEach((idx, value) {
×
219
    if (value is YamlMap) {
×
220
      if (!value.keys.contains(strings.symbolFile)) {
×
221
        result = false;
222
        _logger
×
223
            .severe('Key $name -> $idx -> ${strings.symbolFile} is required.');
×
224
      }
225
      for (final key in value.keys) {
×
226
        if (key == strings.symbolFile) {
×
227
          if (!checkType<String>(
×
228
              [...name, idx.toString(), key as String], value[key])) {
×
229
            result = false;
230
          }
231
        } else {
232
          result = false;
233
          _logger.severe('Unknown key : $name -> $idx -> $key.');
×
234
        }
235
      }
236
    } else if (value is! String) {
×
237
      result = false;
238
      _logger.severe('Expected $name -> $idx should be a String or Map.');
×
239
    }
240
  });
241
  return result;
242
}
243

244
Map<String, List<String>> typeMapExtractor(dynamic yamlConfig) {
3✔
245
  // Key - type_name, Value - [lib, cType, dartType].
246
  final resultMap = <String, List<String>>{};
3✔
247
  final typeMap = yamlConfig as YamlMap?;
248
  if (typeMap != null) {
249
    for (final typeName in typeMap.keys) {
6✔
250
      final typeConfigItem = typeMap[typeName] as YamlMap;
3✔
251
      resultMap[typeName as String] = [
6✔
252
        typeConfigItem[strings.lib] as String,
3✔
253
        typeConfigItem[strings.cType] as String,
3✔
254
        typeConfigItem[strings.dartType] as String,
3✔
255
      ];
256
    }
257
  }
258
  return resultMap;
259
}
260

261
bool typeMapValidator(List<String> name, dynamic yamlConfig) {
×
262
  if (!checkType<YamlMap>(name, yamlConfig)) {
×
263
    return false;
264
  }
265
  var result = true;
266
  for (final key in (yamlConfig as YamlMap).keys) {
×
267
    if (!checkType<YamlMap>([...name, key as String], yamlConfig[key])) {
×
268
      return false;
269
    }
270
    final lib = (yamlConfig[key] as YamlMap).containsKey(strings.lib);
×
271
    if (!lib) {
272
      _logger.severe("Key '${strings.lib}' in $name -> $key is required.");
×
273
      result = false;
274
    }
275
    final cType = (yamlConfig[key] as YamlMap).containsKey(strings.cType);
×
276
    if (!cType) {
277
      _logger.severe("Key '${strings.cType}' in $name -> $key is required.");
×
278
      result = false;
279
    }
280
    final dartType = (yamlConfig[key] as YamlMap).containsKey(strings.dartType);
×
281
    if (!dartType) {
282
      _logger.severe("Key '${strings.dartType}' in $name -> $key is required.");
×
283
      result = false;
284
    }
285
  }
286
  return result;
287
}
288

289
Map<String, String> stringStringMapExtractor(dynamic yamlConfig) {
×
290
  final resultMap = <String, String>{};
×
291
  final inputMap = yamlConfig as YamlMap?;
292
  if (inputMap != null) {
293
    for (final key in inputMap.keys) {
×
294
      resultMap[key as String] = inputMap[key] as String;
×
295
    }
296
  }
297
  return resultMap;
298
}
299

300
bool stringStringMapValidator(List<String> name, dynamic yamlConfig) {
×
301
  if (!checkType<YamlMap>(name, yamlConfig)) {
×
302
    return false;
303
  }
304
  for (final key in (yamlConfig as YamlMap).keys) {
×
305
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
×
306
      return false;
307
    }
308
  }
309
  return true;
310
}
311

312
Map<String, ImportedType> makeImportTypeMapping(
50✔
313
    Map<String, List<String>> rawTypeMappings,
314
    Map<String, LibraryImport> libraryImportsMap) {
315
  final typeMappings = <String, ImportedType>{};
50✔
316
  for (final key in rawTypeMappings.keys) {
53✔
317
    final lib = rawTypeMappings[key]![0];
6✔
318
    final cType = rawTypeMappings[key]![1];
6✔
319
    final dartType = rawTypeMappings[key]![2];
6✔
320
    if (strings.predefinedLibraryImports.containsKey(lib)) {
6✔
321
      typeMappings[key] =
3✔
322
          ImportedType(strings.predefinedLibraryImports[lib]!, cType, dartType);
9✔
323
    } else if (libraryImportsMap.containsKey(lib)) {
1✔
324
      typeMappings[key] =
1✔
325
          ImportedType(libraryImportsMap[lib]!, cType, dartType);
2✔
326
    } else {
327
      throw Exception("Please declare $lib under library-imports.");
×
328
    }
329
  }
330
  return typeMappings;
331
}
332

333
Type makePointerToType(Type type, int pointerCount) {
1✔
334
  for (var i = 0; i < pointerCount; i++) {
2✔
335
    type = PointerType(type);
1✔
336
  }
337
  return type;
338
}
339

340
String makePostfixFromRawVarArgType(List<String> rawVarArgType) {
1✔
341
  return rawVarArgType
342
      .map((e) => e
2✔
343
          .replaceAll('*', 'Ptr')
1✔
344
          .replaceAll(RegExp(r'_t$'), '')
2✔
345
          .replaceAll(' ', '')
1✔
346
          .replaceAll(RegExp('[^A-Za-z0-9_]'), ''))
2✔
347
      .map((e) => e.length > 1 ? '${e[0].toUpperCase()}${e.substring(1)}' : e)
8✔
348
      .join('');
1✔
349
}
350

351
Type makeTypeFromRawVarArgType(
1✔
352
    String rawVarArgType, Map<String, LibraryImport> libraryImportsMap) {
353
  Type baseType;
354
  var rawBaseType = rawVarArgType.trim();
1✔
355
  // Split the raw type based on pointer usage. E.g -
356
  // int => [int]
357
  // char* => [char,*]
358
  // ffi.Hello ** => [ffi.Hello,**]
359
  final typeStringRegexp = RegExp(r'([a-zA-Z0-9_\s\.]+)(\**)$');
1✔
360
  if (!typeStringRegexp.hasMatch(rawBaseType)) {
1✔
361
    throw Exception('Cannot parse variadic argument type - $rawVarArgType.');
×
362
  }
363
  final regExpMatch = typeStringRegexp.firstMatch(rawBaseType)!;
1✔
364
  final groups = regExpMatch.groups([1, 2]);
2✔
365
  rawBaseType = groups[0]!;
1✔
366
  // Handle basic supported types.
367
  if (cxTypeKindToImportedTypes.containsKey(rawBaseType)) {
2✔
368
    baseType = cxTypeKindToImportedTypes[rawBaseType]!;
2✔
369
  } else if (supportedTypedefToImportedType.containsKey(rawBaseType)) {
2✔
370
    baseType = supportedTypedefToImportedType[rawBaseType]!;
×
371
  } else if (suportedTypedefToSuportedNativeType.containsKey(rawBaseType)) {
2✔
372
    baseType = NativeType(suportedTypedefToSuportedNativeType[rawBaseType]!);
×
373
  } else {
374
    // Use library import if specified (E.g - ffi.UintPtr or custom.MyStruct)
375
    final rawVarArgTypeSplit = rawBaseType.split('.');
1✔
376
    if (rawVarArgTypeSplit.length == 1) {
2✔
377
      final typeName = rawVarArgTypeSplit[0].replaceAll(' ', '');
2✔
378
      baseType = SelfImportedType(typeName, typeName);
1✔
379
    } else if (rawVarArgTypeSplit.length == 2) {
×
380
      final lib = rawVarArgTypeSplit[0];
×
381
      final libraryImport = strings.predefinedLibraryImports[lib] ??
×
382
          libraryImportsMap[rawVarArgTypeSplit[0]];
×
383
      if (libraryImport == null) {
384
        throw Exception('Please declare $lib in library-imports.');
×
385
      }
386
      final typeName = rawVarArgTypeSplit[1].replaceAll(' ', '');
×
387
      baseType = ImportedType(libraryImport, typeName, typeName);
×
388
    } else {
389
      throw Exception(
×
390
          'Invalid type $rawVarArgType : Expected 0 or 1 .(dot) separators.');
×
391
    }
392
  }
393

394
  // Handle pointers
395
  final pointerCount = groups[1]!.length;
2✔
396
  return makePointerToType(baseType, pointerCount);
1✔
397
}
398

399
Map<String, List<VarArgFunction>> makeVarArgFunctionsMapping(
50✔
400
    Map<String, List<RawVarArgFunction>> rawVarArgMappings,
401
    Map<String, LibraryImport> libraryImportsMap) {
402
  final mappings = <String, List<VarArgFunction>>{};
50✔
403
  for (final key in rawVarArgMappings.keys) {
51✔
404
    final varArgList = <VarArgFunction>[];
1✔
405
    for (final rawVarArg in rawVarArgMappings[key]!) {
3✔
406
      var postfix = rawVarArg.postfix ?? '';
1✔
407
      final types = <Type>[];
1✔
408
      for (final rva in rawVarArg.rawTypeStrings) {
2✔
409
        types.add(makeTypeFromRawVarArgType(rva, libraryImportsMap));
2✔
410
      }
411
      if (postfix.isEmpty) {
1✔
412
        if (rawVarArgMappings[key]!.length == 1) {
3✔
413
          postfix = '';
414
        } else {
415
          postfix = makePostfixFromRawVarArgType(rawVarArg.rawTypeStrings);
2✔
416
        }
417
      }
418
      // Extract postfix from config and/or deduce from var names.
419
      varArgList.add(VarArgFunction(postfix, types));
2✔
420
    }
421
    mappings[key] = varArgList;
1✔
422
  }
423
  return mappings;
424
}
425

426
final _quoteMatcher = RegExp(r'''^["'](.*)["']$''', dotAll: true);
18✔
427
final _cmdlineArgMatcher = RegExp(r'''['"](\\"|[^"])*?['"]|[^ ]+''');
18✔
428
List<String> compilerOptsToList(String compilerOpts) {
6✔
429
  final list = <String>[];
6✔
430
  _cmdlineArgMatcher.allMatches(compilerOpts).forEach((element) {
24✔
431
    var match = element.group(0);
6✔
432
    if (match != null) {
433
      if (quiver.matchesFull(_quoteMatcher, match)) {
12✔
434
        match = _quoteMatcher.allMatches(match).first.group(1)!;
4✔
435
      }
436
      list.add(match);
6✔
437
    }
438
  });
439

440
  return list;
441
}
442

443
List<String> compilerOptsExtractor(dynamic value) {
5✔
444
  if (value is String) {
5✔
445
    return compilerOptsToList(value);
4✔
446
  }
447

448
  final list = <String>[];
1✔
449
  for (final el in (value as YamlList)) {
2✔
450
    if (el is String) {
1✔
451
      list.addAll(compilerOptsToList(el));
2✔
452
    }
453
  }
454
  return list;
455
}
456

457
bool compilerOptsValidator(List<String> name, dynamic value) {
×
458
  if (value is String || value is YamlList) {
×
459
    return true;
460
  } else {
461
    _logger.severe('Expected $name to be a String or List of String.');
×
462
    return false;
463
  }
464
}
465

466
CompilerOptsAuto compilerOptsAutoExtractor(dynamic value) {
1✔
467
  return CompilerOptsAuto(
1✔
468
    macIncludeStdLib: getKeyValueFromYaml(
1✔
469
      [strings.macos, strings.includeCStdLib],
1✔
470
      value as YamlMap,
471
    ) as bool?,
472
  );
473
}
474

475
bool compilerOptsAutoValidator(List<String> name, dynamic value) {
×
476
  var result = true;
477

478
  if (!checkType<YamlMap>(name, value)) {
×
479
    return false;
480
  }
481

482
  for (final oskey in (value as YamlMap).keys) {
×
483
    if (oskey == strings.macos) {
×
484
      if (!checkType<YamlMap>([...name, oskey as String], value[oskey])) {
×
485
        return false;
486
      }
487

488
      for (final inckey in (value[oskey] as YamlMap).keys) {
×
489
        if (inckey == strings.includeCStdLib) {
×
490
          if (!checkType<bool>(
×
491
              [...name, oskey, inckey as String], value[oskey][inckey])) {
×
492
            result = false;
493
          }
494
        } else {
495
          _logger.severe("Unknown key '$inckey' in '$name -> $oskey.");
×
496
          result = false;
497
        }
498
      }
499
    } else {
500
      _logger.severe("Unknown key '$oskey' in '$name'.");
×
501
      result = false;
502
    }
503
  }
504
  return result;
505
}
506

507
Headers headersExtractor(dynamic yamlConfig, String? configFilename) {
50✔
508
  final entryPoints = <String>[];
50✔
509
  final includeGlobs = <quiver.Glob>[];
50✔
510
  for (final key in (yamlConfig as YamlMap).keys) {
100✔
511
    if (key == strings.entryPoints) {
50✔
512
      for (final h in (yamlConfig[key] as YamlList)) {
150✔
513
        final headerGlob = _normalizePath(h as String, configFilename);
50✔
514
        // Add file directly to header if it's not a Glob but a File.
515
        if (File(headerGlob).existsSync()) {
100✔
516
          final osSpecificPath = headerGlob;
517
          entryPoints.add(osSpecificPath);
35✔
518
          _logger.fine('Adding header/file: $headerGlob');
105✔
519
        } else {
520
          final glob = Glob(headerGlob);
15✔
521
          for (final file in glob.listFileSystemSync(const LocalFileSystem(),
15✔
522
              followLinks: true)) {
×
523
            final fixedPath = file.path;
×
524
            entryPoints.add(fixedPath);
×
525
            _logger.fine('Adding header/file: $fixedPath');
×
526
          }
527
        }
528
      }
529
    }
530
    if (key == strings.includeDirectives) {
50✔
531
      for (final h in (yamlConfig[key] as YamlList)) {
30✔
532
        final headerGlob = h as String;
533
        final fixedGlob = _normalizePath(headerGlob, configFilename);
10✔
534
        includeGlobs.add(quiver.Glob(fixedGlob));
20✔
535
      }
536
    }
537
  }
538
  return Headers(
50✔
539
    entryPoints: entryPoints,
540
    includeFilter: GlobHeaderFilter(
50✔
541
      includeGlobs: includeGlobs,
542
    ),
543
  );
544
}
545

546
bool headersValidator(List<String> name, dynamic value) {
×
547
  if (!checkType<YamlMap>(name, value)) {
×
548
    return false;
549
  }
550
  if (!(value as YamlMap).containsKey(strings.entryPoints)) {
×
551
    _logger.severe("Required '$name -> ${strings.entryPoints}'.");
×
552
    return false;
553
  } else {
554
    for (final key in value.keys) {
×
555
      if (key == strings.entryPoints || key == strings.includeDirectives) {
×
556
        if (!checkType<YamlList>([...name, key as String], value[key])) {
×
557
          return false;
558
        }
559
      } else {
560
        _logger.severe("Unknown key '$key' in '$name'.");
×
561
        return false;
562
      }
563
    }
564
    return true;
565
  }
566
}
567

568
/// Returns location of dynamic library by searching default locations. Logs
569
/// error and throws an Exception if not found.
570
String findDylibAtDefaultLocations() {
50✔
571
  String? k;
572
  if (Platform.isLinux) {
50✔
573
    for (final l in strings.linuxDylibLocations) {
×
574
      k = findLibclangDylib(l);
×
575
      if (k != null) return k;
576
    }
577
    Process.runSync('ldconfig', ['-p']);
×
578
    final ldConfigResult = Process.runSync('ldconfig', ['-p']);
×
579
    if (ldConfigResult.exitCode == 0) {
×
580
      final lines = (ldConfigResult.stdout as String).split('\n');
×
581
      final paths = [
×
582
        for (final line in lines)
×
583
          if (line.contains('libclang')) line.split(' => ')[1],
×
584
      ];
585
      for (final location in paths) {
×
586
        if (File(location).existsSync()) {
×
587
          return location;
588
        }
589
      }
590
    }
591
  } else if (Platform.isWindows) {
50✔
592
    final dylibLocations = strings.windowsDylibLocations.toList();
×
593
    final userHome = Platform.environment['USERPROFILE'];
×
594
    if (userHome != null) {
595
      dylibLocations
596
          .add(p.join(userHome, 'scoop', 'apps', 'llvm', 'current', 'bin'));
×
597
    }
598
    for (final l in dylibLocations) {
×
599
      k = findLibclangDylib(l);
×
600
      if (k != null) return k;
601
    }
602
  } else if (Platform.isMacOS) {
50✔
603
    for (final l in strings.macOsDylibLocations) {
100✔
604
      k = findLibclangDylib(l);
50✔
605
      if (k != null) return k;
606
    }
607
    final findLibraryResult =
608
        Process.runSync('xcodebuild', ['-find-library', 'libclang.dylib']);
×
609
    if (findLibraryResult.exitCode == 0) {
×
610
      final location = (findLibraryResult.stdout as String).split('\n').first;
×
611
      if (File(location).existsSync()) {
×
612
        return location;
613
      }
614
    }
615
    final xcodePathResult = Process.runSync('xcode-select', ['-print-path']);
×
616
    if (xcodePathResult.exitCode == 0) {
×
617
      final xcodePath = (xcodePathResult.stdout as String).split('\n').first;
×
618
      final location =
619
          p.join(xcodePath, strings.xcodeDylibLocation, strings.dylibFileName);
×
620
      if (File(location).existsSync()) {
×
621
        return location;
622
      }
623
    }
624
  } else {
625
    throw Exception('Unsupported Platform.');
×
626
  }
627

628
  _logger.severe("Couldn't find dynamic library in default locations.");
×
629
  _logger.severe(
×
630
      "Please supply one or more path/to/llvm in ffigen's config under the key '${strings.llvmPath}'.");
×
631
  throw Exception("Couldn't find dynamic library in default locations.");
×
632
}
633

634
String? findLibclangDylib(String parentFolder) {
50✔
635
  final location = p.join(parentFolder, strings.dylibFileName);
100✔
636
  if (File(location).existsSync()) {
100✔
637
    return location;
638
  } else {
639
    return null;
640
  }
641
}
642

643
String llvmPathExtractor(dynamic value) {
×
644
  // Extract libclang's dylib from user specified paths.
645
  for (final path in (value as YamlList)) {
×
646
    if (path is! String) continue;
×
647
    final dylibPath =
648
        findLibclangDylib(p.join(path, strings.dynamicLibParentName));
×
649
    if (dylibPath != null) {
650
      _logger.fine('Found dynamic library at: $dylibPath');
×
651
      return dylibPath;
652
    }
653
    // Check if user has specified complete path to dylib.
654
    final completeDylibPath = path;
655
    if (p.extension(completeDylibPath).isNotEmpty &&
×
656
        File(completeDylibPath).existsSync()) {
×
657
      _logger.info(
×
658
          'Using complete dylib path: $completeDylibPath from llvm-path.');
×
659
      return completeDylibPath;
660
    }
661
  }
662
  _logger.fine(
×
663
      "Couldn't find dynamic library under paths specified by ${strings.llvmPath}.");
×
664
  // Extract path from default locations.
665
  try {
666
    final res = findDylibAtDefaultLocations();
×
667
    return res;
668
  } catch (e) {
669
    _logger.severe(
×
670
        "Couldn't find ${p.join(strings.dynamicLibParentName, strings.dylibFileName)} in specified locations.");
×
671
    exit(1);
×
672
  }
673
}
674

675
bool llvmPathValidator(List<String> name, dynamic value) {
×
676
  if (!checkType<YamlList>(name, value)) {
×
677
    return false;
678
  }
679
  return true;
680
}
681

682
OutputConfig outputExtractor(
50✔
683
    dynamic value, String? configFilename, PackageConfig? packageConfig) {
684
  if (value is String) {
50✔
685
    return OutputConfig(_normalizePath(value, configFilename), null);
100✔
686
  }
687
  value = value as YamlMap;
688
  return OutputConfig(
1✔
689
    _normalizePath((value)[strings.bindings] as String, configFilename),
2✔
690
    value.containsKey(strings.symbolFile)
1✔
691
        ? symbolFileOutputExtractor(
1✔
692
            value[strings.symbolFile], configFilename, packageConfig)
1✔
693
        : null,
694
  );
695
}
696

697
bool outputValidator(List<String> name, dynamic value) {
×
698
  if (value is String) {
×
699
    return true;
700
  } else if (value is YamlMap) {
×
701
    final keys = value.keys;
×
702
    var result = true;
703
    for (final key in keys) {
×
704
      if (key == strings.bindings) {
×
705
        if (!checkType<String>([...name, key as String], value[key])) {
×
706
          result = false;
707
        }
708
      } else if (key == strings.symbolFile) {
×
709
        result = symbolFileOutputValidator(
×
710
            [...name, strings.symbolFile], value[key]);
×
711
      } else {
712
        result = false;
713
        _logger.severe("Unknown key '$key' in '$name'.");
×
714
      }
715
    }
716
    return result;
717
  } else {
718
    _logger.severe(
×
719
        "Expected value of key '${name.join(' -> ')}' to be a String or Map.");
×
720
    return false;
721
  }
722
}
723

724
SymbolFile symbolFileOutputExtractor(
1✔
725
    dynamic value, String? configFilename, PackageConfig? packageConfig) {
726
  value = value as YamlMap;
727
  var output = value[strings.output] as String;
1✔
728
  if (Uri.parse(output).scheme != "package") {
3✔
729
    _logger.warning(
×
730
        'Consider using a Package Uri for ${strings.symbolFile} -> ${strings.output}: $output so that external packages can use it.');
×
731
    output = _normalizePath(output, configFilename);
×
732
  } else {
733
    output = packageConfig!.resolve(Uri.parse(output))!.toFilePath();
3✔
734
  }
735
  final importPath = value[strings.importPath] as String;
1✔
736
  if (Uri.parse(importPath).scheme != "package") {
3✔
737
    _logger.warning(
×
738
        'Consider using a Package Uri for ${strings.symbolFile} -> ${strings.importPath}: $importPath so that external packages can use it.');
×
739
  }
740
  return SymbolFile(importPath, output);
1✔
741
}
742

743
bool symbolFileOutputValidator(List<String> name, dynamic value) {
×
744
  if (!checkType<YamlMap>(name, value)) {
×
745
    return false;
746
  }
747
  if (!(value as YamlMap).containsKey(strings.output)) {
×
748
    _logger.severe("Required '$name -> ${strings.output}'.");
×
749
    return false;
750
  }
751
  if (!(value).containsKey(strings.importPath)) {
×
752
    _logger.severe("Required '$name -> ${strings.importPath}'.");
×
753
    return false;
754
  }
755
  for (final key in value.keys) {
×
756
    if (key == strings.output || key == strings.importPath) {
×
757
      if (!checkType<String>([...name, key as String], value[key])) {
×
758
        return false;
759
      }
760
    } else {
761
      _logger.severe("Unknown key '$key' in '$name'.");
×
762
      return false;
763
    }
764
  }
765
  return true;
766
}
767

768
Language languageExtractor(dynamic value) {
×
769
  if (value == strings.langC) {
×
770
    return Language.c;
771
  } else if (value == strings.langObjC) {
×
772
    return Language.objc;
773
  }
774
  return Language.c;
775
}
776

777
bool languageValidator(List<String> name, dynamic value) {
×
778
  if (value is String) {
×
779
    if (value == strings.langC) {
×
780
      return true;
781
    }
782
    if (value == strings.langObjC) {
×
783
      _logger.severe('Objective C support is EXPERIMENTAL. The API may change '
×
784
          'in a breaking way without notice.');
785
      return true;
786
    }
787
    _logger.severe("'$name' must be one of the following - "
×
788
        "{${strings.langC}, ${strings.langObjC}}");
789
    return false;
790
  }
791
  _logger.severe("Expected value of key '$name' to be a String.");
×
792
  return false;
793
}
794

795
/// Returns true if [str] is not a full name.
796
///
797
/// E.g `abc` is a full name, `abc.*` is not.
798
bool isFullDeclarationName(String str) =>
27✔
799
    quiver.matchesFull(RegExp('[a-zA-Z_0-9]*'), str);
54✔
800

801
Includer extractIncluderFromYaml(dynamic yamlMap) {
50✔
802
  final includeMatchers = <RegExp>[],
50✔
803
      includeFull = <String>{},
804
      excludeMatchers = <RegExp>[],
50✔
805
      excludeFull = <String>{};
806

807
  final include = (yamlMap[strings.include] as YamlList?)?.cast<String>();
72✔
808
  if (include != null) {
809
    if (include.isEmpty) {
22✔
810
      return Includer.excludeByDefault();
×
811
    }
812
    for (final str in include) {
44✔
813
      if (isFullDeclarationName(str)) {
22✔
814
        includeFull.add(str);
22✔
815
      } else {
816
        includeMatchers.add(RegExp(str, dotAll: true));
2✔
817
      }
818
    }
819
  }
820

821
  final exclude = (yamlMap[strings.exclude] as YamlList?)?.cast<String>();
56✔
822
  if (exclude != null) {
823
    for (final str in exclude) {
12✔
824
      if (isFullDeclarationName(str)) {
6✔
825
        excludeFull.add(str);
6✔
826
      } else {
827
        excludeMatchers.add(RegExp(str, dotAll: true));
×
828
      }
829
    }
830
  }
831

832
  return Includer(
50✔
833
    includeMatchers: includeMatchers,
834
    includeFull: includeFull,
835
    excludeMatchers: excludeMatchers,
836
    excludeFull: excludeFull,
837
  );
838
}
839

840
Map<String, List<RawVarArgFunction>> varArgFunctionConfigExtractor(
1✔
841
    dynamic yamlMap) {
842
  final result = <String, List<RawVarArgFunction>>{};
1✔
843
  final configMap = (yamlMap as YamlMap);
844
  for (final key in configMap.keys) {
2✔
845
    final List<RawVarArgFunction> vafuncs = [];
1✔
846
    for (final rawVaFunc in (configMap[key] as YamlList)) {
3✔
847
      if (rawVaFunc is YamlList) {
1✔
848
        vafuncs.add(RawVarArgFunction(null, rawVaFunc.cast()));
3✔
849
      } else if (rawVaFunc is YamlMap) {
1✔
850
        vafuncs.add(RawVarArgFunction(rawVaFunc[strings.postfix] as String?,
3✔
851
            (rawVaFunc[strings.types] as YamlList).cast()));
2✔
852
      } else {
853
        throw Exception("Unexpected type in variadic-argument config.");
×
854
      }
855
    }
856
    result[key as String] = vafuncs;
1✔
857
  }
858

859
  return result;
860
}
861

862
bool varArgFunctionConfigValidator(List<String> name, dynamic value) {
×
863
  if (!checkType<YamlMap>(name, value)) {
×
864
    return false;
865
  }
866
  var result = true;
867
  for (final key in (value as YamlMap).keys) {
×
868
    final list = value[key as String];
×
869
    if (!checkType<YamlList>([...name, key], list)) {
×
870
      result = false;
871
      continue;
872
    }
873
    (list as YamlList).asMap().forEach((idx, subList) {
×
874
      if (subList is YamlMap) {
×
875
        if (!subList.containsKey(strings.types)) {
×
876
          result = false;
877
          _logger.severe('Missing required key - ${[
×
878
            ...name,
879
            key,
×
880
            idx.toString(),
×
881
            strings.types
×
882
          ].join(" -> ")}');
×
883
        }
884
        subList.forEach((subkey, subvalue) {
×
885
          subkey = subkey as String;
886
          if (subkey == strings.postfix) {
×
887
            if (!checkType<String>(
×
888
                [...name, key, idx.toString(), subkey], subvalue)) {
×
889
              result = false;
890
            }
891
          } else if (subkey == strings.types) {
×
892
            if (!checkType<YamlList>(
×
893
                [...name, key, idx.toString(), subkey], subvalue)) {
×
894
              result = false;
895
            }
896
          } else {
897
            result = false;
898
            _logger.severe('Unknown key - ${[
×
899
              ...name,
900
              key,
×
901
              idx.toString(),
×
902
              subkey
×
903
            ].join(" -> ")}');
×
904
          }
905
        });
906
      } else if (subList is! YamlList) {
×
907
        result = false;
908
        _logger.severe('Expected ${[
×
909
          ...name,
910
          key,
×
911
          idx
×
912
        ].join(" -> ")} to be a List or a Map.');
×
913
      }
914
    });
915
  }
916
  return result;
917
}
918

919
Declaration declarationConfigExtractor(dynamic yamlMap) {
50✔
920
  final renamePatterns = <RegExpRenamer>[];
50✔
921
  final renameFull = <String, String>{};
50✔
922
  final memberRenamePatterns = <RegExpMemberRenamer>[];
50✔
923
  final memberRenamerFull = <String, Renamer>{};
50✔
924

925
  final includer = extractIncluderFromYaml(yamlMap);
50✔
926

927
  Includer? symbolIncluder;
928
  if (yamlMap[strings.symbolAddress] != null) {
50✔
929
    symbolIncluder = extractIncluderFromYaml(yamlMap[strings.symbolAddress]);
6✔
930
  }
931

932
  final rename = (yamlMap[strings.rename] as YamlMap?)?.cast<String, String>();
52✔
933

934
  if (rename != null) {
935
    for (final str in rename.keys) {
4✔
936
      if (isFullDeclarationName(str)) {
2✔
937
        renameFull[str] = rename[str]!;
2✔
938
      } else {
939
        renamePatterns
940
            .add(RegExpRenamer(RegExp(str, dotAll: true), rename[str]!));
8✔
941
      }
942
    }
943
  }
944

945
  final memberRename =
946
      (yamlMap[strings.memberRename] as YamlMap?)?.cast<String, YamlMap>();
51✔
947

948
  if (memberRename != null) {
949
    for (final decl in memberRename.keys) {
2✔
950
      final renamePatterns = <RegExpRenamer>[];
1✔
951
      final renameFull = <String, String>{};
1✔
952

953
      final memberRenameMap = memberRename[decl]!.cast<String, String>();
2✔
954
      for (final member in memberRenameMap.keys) {
2✔
955
        if (isFullDeclarationName(member)) {
1✔
956
          renameFull[member] = memberRenameMap[member]!;
2✔
957
        } else {
958
          renamePatterns.add(RegExpRenamer(
2✔
959
              RegExp(member, dotAll: true), memberRenameMap[member]!));
2✔
960
        }
961
      }
962
      if (isFullDeclarationName(decl)) {
1✔
963
        memberRenamerFull[decl] = Renamer(
2✔
964
          renameFull: renameFull,
965
          renamePatterns: renamePatterns,
966
        );
967
      } else {
968
        memberRenamePatterns.add(
1✔
969
          RegExpMemberRenamer(
1✔
970
            RegExp(decl, dotAll: true),
1✔
971
            Renamer(
1✔
972
              renameFull: renameFull,
973
              renamePatterns: renamePatterns,
974
            ),
975
          ),
976
        );
977
      }
978
    }
979
  }
980

981
  return Declaration(
50✔
982
    includer: includer,
983
    renamer: Renamer(
50✔
984
      renameFull: renameFull,
985
      renamePatterns: renamePatterns,
986
    ),
987
    memberRenamer: MemberRenamer(
50✔
988
      memberRenameFull: memberRenamerFull,
989
      memberRenamePattern: memberRenamePatterns,
990
    ),
991
    symbolAddressIncluder: symbolIncluder,
992
  );
993
}
994

995
bool declarationConfigValidator(List<String> name, dynamic value) {
×
996
  var result = true;
997
  if (value is YamlMap) {
×
998
    for (final key in value.keys) {
×
999
      if (key == strings.include || key == strings.exclude) {
×
1000
        if (!checkType<YamlList>([...name, key as String], value[key])) {
×
1001
          result = false;
1002
        }
1003
      } else if (key == strings.rename) {
×
1004
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
×
1005
          result = false;
1006
        } else {
1007
          for (final subkey in (value[key] as YamlMap).keys) {
×
1008
            if (!checkType<String>(
×
1009
                [...name, key, subkey as String], value[key][subkey])) {
×
1010
              result = false;
1011
            }
1012
          }
1013
        }
1014
      } else if (key == strings.memberRename) {
×
1015
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
×
1016
          result = false;
1017
        } else {
1018
          for (final declNameKey in (value[key] as YamlMap).keys) {
×
1019
            if (!checkType<YamlMap>([...name, key, declNameKey as String],
×
1020
                value[key][declNameKey])) {
×
1021
              result = false;
1022
            } else {
1023
              for (final memberNameKey
1024
                  in ((value[key] as YamlMap)[declNameKey] as YamlMap).keys) {
×
1025
                if (!checkType<String>([
×
1026
                  ...name,
1027
                  key,
×
1028
                  declNameKey,
×
1029
                  memberNameKey as String,
×
1030
                ], value[key][declNameKey][memberNameKey])) {
×
1031
                  result = false;
1032
                }
1033
              }
1034
            }
1035
          }
1036
        }
1037
      } else if (key == strings.symbolAddress) {
×
1038
        if (!checkType<YamlMap>([...name, key as String], value[key])) {
×
1039
          result = false;
1040
        } else {
1041
          for (final subkey in (value[key] as YamlMap).keys) {
×
1042
            if (subkey == strings.include || subkey == strings.exclude) {
×
1043
              if (!checkType<YamlList>(
×
1044
                  [...name, key, subkey as String], value[key][subkey])) {
×
1045
                result = false;
1046
              }
1047
            } else {
1048
              _logger.severe("Unknown key '$subkey' in '$name -> $key'.");
×
1049
              result = false;
1050
            }
1051
          }
1052
        }
1053
      }
1054
    }
1055
  } else {
1056
    _logger.severe("Expected value '$name' to be a Map.");
×
1057
    result = false;
1058
  }
1059
  return result;
1060
}
1061

1062
Includer exposeFunctionTypeExtractor(dynamic value) =>
×
1063
    extractIncluderFromYaml(value);
×
1064

1065
bool exposeFunctionTypeValidator(List<String> name, dynamic value) {
×
1066
  var result = true;
1067

1068
  if (!checkType<YamlMap>(name, value)) {
×
1069
    result = false;
1070
  } else {
1071
    final mp = value as YamlMap;
1072
    for (final key in mp.keys) {
×
1073
      if (key == strings.include || key == strings.exclude) {
×
1074
        if (!checkType<YamlList>([...name, key as String], value[key])) {
×
1075
          result = false;
1076
        }
1077
      } else {
1078
        _logger.severe("Unknown subkey '$key' in '$name'.");
×
1079
        result = false;
1080
      }
1081
    }
1082
  }
1083

1084
  return result;
1085
}
1086

1087
Includer leafFunctionExtractor(dynamic value) => extractIncluderFromYaml(value);
×
1088

1089
bool leafFunctionValidator(List<String> name, dynamic value) {
×
1090
  var result = true;
1091

1092
  if (!checkType<YamlMap>(name, value)) {
×
1093
    result = false;
1094
  } else {
1095
    final mp = value as YamlMap;
1096
    for (final key in mp.keys) {
×
1097
      if (key == strings.include || key == strings.exclude) {
×
1098
        if (!checkType<YamlList>([...name, key as String], value[key])) {
×
1099
          result = false;
1100
        }
1101
      } else {
1102
        _logger.severe("Unknown subkey '$key' in '$name'.");
×
1103
        result = false;
1104
      }
1105
    }
1106
  }
1107

1108
  return result;
1109
}
1110

1111
SupportedNativeType nativeSupportedType(int value, {bool signed = true}) {
×
1112
  switch (value) {
1113
    case 1:
×
1114
      return signed ? SupportedNativeType.Int8 : SupportedNativeType.Uint8;
1115
    case 2:
×
1116
      return signed ? SupportedNativeType.Int16 : SupportedNativeType.Uint16;
1117
    case 4:
×
1118
      return signed ? SupportedNativeType.Int32 : SupportedNativeType.Uint32;
1119
    case 8:
×
1120
      return signed ? SupportedNativeType.Int64 : SupportedNativeType.Uint64;
1121
    default:
1122
      throw Exception(
×
1123
          'Unsupported value given to sizemap, Allowed values for sizes are: 1, 2, 4, 8');
1124
  }
1125
}
1126

1127
String stringExtractor(dynamic value) => value as String;
×
1128

1129
bool nonEmptyStringValidator(List<String> name, dynamic value) {
×
1130
  if (value is String && value.isNotEmpty) {
×
1131
    return true;
1132
  } else {
1133
    _logger.severe("Expected value of key '$name' to be a non-empty String.");
×
1134
    return false;
1135
  }
1136
}
1137

1138
bool dartClassNameValidator(List<String> name, dynamic value) {
×
1139
  if (value is String &&
×
1140
      quiver.matchesFull(RegExp('[a-zA-Z]+[_a-zA-Z0-9]*'), value)) {
×
1141
    return true;
1142
  } else {
1143
    _logger.severe(
×
1144
        "Expected value of key '$name' to be a valid public class name.");
×
1145
    return false;
1146
  }
1147
}
1148

1149
CommentType commentExtractor(dynamic value) {
×
1150
  if (value is bool) {
×
1151
    if (value) {
1152
      return CommentType.def();
×
1153
    } else {
1154
      return CommentType.none();
×
1155
    }
1156
  }
1157
  final ct = CommentType.def();
×
1158
  if (value is YamlMap) {
×
1159
    for (final key in value.keys) {
×
1160
      if (key == strings.style) {
×
1161
        if (value[key] == strings.any) {
×
1162
          ct.style = CommentStyle.any;
×
1163
        } else if (value[key] == strings.doxygen) {
×
1164
          ct.style = CommentStyle.doxygen;
×
1165
        }
1166
      } else if (key == strings.length) {
×
1167
        if (value[key] == strings.full) {
×
1168
          ct.length = CommentLength.full;
×
1169
        } else if (value[key] == strings.brief) {
×
1170
          ct.length = CommentLength.brief;
×
1171
        }
1172
      }
1173
    }
1174
  }
1175
  return ct;
1176
}
1177

1178
bool commentValidator(List<String> name, dynamic value) {
×
1179
  if (value is bool) {
×
1180
    return true;
1181
  } else if (value is YamlMap) {
×
1182
    var result = true;
1183
    for (final key in value.keys) {
×
1184
      if (key == strings.style) {
×
1185
        if (value[key] is! String ||
×
1186
            !(value[key] == strings.doxygen || value[key] == strings.any)) {
×
1187
          _logger.severe(
×
1188
              "'$name'>'${strings.style}' must be one of the following - {${strings.doxygen}, ${strings.any}}");
×
1189
          result = false;
1190
        }
1191
      } else if (key == strings.length) {
×
1192
        if (value[key] is! String ||
×
1193
            !(value[key] == strings.brief || value[key] == strings.full)) {
×
1194
          _logger.severe(
×
1195
              "'$name'>'${strings.length}' must be one of the following - {${strings.brief}, ${strings.full}}");
×
1196
          result = false;
1197
        }
1198
      } else {
1199
        _logger.severe("Unknown key '$key' in '$name'.");
×
1200
        result = false;
1201
      }
1202
    }
1203
    return result;
1204
  } else {
1205
    _logger.severe("Expected value of key '$name' to be a bool or a Map.");
×
1206
    return false;
1207
  }
1208
}
1209

1210
CompoundDependencies dependencyOnlyExtractor(dynamic value) {
×
1211
  var result = CompoundDependencies.full;
1212
  if (value == strings.opaqueCompoundDependencies) {
×
1213
    result = CompoundDependencies.opaque;
1214
  }
1215
  return result;
1216
}
1217

1218
bool dependencyOnlyValidator(List<String> name, dynamic value) {
×
1219
  var result = true;
1220
  if (value is! String ||
×
1221
      !(value == strings.fullCompoundDependencies ||
×
1222
          value == strings.opaqueCompoundDependencies)) {
×
1223
    _logger.severe(
×
1224
        "'$name' must be one of the following - {${strings.fullCompoundDependencies}, ${strings.opaqueCompoundDependencies}}");
×
1225
    result = false;
1226
  }
1227
  return result;
1228
}
1229

1230
StructPackingOverride structPackingOverrideExtractor(dynamic value) {
1✔
1231
  final matcherMap = <RegExp, int?>{};
1✔
1232
  for (final key in (value as YamlMap).keys) {
2✔
1233
    matcherMap[RegExp(key as String, dotAll: true)] =
2✔
1234
        strings.packingValuesMap[value[key]];
2✔
1235
  }
1236
  return StructPackingOverride(matcherMap: matcherMap);
1✔
1237
}
1238

1239
bool structPackingOverrideValidator(List<String> name, dynamic value) {
×
1240
  var result = true;
1241

1242
  if (!checkType<YamlMap>([...name], value)) {
×
1243
    result = false;
1244
  } else {
1245
    for (final key in (value as YamlMap).keys) {
×
1246
      if (!(strings.packingValuesMap.keys.contains(value[key]))) {
×
1247
        _logger.severe(
×
1248
            "'$name -> $key' must be one of the following - ${strings.packingValuesMap.keys.toList()}");
×
1249
        result = false;
1250
      }
1251
    }
1252
  }
1253

1254
  return result;
1255
}
1256

1257
FfiNativeConfig ffiNativeExtractor(dynamic yamlConfig) {
1✔
1258
  final yamlMap = yamlConfig as YamlMap?;
1259
  return FfiNativeConfig(
1✔
1260
    enabled: true,
1261
    asset: yamlMap?[strings.ffiNativeAsset] as String?,
×
1262
  );
1263
}
1264

1265
bool ffiNativeValidator(List<String> name, dynamic yamlConfig) {
×
1266
  if (!checkType<YamlMap?>(name, yamlConfig)) {
×
1267
    return false;
1268
  }
1269
  if (yamlConfig == null) {
1270
    // Empty means no asset name.
1271
    return true;
1272
  }
1273
  for (final key in (yamlConfig as YamlMap).keys) {
×
1274
    if (!checkType<String>([...name, key as String], yamlConfig[key])) {
×
1275
      return false;
1276
    }
1277
    if (key != strings.ffiNativeAsset) {
×
1278
      _logger.severe("'$name -> $key' must be one of the following - ${[
×
1279
        strings.ffiNativeAsset
1280
      ]}");
×
1281
    }
1282
  }
1283
  return true;
1284
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc