blob: b699239a35c58c0b64c3e2fa4d3c7cce0fcb8d3a [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.
/// Contains all the neccesary classes required by config.
import 'package:meta/meta.dart';
import 'package:quiver/pattern.dart' as quiver;
class CommentType {
CommentStyle style;
CommentLength length;
CommentType(this.style, this.length);
/// Sets default style as [CommentStyle.doxygen], default length as
/// [CommentLength.full].
CommentType.def()
: style = CommentStyle.doxygen,
length = CommentLength.full;
/// Disables any comments.
CommentType.none()
: style = CommentStyle.doxygen,
length = CommentLength.none;
}
enum CommentStyle { doxygen, any }
enum CommentLength { none, brief, full }
/// Represents a single specification in configurations.
///
/// [E] is the return type of the extractedResult.
class Specification<E> {
final bool Function(String name, dynamic value) validator;
final E Function(dynamic map) extractor;
final E Function() defaultValue;
final Requirement requirement;
final void Function(dynamic result) extractedResult;
Specification({
@required this.extractedResult,
@required this.validator,
@required this.extractor,
this.defaultValue,
this.requirement = Requirement.no,
});
}
enum Requirement { yes, prefer, no }
// Holds headers and filters for header.
class Headers {
/// Path to headers.
///
/// This contains all the headers, after extraction from Globs.
List<String> entryPoints = [];
/// Include filter for headers.
HeaderIncludeFilter includeFilter = GlobHeaderFilter();
Headers({this.entryPoints, this.includeFilter});
}
abstract class HeaderIncludeFilter {
bool shouldInclude(String headerSourceFile);
}
class GlobHeaderFilter extends HeaderIncludeFilter {
List<quiver.Glob> includeGlobs = [];
GlobHeaderFilter({
this.includeGlobs,
});
@override
bool shouldInclude(String header) {
// Return true if header was included.
for (final globPattern in includeGlobs) {
if (quiver.matchesFull(globPattern, header)) {
return true;
}
}
// If any includedInclusionHeaders is provided, return false.
if (includeGlobs.isNotEmpty) {
return false;
} else {
return true;
}
}
}
/// A generic declaration config, used for Functions, Structs and Enums.
class Declaration {
final Includer _includer;
final Renamer _renamer;
final MemberRenamer _memberRenamer;
Declaration({
Includer includer,
Renamer renamer,
MemberRenamer memberRenamer,
}) : _includer = includer ?? Includer(),
_renamer = renamer ?? Renamer(),
_memberRenamer = memberRenamer ?? MemberRenamer();
/// Applies renaming and returns the result.
String renameUsingConfig(String name) => _renamer.rename(name);
/// Applies member renaming and returns the result.
String renameMemberUsingConfig(String declaration, String member) =>
_memberRenamer.rename(declaration, member);
/// Checks if a name is allowed by a filter.
bool shouldInclude(String name) => _includer.shouldInclude(name);
}
/// Matches `$<single_digit_int>`, value can be accessed in group 1 of match.
final replaceGroupRegexp = RegExp(r'\$([0-9])');
/// Match/rename using [regExp].
class RegExpRenamer {
final RegExp regExp;
final String replacementPattern;
RegExpRenamer(this.regExp, this.replacementPattern);
/// Returns true if [str] has a full match with [regExp].
bool matches(String str) => quiver.matchesFull(regExp, str);
/// Renames [str] according to [replacementPattern].
///
/// Returns [str] if [regExp] doesn't have a full match.
String rename(String str) {
if (matches(str)) {
// Get match.
final regExpMatch = regExp.firstMatch(str);
/// Get group values.
/// E.g for `str`: `clang_dispose` and `regExp`: `clang_(.*)`
/// groups will be `0`: `clang_disponse`, `1`: `dispose`.
final groups = regExpMatch.groups(
List.generate(regExpMatch.groupCount, (index) => index) +
[regExpMatch.groupCount]);
/// Replace all `$<int>` symbols with respective groups (if any).
final result =
replacementPattern.replaceAllMapped(replaceGroupRegexp, (match) {
final groupInt = int.parse(match.group(1));
return groups[groupInt];
});
return result;
} else {
return str;
}
}
@override
String toString() {
return 'Regexp: $regExp, ReplacementPattern: $replacementPattern';
}
}
/// Handles `include/exclude` logic for a declaration.
class Includer {
final List<RegExp> _includeMatchers;
final Set<String> _includeFull;
final List<RegExp> _excludeMatchers;
final Set<String> _excludeFull;
Includer({
List<RegExp> includeMatchers,
Set<String> includeFull,
List<RegExp> excludeMatchers,
Set<String> excludeFull,
}) : _includeMatchers = includeMatchers ?? [],
_includeFull = includeFull ?? {},
_excludeMatchers = excludeMatchers ?? [],
_excludeFull = excludeFull ?? {};
/// Returns true if [name] is allowed.
///
/// Exclude overrides include.
bool shouldInclude(String name) {
if (_excludeFull.contains(name)) {
return false;
}
for (final em in _excludeMatchers) {
if (quiver.matchesFull(em, name)) {
return false;
}
}
if (_includeFull.contains(name)) {
return true;
}
for (final im in _includeMatchers) {
if (quiver.matchesFull(im, name)) {
return true;
}
}
// If user has provided 'include' field in the filter, then default
// matching is false.
if (_includeMatchers.isNotEmpty || _includeFull.isNotEmpty) {
return false;
} else {
return true;
}
}
}
/// Handles `full/regexp` renaming logic.
class Renamer {
final Map<String, String> _renameFull;
final List<RegExpRenamer> _renameMatchers;
Renamer({
List<RegExpRenamer> renamePatterns,
Map<String, String> renameFull,
}) : _renameMatchers = renamePatterns ?? [],
_renameFull = renameFull ?? {};
Renamer.noRename()
: _renameMatchers = [],
_renameFull = {};
String rename(String name) {
// Apply full rename (if any).
if (_renameFull.containsKey(name)) {
return _renameFull[name];
}
// Apply rename regexp (if matches).
for (final renamer in _renameMatchers) {
if (renamer.matches(name)) {
return renamer.rename(name);
}
}
// No renaming is provided for this declaration, return unchanged.
return name;
}
}
/// Match declaration name using [declarationRegExp].
class RegExpMemberRenamer {
final RegExp declarationRegExp;
final Renamer memberRenamer;
RegExpMemberRenamer(this.declarationRegExp, this.memberRenamer);
/// Returns true if [declaration] has a full match with [regExp].
bool matchesDeclarationName(String declaration) =>
quiver.matchesFull(declarationRegExp, declaration);
@override
String toString() {
return 'DeclarationRegExp: $declarationRegExp, MemberRenamer: $memberRenamer';
}
}
/// Handles `full/regexp` member renaming.
class MemberRenamer {
final Map<String, Renamer> _memberRenameFull;
final List<RegExpMemberRenamer> _memberRenameMatchers;
final Map<String, Renamer> _cache = {};
MemberRenamer({
Map<String, Renamer> memberRenameFull,
List<RegExpMemberRenamer> memberRenamePattern,
}) : _memberRenameFull = memberRenameFull ?? {},
_memberRenameMatchers = memberRenamePattern ?? [];
String rename(String declaration, String member) {
if (_cache.containsKey(declaration)) {
return _cache[declaration].rename(member);
}
// Apply full rename (if any).
if (_memberRenameFull.containsKey(declaration)) {
// Add to cache.
_cache[declaration] = _memberRenameFull[declaration];
return _cache[declaration].rename(member);
}
// Apply rename regexp (if matches).
for (final renamer in _memberRenameMatchers) {
if (renamer.matchesDeclarationName(declaration)) {
// Add to cache.
_cache[declaration] = renamer.memberRenamer;
return _cache[declaration].rename(member);
}
}
// No renaming is provided for this declaration, return unchanged.
return member;
}
}