blob: 452046d0db14d701f81e4f20f792f0f96ca00227 [file] [log] [blame]
// Copyright (c) 2019, 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:convert';
import 'package:async/async.dart';
import 'package:build/build.dart';
import 'meta_module.dart';
import 'modules.dart';
Map<String, dynamic> _deserialize(List<int> bytes) =>
jsonDecode(utf8.decode(bytes)) as Map<String, dynamic>;
List<int> _serialize(Map<String, dynamic> data) =>
utf8.encode(jsonEncode(data));
final metaModuleCache = DecodingCache.resource(
(m) => MetaModule.fromJson(_deserialize(m)), (m) => _serialize(m.toJson()));
final moduleCache = DecodingCache.resource(
(m) => Module.fromJson(_deserialize(m)), (m) => _serialize(m.toJson()));
/// A cache of objects decoded from written assets suitable for use as a
/// [Resource].
///
/// Instances that are decoded will be cached throughout the duration of a build
/// and invalidated between builds. Instances that are shared through the cache
/// should be treated as immutable to avoid leaking any information which was
/// not loaded from the underlying asset.
class DecodingCache<T> {
/// Create a [Resource] which can decoded instances of [T] serialized via json
/// to assets.
static Resource<DecodingCache<T>> resource<T>(
T Function(List<int>) fromBytes, List<int> Function(T) toBytes) =>
Resource<DecodingCache<T>>(() => DecodingCache._(fromBytes, toBytes),
dispose: (c) => c._dispose());
final _cached = <AssetId, Future<Result<T>>>{};
final T Function(List<int>) _fromBytes;
final List<int> Function(T) _toBytes;
DecodingCache._(this._fromBytes, this._toBytes);
void _dispose() => _cached.clear();
/// Find and deserialize a [T] stored in [id].
///
/// If the asset at [id] is unreadable the returned future will resolve to
/// `null`. If the instance is cached it will not be decoded again, but the
/// content dependencies will be tracked through [reader].
Future<T> find(AssetId id, AssetReader reader) async {
if (!await reader.canRead(id)) return null;
var result = _cached.putIfAbsent(
id, () => Result.capture(reader.readAsBytes(id).then(_fromBytes)));
return Result.release(result);
}
/// Serialized and write a [T] to [id].
///
/// The instance will be cached so that later calls to [find] may return the
/// instances without deserializing it.
Future<void> write(AssetId id, AssetWriter writer, T instance) async {
await writer.writeAsBytes(id, _toBytes(instance));
_cached[id] = Result.capture(Future.value(instance));
}
}