creditGaps function

({Set<String> shipped, Set<String> uncredited, Set<String> unshipped}) creditGaps(
  1. List<Credit> credited, {
  2. required String shippedFrom,
  3. String extension = '.glb',
})

What is shipped and uncredited, and what is credited and not shipped.

shippedFrom is a directory of assets, read as it is on disk. extension is what counts as a model there; everything else in the folder — a LICENSES.md, a .blend nobody ships — is ignored.

Paths are compared as <folder>/<name>, which is the form a Credit.file takes: models/penguin.glb.

Implementation

({Set<String> uncredited, Set<String> unshipped, Set<String> shipped})
creditGaps(
  List<Credit> credited, {
  required String shippedFrom,
  String extension = '.glb',
}) {
  final folder = shippedFrom.split('/').last;
  final directory = Directory(shippedFrom);
  if (!directory.existsSync()) {
    // Loud, because an empty set would make "this game ships no models" and
    // "the test ran from the wrong directory" the same answer.
    throw StateError(
      '$shippedFrom is not there — run from the application root, where its '
      'assets are',
    );
  }

  final shipped = directory
      .listSync()
      .whereType<File>()
      .map((File f) => '$folder/${f.uri.pathSegments.last}')
      .where((String name) => name.endsWith(extension))
      .toSet();

  final named = credited.map((Credit c) => c.file).toSet();
  return (
    uncredited: shipped.difference(named),
    unshipped: named.difference(shipped),
    shipped: shipped,
  );
}