Add core models and state.

This commit is contained in:
Dain Nilsson 2021-11-19 11:10:00 +01:00
parent fc15f2e7e2
commit dd6a3e84c8
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
5 changed files with 1035 additions and 0 deletions

32
lib/core/models.dart Normal file
View File

@ -0,0 +1,32 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'models.freezed.dart';
part 'models.g.dart';
@freezed
class Version with _$Version {
const Version._();
const factory Version(int major, int minor, int patch) = _Version;
factory Version.fromJson(List<dynamic> values) {
return Version(values[0], values[1], values[2]);
}
List<dynamic> toJson() => [major, minor, patch];
@override
String toString() {
return "$major.$minor.$patch";
}
}
@Freezed(unionKey: 'kind')
class RpcResponse with _$RpcResponse {
factory RpcResponse.success(Map<String, dynamic> body) = Success;
factory RpcResponse.signal(String status, Map<String, dynamic> body) = Signal;
factory RpcResponse.error(
String status, String message, Map<String, dynamic> body) = RpcError;
factory RpcResponse.fromJson(Map<String, dynamic> json) =>
_$RpcResponseFromJson(json);
}

View File

@ -0,0 +1,798 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'models.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
/// @nodoc
class _$VersionTearOff {
const _$VersionTearOff();
_Version call(int major, int minor, int patch) {
return _Version(
major,
minor,
patch,
);
}
}
/// @nodoc
const $Version = _$VersionTearOff();
/// @nodoc
mixin _$Version {
int get major => throw _privateConstructorUsedError;
int get minor => throw _privateConstructorUsedError;
int get patch => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$VersionCopyWith<Version> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $VersionCopyWith<$Res> {
factory $VersionCopyWith(Version value, $Res Function(Version) then) =
_$VersionCopyWithImpl<$Res>;
$Res call({int major, int minor, int patch});
}
/// @nodoc
class _$VersionCopyWithImpl<$Res> implements $VersionCopyWith<$Res> {
_$VersionCopyWithImpl(this._value, this._then);
final Version _value;
// ignore: unused_field
final $Res Function(Version) _then;
@override
$Res call({
Object? major = freezed,
Object? minor = freezed,
Object? patch = freezed,
}) {
return _then(_value.copyWith(
major: major == freezed
? _value.major
: major // ignore: cast_nullable_to_non_nullable
as int,
minor: minor == freezed
? _value.minor
: minor // ignore: cast_nullable_to_non_nullable
as int,
patch: patch == freezed
? _value.patch
: patch // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
abstract class _$VersionCopyWith<$Res> implements $VersionCopyWith<$Res> {
factory _$VersionCopyWith(_Version value, $Res Function(_Version) then) =
__$VersionCopyWithImpl<$Res>;
@override
$Res call({int major, int minor, int patch});
}
/// @nodoc
class __$VersionCopyWithImpl<$Res> extends _$VersionCopyWithImpl<$Res>
implements _$VersionCopyWith<$Res> {
__$VersionCopyWithImpl(_Version _value, $Res Function(_Version) _then)
: super(_value, (v) => _then(v as _Version));
@override
_Version get _value => super._value as _Version;
@override
$Res call({
Object? major = freezed,
Object? minor = freezed,
Object? patch = freezed,
}) {
return _then(_Version(
major == freezed
? _value.major
: major // ignore: cast_nullable_to_non_nullable
as int,
minor == freezed
? _value.minor
: minor // ignore: cast_nullable_to_non_nullable
as int,
patch == freezed
? _value.patch
: patch // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _$_Version extends _Version {
const _$_Version(this.major, this.minor, this.patch) : super._();
@override
final int major;
@override
final int minor;
@override
final int patch;
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _Version &&
(identical(other.major, major) || other.major == major) &&
(identical(other.minor, minor) || other.minor == minor) &&
(identical(other.patch, patch) || other.patch == patch));
}
@override
int get hashCode => Object.hash(runtimeType, major, minor, patch);
@JsonKey(ignore: true)
@override
_$VersionCopyWith<_Version> get copyWith =>
__$VersionCopyWithImpl<_Version>(this, _$identity);
}
abstract class _Version extends Version {
const factory _Version(int major, int minor, int patch) = _$_Version;
const _Version._() : super._();
@override
int get major;
@override
int get minor;
@override
int get patch;
@override
@JsonKey(ignore: true)
_$VersionCopyWith<_Version> get copyWith =>
throw _privateConstructorUsedError;
}
RpcResponse _$RpcResponseFromJson(Map<String, dynamic> json) {
switch (json['kind']) {
case 'success':
return Success.fromJson(json);
case 'signal':
return Signal.fromJson(json);
case 'error':
return RpcError.fromJson(json);
default:
throw CheckedFromJsonException(
json, 'kind', 'RpcResponse', 'Invalid union type "${json['kind']}"!');
}
}
/// @nodoc
class _$RpcResponseTearOff {
const _$RpcResponseTearOff();
Success success(Map<String, dynamic> body) {
return Success(
body,
);
}
Signal signal(String status, Map<String, dynamic> body) {
return Signal(
status,
body,
);
}
RpcError error(String status, String message, Map<String, dynamic> body) {
return RpcError(
status,
message,
body,
);
}
RpcResponse fromJson(Map<String, Object?> json) {
return RpcResponse.fromJson(json);
}
}
/// @nodoc
const $RpcResponse = _$RpcResponseTearOff();
/// @nodoc
mixin _$RpcResponse {
Map<String, dynamic> get body => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Success value) success,
required TResult Function(Signal value) signal,
required TResult Function(RpcError value) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$RpcResponseCopyWith<RpcResponse> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RpcResponseCopyWith<$Res> {
factory $RpcResponseCopyWith(
RpcResponse value, $Res Function(RpcResponse) then) =
_$RpcResponseCopyWithImpl<$Res>;
$Res call({Map<String, dynamic> body});
}
/// @nodoc
class _$RpcResponseCopyWithImpl<$Res> implements $RpcResponseCopyWith<$Res> {
_$RpcResponseCopyWithImpl(this._value, this._then);
final RpcResponse _value;
// ignore: unused_field
final $Res Function(RpcResponse) _then;
@override
$Res call({
Object? body = freezed,
}) {
return _then(_value.copyWith(
body: body == freezed
? _value.body
: body // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
/// @nodoc
abstract class $SuccessCopyWith<$Res> implements $RpcResponseCopyWith<$Res> {
factory $SuccessCopyWith(Success value, $Res Function(Success) then) =
_$SuccessCopyWithImpl<$Res>;
@override
$Res call({Map<String, dynamic> body});
}
/// @nodoc
class _$SuccessCopyWithImpl<$Res> extends _$RpcResponseCopyWithImpl<$Res>
implements $SuccessCopyWith<$Res> {
_$SuccessCopyWithImpl(Success _value, $Res Function(Success) _then)
: super(_value, (v) => _then(v as Success));
@override
Success get _value => super._value as Success;
@override
$Res call({
Object? body = freezed,
}) {
return _then(Success(
body == freezed
? _value.body
: body // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$Success implements Success {
_$Success(this.body, {String? $type}) : $type = $type ?? 'success';
factory _$Success.fromJson(Map<String, dynamic> json) =>
_$$SuccessFromJson(json);
@override
final Map<String, dynamic> body;
@JsonKey(name: 'kind')
final String $type;
@override
String toString() {
return 'RpcResponse.success(body: $body)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is Success &&
const DeepCollectionEquality().equals(other.body, body));
}
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(body));
@JsonKey(ignore: true)
@override
$SuccessCopyWith<Success> get copyWith =>
_$SuccessCopyWithImpl<Success>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
error,
}) {
return success(body);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
}) {
return success?.call(body);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
required TResult orElse(),
}) {
if (success != null) {
return success(body);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Success value) success,
required TResult Function(Signal value) signal,
required TResult Function(RpcError value) error,
}) {
return success(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
}) {
return success?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
required TResult orElse(),
}) {
if (success != null) {
return success(this);
}
return orElse();
}
@override
Map<String, dynamic> toJson() {
return _$$SuccessToJson(this);
}
}
abstract class Success implements RpcResponse {
factory Success(Map<String, dynamic> body) = _$Success;
factory Success.fromJson(Map<String, dynamic> json) = _$Success.fromJson;
@override
Map<String, dynamic> get body;
@override
@JsonKey(ignore: true)
$SuccessCopyWith<Success> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SignalCopyWith<$Res> implements $RpcResponseCopyWith<$Res> {
factory $SignalCopyWith(Signal value, $Res Function(Signal) then) =
_$SignalCopyWithImpl<$Res>;
@override
$Res call({String status, Map<String, dynamic> body});
}
/// @nodoc
class _$SignalCopyWithImpl<$Res> extends _$RpcResponseCopyWithImpl<$Res>
implements $SignalCopyWith<$Res> {
_$SignalCopyWithImpl(Signal _value, $Res Function(Signal) _then)
: super(_value, (v) => _then(v as Signal));
@override
Signal get _value => super._value as Signal;
@override
$Res call({
Object? status = freezed,
Object? body = freezed,
}) {
return _then(Signal(
status == freezed
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
body == freezed
? _value.body
: body // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$Signal implements Signal {
_$Signal(this.status, this.body, {String? $type}) : $type = $type ?? 'signal';
factory _$Signal.fromJson(Map<String, dynamic> json) =>
_$$SignalFromJson(json);
@override
final String status;
@override
final Map<String, dynamic> body;
@JsonKey(name: 'kind')
final String $type;
@override
String toString() {
return 'RpcResponse.signal(status: $status, body: $body)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is Signal &&
(identical(other.status, status) || other.status == status) &&
const DeepCollectionEquality().equals(other.body, body));
}
@override
int get hashCode => Object.hash(
runtimeType, status, const DeepCollectionEquality().hash(body));
@JsonKey(ignore: true)
@override
$SignalCopyWith<Signal> get copyWith =>
_$SignalCopyWithImpl<Signal>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
error,
}) {
return signal(status, body);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
}) {
return signal?.call(status, body);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
required TResult orElse(),
}) {
if (signal != null) {
return signal(status, body);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Success value) success,
required TResult Function(Signal value) signal,
required TResult Function(RpcError value) error,
}) {
return signal(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
}) {
return signal?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
required TResult orElse(),
}) {
if (signal != null) {
return signal(this);
}
return orElse();
}
@override
Map<String, dynamic> toJson() {
return _$$SignalToJson(this);
}
}
abstract class Signal implements RpcResponse {
factory Signal(String status, Map<String, dynamic> body) = _$Signal;
factory Signal.fromJson(Map<String, dynamic> json) = _$Signal.fromJson;
String get status;
@override
Map<String, dynamic> get body;
@override
@JsonKey(ignore: true)
$SignalCopyWith<Signal> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $RpcErrorCopyWith<$Res> implements $RpcResponseCopyWith<$Res> {
factory $RpcErrorCopyWith(RpcError value, $Res Function(RpcError) then) =
_$RpcErrorCopyWithImpl<$Res>;
@override
$Res call({String status, String message, Map<String, dynamic> body});
}
/// @nodoc
class _$RpcErrorCopyWithImpl<$Res> extends _$RpcResponseCopyWithImpl<$Res>
implements $RpcErrorCopyWith<$Res> {
_$RpcErrorCopyWithImpl(RpcError _value, $Res Function(RpcError) _then)
: super(_value, (v) => _then(v as RpcError));
@override
RpcError get _value => super._value as RpcError;
@override
$Res call({
Object? status = freezed,
Object? message = freezed,
Object? body = freezed,
}) {
return _then(RpcError(
status == freezed
? _value.status
: status // ignore: cast_nullable_to_non_nullable
as String,
message == freezed
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
body == freezed
? _value.body
: body // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$RpcError implements RpcError {
_$RpcError(this.status, this.message, this.body, {String? $type})
: $type = $type ?? 'error';
factory _$RpcError.fromJson(Map<String, dynamic> json) =>
_$$RpcErrorFromJson(json);
@override
final String status;
@override
final String message;
@override
final Map<String, dynamic> body;
@JsonKey(name: 'kind')
final String $type;
@override
String toString() {
return 'RpcResponse.error(status: $status, message: $message, body: $body)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is RpcError &&
(identical(other.status, status) || other.status == status) &&
(identical(other.message, message) || other.message == message) &&
const DeepCollectionEquality().equals(other.body, body));
}
@override
int get hashCode => Object.hash(
runtimeType, status, message, const DeepCollectionEquality().hash(body));
@JsonKey(ignore: true)
@override
$RpcErrorCopyWith<RpcError> get copyWith =>
_$RpcErrorCopyWithImpl<RpcError>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(Map<String, dynamic> body) success,
required TResult Function(String status, Map<String, dynamic> body) signal,
required TResult Function(
String status, String message, Map<String, dynamic> body)
error,
}) {
return error(status, message, body);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
}) {
return error?.call(status, message, body);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(Map<String, dynamic> body)? success,
TResult Function(String status, Map<String, dynamic> body)? signal,
TResult Function(String status, String message, Map<String, dynamic> body)?
error,
required TResult orElse(),
}) {
if (error != null) {
return error(status, message, body);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Success value) success,
required TResult Function(Signal value) signal,
required TResult Function(RpcError value) error,
}) {
return error(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
}) {
return error?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Success value)? success,
TResult Function(Signal value)? signal,
TResult Function(RpcError value)? error,
required TResult orElse(),
}) {
if (error != null) {
return error(this);
}
return orElse();
}
@override
Map<String, dynamic> toJson() {
return _$$RpcErrorToJson(this);
}
}
abstract class RpcError implements RpcResponse {
factory RpcError(String status, String message, Map<String, dynamic> body) =
_$RpcError;
factory RpcError.fromJson(Map<String, dynamic> json) = _$RpcError.fromJson;
String get status;
String get message;
@override
Map<String, dynamic> get body;
@override
@JsonKey(ignore: true)
$RpcErrorCopyWith<RpcError> get copyWith =>
throw _privateConstructorUsedError;
}

44
lib/core/models.g.dart Normal file
View File

@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'models.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$Success _$$SuccessFromJson(Map<String, dynamic> json) => _$Success(
json['body'] as Map<String, dynamic>,
$type: json['kind'] as String?,
);
Map<String, dynamic> _$$SuccessToJson(_$Success instance) => <String, dynamic>{
'body': instance.body,
'kind': instance.$type,
};
_$Signal _$$SignalFromJson(Map<String, dynamic> json) => _$Signal(
json['status'] as String,
json['body'] as Map<String, dynamic>,
$type: json['kind'] as String?,
);
Map<String, dynamic> _$$SignalToJson(_$Signal instance) => <String, dynamic>{
'status': instance.status,
'body': instance.body,
'kind': instance.$type,
};
_$RpcError _$$RpcErrorFromJson(Map<String, dynamic> json) => _$RpcError(
json['status'] as String,
json['message'] as String,
json['body'] as Map<String, dynamic>,
$type: json['kind'] as String?,
);
Map<String, dynamic> _$$RpcErrorToJson(_$RpcError instance) =>
<String, dynamic>{
'status': instance.status,
'message': instance.message,
'body': instance.body,
'kind': instance.$type,
};

122
lib/core/rpc.dart Normal file
View File

@ -0,0 +1,122 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:developer' as developer;
import 'models.dart';
class Signaler {
final _controller = StreamController<Signal>();
void Function(String)? _sendSignal;
Stream<Signal> get signals => _controller.stream;
void cancel() {
final sendSignal = _sendSignal;
if (sendSignal == null) {
throw Exception("Signaler not attached to any request!");
}
sendSignal("cancel");
}
}
class _Request {
final String action;
final List<String> target;
final Map body;
final Completer<Map<String, dynamic>> completer = Completer();
final Signaler? signal;
_Request(this.action, this.target, this.body, this.signal);
Map<String, dynamic> toJson() => {
"kind": "command",
"action": action,
"target": target,
"body": body,
};
}
class RpcSession {
final Process _process;
final StreamSubscription<Map<String, dynamic>> responses;
final Queue<_Request> _requests = Queue();
bool _busy = false;
RpcSession(this._process)
: responses = _process.stdout
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.map((event) => jsonDecode(event))
.cast<Map<String, dynamic>>()
.listen(null) {
stderr.addStream(_process.stderr);
developer.log("Launched ykman subprocess...", name: "rpc");
}
static Future<RpcSession> launch(String executable) async {
var process =
await Process.start(executable, [], environment: {"_YKMAN_RPC": "1"});
return RpcSession(process);
}
Future<Map<String, dynamic>> command(String action, List<String>? target,
{Map? params, Signaler? signal}) {
var request = _Request(action, target ?? [], params ?? {}, signal);
_requests.add(request);
pump();
return request.completer.future;
}
void pump() {
if (!_busy && _requests.isNotEmpty) {
final request = _requests.removeFirst();
_busy = true;
request.signal?._sendSignal = _sendSignal;
responses.onData((data) {
developer.log("RECV", name: "rpc", error: jsonEncode(data));
try {
final response = RpcResponse.fromJson(data);
if (response.map(
signal: (signal) {
request.signal?._controller.sink.add(signal);
return false;
},
success: (success) {
request.completer.complete(success.body);
return true;
},
error: (error) {
request.completer.completeError(error);
return true;
},
)) {
responses.onData(null);
request.signal?._sendSignal = null;
request.signal?._controller.close();
_busy = false;
pump();
}
} catch (e) {
developer.log("Invalid RPC message",
name: "rpc", error: jsonEncode(e));
}
});
_send(request.toJson());
}
}
void _sendSignal(String status) {
_send({"kind": "signal", "status": status});
}
void _send(Map data) {
developer.log("SEND", name: "rpc", error: jsonEncode(data));
_process.stdin.writeln(jsonEncode(data));
_process.stdin.flush();
}
}

39
lib/core/state.dart Normal file
View File

@ -0,0 +1,39 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'models.dart';
import 'rpc.dart';
// This must be initialized before use, in main.dart.
final rpcProvider = Provider<RpcSession>((ref) {
throw UnimplementedError();
});
class RpcNodeSession {
final RpcSession _rpc;
final List<String> devicePath;
final List<String> subPath;
final Function _reset;
RpcNodeSession(this._rpc, this.devicePath, this.subPath, this._reset);
Future<Map<String, dynamic>> command(
String action, {
List<String> target = const [],
Map<dynamic, dynamic>? params,
Signaler? signal,
}) async {
try {
return await _rpc.command(
action,
devicePath + subPath + target,
params: params,
signal: signal,
);
} on RpcError catch (e) {
if (e.status == "state-reset") {
_reset();
}
rethrow;
}
}
}