Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

exchange.nct

see ./index.nct

use std/io.{Reader, Writer}
use std/io/buffer.{BufReader, BufWriter}
use std/net
use std/net.{IpAddress, Ipv4Address, SocketAddress}
use std/task
use std/task.Timeout
use std/time.Duration

struct ExchangeComplete {}

func loopback(): SocketAddress {
    return SocketAddress.new(IpAddress.from_ipv4(Ipv4Address.loopback()), 0)
}

async func serve(stream: net.TcpStream): ExchangeComplete! {
    var input = BufReader.with_capacity(move stream, 3)
    let request_line = await input.read_line()? otherwise {
        return error.new("async-loopback.request_eof", "client closed before one request line")
    }
    if request_line != "ping" {
        return error.new("async-loopback.request", "server received an unexpected request line")
    }

    let response_stream = input.finish()
    var output = BufWriter.with_capacity(move response_stream, 2)
    await output.write_line("pong")?
    var completed_stream = await output.finish()?
    completed_stream.close()
    return ExchangeComplete {}
}

async func exchange_request(stream: net.TcpStream): ExchangeComplete! {
    var output = BufWriter.with_capacity(move stream, 2)
    await output.write_line("ping")?
    let response_stream = await output.finish()?

    var input = BufReader.with_capacity(move response_stream, 3)
    let response = await input.read_line()? otherwise {
        return error.new("async-loopback.response_eof", "server closed before one response line")
    }
    if response != "pong" {
        return error.new("async-loopback.response", "client received an unexpected response line")
    }
    var completed_stream = input.finish()
    completed_stream.close()
    return ExchangeComplete {}
}

async func run(): i32! {
    let timeout = Duration.from_seconds(1)
    var listener = await net.bind_tcp(loopback())?
    let listening_address = listener.local_address()?
    let connection = await task.join(
        net.connect_tcp_with_timeout(listening_address, timeout),
        listener.accept_with_timeout(timeout),
    )
    let client_result = move connection.0
    let accepted_result = move connection.1
    let client = move client_result?
    let accepted = move accepted_result?

    let exchange = await task.with_timeout(
        task.join(exchange_request(move client), serve(move accepted.0)),
        timeout,
    )
    match move exchange {
        Timeout.completed(results) {
            let exchange_client_result = move results.0
            let server_result = move results.1
            let _client = move exchange_client_result?
            let _server = move server_result?
            return 0
        }
        Timeout.elapsed {
            return error.new("async-loopback.timeout", "buffered line exchange timed out")
        }
    }
}