blob: 70cccdd30a08ba38d83ee2abdc1af970576c1095 [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'package:ffigen/src/code_generator.dart';
import 'package:glob/glob.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:yaml/yaml.dart';
import '../strings.dart' as strings;
import './config.dart';
import 'declaration.dart';
var _logger = Logger('config_provider:spec_utils.dart');
bool booleanExtractor(dynamic value) => value as bool;
bool booleanValidator(String name, dynamic value) {
if (value is! bool) {
_logger.severe("Expected value of key '$name' to be a bool.");
return false;
} else {
return true;
}
}
Map<int, SupportedNativeType> sizemapExtractor(dynamic yamlConfig) {
final resultMap = <int, SupportedNativeType>{};
final sizemap = yamlConfig as YamlMap;
if (sizemap != null) {
for (final typeName in strings.sizemap_native_mapping.keys) {
if (sizemap.containsKey(typeName)) {
final cxTypeInt = strings.sizemap_native_mapping[typeName];
final byteSize = sizemap[typeName] as int;
resultMap[cxTypeInt] = nativeSupportedType(byteSize,
signed: typeName.contains('unsigned') ? false : true);
}
}
}
return resultMap;
}
bool sizemapValidator(String name, dynamic yamlConfig) {
if (yamlConfig is! YamlMap) {
_logger.severe("Expected value of key '$name' to be a Map.");
return false;
}
for (final key in (yamlConfig as YamlMap).keys) {
if (!strings.sizemap_native_mapping.containsKey(key)) {
_logger.warning("Unknown subkey '$key' in '$name'.");
}
}
return true;
}
List<String> compilerOptsExtractor(dynamic value) =>
(value as String)?.split(' ');
bool compilerOptsValidator(String name, dynamic value) {
if (value is! String) {
_logger.severe("Expected value of key '$name' to be a string.");
return false;
} else {
return true;
}
}
HeaderFilter headerFilterExtractor(dynamic yamlConfig) {
final includedInclusionHeaders = <String>{};
final excludedInclusionHeaders = <String>{};
final headerFilter = yamlConfig as YamlMap;
if (headerFilter != null) {
// Add include/excluded header-filter from Yaml.
final include = headerFilter[strings.include] as YamlList;
include?.cast<String>()?.forEach(includedInclusionHeaders.add);
final exclude = headerFilter[strings.exclude] as YamlList;
exclude?.cast<String>()?.forEach(excludedInclusionHeaders.add);
}
return HeaderFilter(
includedInclusionHeaders: includedInclusionHeaders,
excludedInclusionHeaders: excludedInclusionHeaders,
);
}
bool headerFilterValidator(String name, dynamic value) {
if (value is! YamlMap) {
_logger.severe("Expected value of key '$name' to be a Map.");
return false;
} else {
return true;
}
}
List<String> headersExtractor(dynamic yamlConfig) {
final headers = <String>[];
for (final h in (yamlConfig as YamlList)) {
final headerGlob = h as String;
// Add file directly to header if it's not a Glob but a File.
if (File(headerGlob).existsSync()) {
headers.add(headerGlob);
_logger.fine('Adding header/file: $headerGlob');
} else {
final glob = Glob(headerGlob);
for (final file in glob.listSync(followLinks: true)) {
headers.add(file.path);
_logger.fine('Adding header/file: ${file.path}');
}
}
}
return headers;
}
bool headersValidator(String name, dynamic value) {
if (value is! YamlList) {
_logger.severe(
"Expected value of key '${strings.headers}' to be a List of String.");
return false;
} else {
return true;
}
}
String libclangDylibExtractor(dynamic value) => getDylibPath(value as String);
bool libclangDylibValidator(String name, dynamic value) {
if (value is! String) {
_logger.severe("Expected value of key '$name' to be a string.");
return false;
} else {
final dylibPath = getDylibPath(value as String);
if (!File(dylibPath).existsSync()) {
_logger.severe(
'Dynamic library: $dylibPath does not exist or is corrupt, input folder: $value.');
return false;
} else {
return true;
}
}
}
String getDylibPath(String dylibParentFoler) {
String dylibPath;
if (Platform.isMacOS) {
dylibPath = p.join(dylibParentFoler, strings.libclang_dylib_macos);
} else if (Platform.isWindows) {
// Fix path for windows if '/' is used as seperator instead of '\'
// because our examples input path like this.
final newValue = dylibParentFoler.replaceAll('/', r'\');
dylibPath = p.join(newValue, strings.libclang_dylib_windows);
} else {
dylibPath = p.join(dylibParentFoler, strings.libclang_dylib_linux);
}
return dylibPath;
}
String outputExtractor(dynamic value) => value as String;
bool outputValidator(String name, dynamic value) {
if (value is String) {
return true;
} else {
_logger.severe("Expected value of key '$name' to be a String.");
return false;
}
}
Declaration declarationConfigExtractor(dynamic yamlMap) {
List<String> includeMatchers, includeFull, excludeMatchers, excludeFull;
String prefix;
Map<String, String> prefixReplacement;
final include = yamlMap[strings.include] as YamlMap;
if (include != null) {
includeMatchers = (include[strings.matches] as YamlList)?.cast<String>();
includeFull = (include[strings.names] as YamlList)?.cast<String>();
}
final exclude = yamlMap[strings.exclude] as YamlMap;
if (exclude != null) {
excludeMatchers = (exclude[strings.matches] as YamlList)?.cast<String>();
excludeFull = (exclude[strings.names] as YamlList)?.cast<String>();
}
prefix = yamlMap[strings.prefix] as String;
prefixReplacement =
(yamlMap[strings.prefix_replacement] as YamlMap)?.cast<String, String>();
return Declaration(
includeMatchers: includeMatchers,
includeFull: includeFull,
excludeMatchers: excludeMatchers,
excludeFull: excludeFull,
globalPrefix: prefix,
prefixReplacement: prefixReplacement,
);
}
bool declarationConfigValidator(String name, dynamic value) {
var _result = true;
if (value is YamlMap) {
for (final key in value.keys) {
if (key == strings.include || key == strings.exclude) {
if (value[key] is! YamlMap) {
_logger.severe("Expected '$name -> $key' to be a Map.");
_result = false;
}
for (final subkey in value[key].keys) {
if (subkey == strings.matches || subkey == strings.names) {
if (value[key][subkey] is! YamlList) {
_logger
.severe("Expected '$name -> $key -> $subkey' to be a List.");
_result = false;
}
} else {
_logger.severe("Unknown key '$subkey' in '$name -> $key'.");
}
}
} else if (key == strings.prefix) {
if (value[key] is! String) {
_logger.severe("Expected '$name -> $key' to be a String.");
_result = false;
}
} else if (key == strings.prefix_replacement) {
if (value[key] is! YamlMap) {
_logger.severe("Expected '$name -> $key' to be a Map.");
_result = false;
} else {
for (final subkey in value[key].keys) {
if (value[key][subkey] is! String) {
_logger.severe(
"Expected '$name -> $key -> $subkey' to be a String.");
_result = false;
}
}
}
} else {
_logger.severe("Unknown key '$key' in '$name'.");
_result = false;
}
}
} else {
_logger.severe("Expected value '$name' to be a Map.");
_result = false;
}
return _result;
}
SupportedNativeType nativeSupportedType(int value, {bool signed = true}) {
switch (value) {
case 1:
return signed ? SupportedNativeType.Int8 : SupportedNativeType.Uint8;
case 2:
return signed ? SupportedNativeType.Int16 : SupportedNativeType.Uint16;
case 4:
return signed ? SupportedNativeType.Int32 : SupportedNativeType.Uint32;
case 8:
return signed ? SupportedNativeType.Int64 : SupportedNativeType.Uint64;
default:
throw Exception(
'Unsupported value given to sizemap, Allowed values for sizes are: 1, 2, 4, 8');
}
}
String stringExtractor(dynamic value) => value as String;
bool nonEmptyStringValidator(String name, dynamic value) {
if (value is String && value.isNotEmpty) {
return true;
} else {
_logger.severe("Expected value of key '$name' to be a non-empty String.");
return false;
}
}
bool dartClassNameValidator(String name, dynamic value) {
if (value is String &&
RegExp('[a-zA-Z]+[_a-zA-Z0-9]*').stringMatch(value) == value) {
return true;
} else {
_logger.severe(
"Expected value of key '$name' to be a valid public class name.");
return false;
}
}
String commentExtractor(dynamic value) => value as String;
bool commentValidator(String name, dynamic value) {
if (value is! String) {
_logger.severe("Expected value of key '$name' to be a String.");
return false;
} else {
if (strings.commentTypeSet.contains(value as String)) {
return true;
} else {
_logger.severe(
"Value of key '$name' must be one of the following - ${strings.commentTypeSet}.");
return false;
}
}
}