dart_pkpass/lib/pkpass/utils/file_matcher.dart
The one with the braid 0863580b23 fix: support compressed archives and advanced character encoding
Signed-off-by: The one with the braid <info@braid.business>
2024-07-11 11:52:22 +02:00

155 lines
4 KiB
Dart

import 'package:intl/locale.dart';
abstract class FileMatcher {
const FileMatcher._();
static String? matchFile({
required List<String> files,
required String name,
String extension = 'png',
int scale = 1,
Locale? locale,
}) {
final localized = matchLocale(files: files, name: 'logo', extension: 'png');
if (localized.isEmpty) return null;
final scaled = matchScale(
files: localized,
name: 'logo',
extension: 'png',
scale: scale,
);
final file = files.singleWhere((element) => element == scaled);
return file;
}
static List<String> matchLocale({
required List<String> files,
Locale? locale,
required String name,
required String extension,
}) {
files.sort((a, b) {
final aLocalized = a.startsWith(RegExp('^[a-z]+(-[a-z]+)?\\.lproj\\/'));
final bLocalized = b.startsWith(RegExp('^[a-z]+(-[a-z]+)?\\.lproj\\/'));
if (aLocalized && bLocalized) {
return a.compareTo(b);
} else if (aLocalized) {
return -1;
} else {
return 1;
}
});
files = files.reversed.toList();
List<RegExp> expressions = <RegExp>[];
// adding the fallbacks
// - match only unlocalized
// - match the five mostly spoken languages of the world, copied from Wikipedia
// - match just *any* language
expressions.addAll(
[
RegExp(
'^[a-z]+(-[a-z]+)?\\.lproj\\/$name(@\\d+x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
),
...['en', 'zh', 'hi', 'es', 'fr'].reversed.map(
(language) => RegExp(
'^$language(-[a-z]+)?\\.lproj\\/$name(@\\d+x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
),
),
RegExp(
'^$name(@\\d+x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
),
],
);
if (locale != null) {
final language = locale.languageCode;
expressions.add(
RegExp(
'^$language(-[a-z]+)?\\.lproj\\/$name(@\\d+x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
),
);
final region = locale.countryCode;
if (region != null) {
expressions.add(
RegExp(
'^$language-$region\\.lproj\\/$name(@\\d+x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
),
);
}
}
for (final regex in expressions.reversed) {
final matches = <String>[];
for (final file in files) {
final match = regex.stringMatch(file);
if (match != null) matches.add(match);
}
if (matches.isNotEmpty) return matches;
}
return [];
}
static String matchScale({
required List<String> files,
int scale = 1,
required String name,
required String extension,
}) {
files.sort();
files = files.reversed.toList();
final regex = RegExp(
'$name(@(\\d+)x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
);
int closestScale = 0;
final matches = <String>[];
for (final file in files) {
final match = regex.firstMatch(file);
if (match != null) {
String? group = match[2];
if (group == null) {
if (scale == 1) return match.input;
group = '1';
}
final matchedScale = int.parse(group);
if (matchedScale == scale) return match.input;
if (matchedScale < scale) {
if (closestScale < matchedScale) closestScale = matchedScale;
} else if (closestScale < matchedScale) {
closestScale = matchedScale;
}
matches.add(match.input);
}
}
final scaledMatches = matches
.where(
(element) => RegExp(
'$name(@${closestScale}x)?\\.$extension\$',
unicode: true,
caseSensitive: false,
).hasMatch(element),
)
.toList();
scaledMatches.sort();
return scaledMatches.last;
}
}