blob: 2103436bb33e5042cdc0642c61fcb016cb9a5bca [file] [log] [blame] [edit]
// Copyright (c) 2024, 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:async';
import 'dart:convert';
import 'dart:io';
import 'package:dart_model/dart_model.dart';
import 'package:macro_service/macro_service.dart';
/// Serves a [MacroService].
class MacroServer {
final HostService service;
final HostEndpoint endpoint;
final ServerSocket serverSocket;
// TODO(davidmorgan): track which socket corresponds to which macro(s).
Socket? _lastSocket;
// TODO(davidmorgan): properly track requests and responses.
Completer<Response>? _responseCompleter;
MacroServer._(this.service, this.endpoint, this.serverSocket) {
serverSocket.forEach(_handleConnection);
}
/// Serves [service].
///
/// TODO(davidmorgan): other transports besides TCP/IP.
static Future<MacroServer> serve({required HostService service}) async {
final serverSocket = await ServerSocket.bind('localhost', 0);
return MacroServer._(
service, HostEndpoint(port: serverSocket.port), serverSocket);
}
Future<Response> sendToMacro(QualifiedName name, HostRequest request) async {
_responseCompleter = Completer<Response>();
_lastSocket!.writeln(json.encode(request.node));
return _responseCompleter!.future;
}
void _handleConnection(Socket socket) {
_lastSocket = socket;
// TODO(davidmorgan): currently this is JSON with one request per line,
// switch to binary.
socket
.cast<List<int>>()
.transform(const Utf8Decoder())
.transform(const LineSplitter())
.forEach((line) {
final jsonData = json.decode(line) as Map<String, Object?>;
final request = MacroRequest.fromJson(jsonData);
if (request.type.isKnown) {
service
.handle(request)
.then((response) => socket.writeln(json.encode(response!.node)));
}
final response = Response.fromJson(jsonData);
if (response.type.isKnown) {
_responseCompleter!.complete(response);
_responseCompleter = null;
}
});
}
}