blob: 8ce73cddd9b6157bf28d2a5c49346d1397ffc4f3 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. 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:async';
import 'dart:convert';
import 'package:meta/meta.dart';
/// When an Entity does not support a given type.
class EntityTypeException implements Exception {
/// The unsuported type.
final String type;
/// Create a new [EntityTypeException].
EntityTypeException(this.type);
@override
String toString() =>
'EntityTypeError: type "$type" is not available for Entity';
}
/// A [Codec] used for handling the the automatic translation of to and from and
/// Entity's source data to the specified Dart type [T].
class EntityCodec<T> extends Codec<T, String> {
/// The "type" this codec is associated with, similar to mime-type, see
/// [Entity#GetTypes](https://goo.gl/QJo3tW).
final String type;
final _EntityEncoder<T> _encoder;
final _EntityDecoder<T> _decoder;
/// Create a new [EntityCodec].
EntityCodec({
@required this.type,
@required _EncodeEntity<T> encode,
@required _DecodeEntity<T> decode,
}) : assert(type != null),
assert(type.isEmpty == false),
assert(encode != null),
assert(decode != null),
_encoder = _EntityEncoder<T>(encode),
_decoder = _EntityDecoder<T>(decode);
@override
_EntityEncoder<T> get encoder => _encoder;
@override
_EntityDecoder<T> get decoder => _decoder;
}
typedef _EncodeEntity<T> = String Function(T value);
class _EntityEncoder<T> extends Converter<T, String> {
final _EncodeEntity<T> encode;
const _EntityEncoder(this.encode);
@override
String convert(T source) {
return encode(source);
}
}
typedef _DecodeEntity<T> = T Function(String data);
class _EntityDecoder<T> extends Converter<String, T> {
final _DecodeEntity<T> decode;
const _EntityDecoder(this.decode);
@override
T convert(String data) => decode(data);
@override
Stream<T> bind(Stream<String> source) {
EntityDecoderSink<T> map(EventSink<T> out) =>
EntityDecoderSink<T>(out, this);
return Stream<T>.eventTransformed(source, map);
}
}
/// Entity data [String]s in, [T] out.
class EntityDecoderSink<T> extends EventSink<String> {
/// The [EventSink] that values are decoded into. Errors generated by the
/// decoder are also added to [out].
final EventSink<T> out;
/// The decoder to used to convert the source ([Stream<String>]) events.
final _EntityDecoder<T> decoder;
/// Create an instance of [EntityDecoderSink], usually via the
/// [Stream#eventTransformed] constructor.
EntityDecoderSink(this.out, this.decoder);
@override
void add(String data) {
try {
T value = decoder.decode(data);
out.add(value);
} on Object catch (err, stackTrace) {
addError(err, stackTrace);
}
}
@override
void addError(Object e, [StackTrace s]) => out.addError(e, s);
@override
void close() => out.close();
}