diff options
Diffstat (limited to 'stdlib/source/lux/world/net/udp.lux')
-rw-r--r-- | stdlib/source/lux/world/net/udp.lux | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/stdlib/source/lux/world/net/udp.lux b/stdlib/source/lux/world/net/udp.lux new file mode 100644 index 000000000..df9244186 --- /dev/null +++ b/stdlib/source/lux/world/net/udp.lux @@ -0,0 +1,124 @@ +(.module: + [lux #* + [host (#+ import:)] + ["@" target] + [abstract + monad] + [control + ["ex" exception (#+ exception:)] + ["." io (#+ IO)] + [concurrency + ["." promise (#+ Promise)]] + [security + ["!" capability]]] + [data + ["." error (#+ Error)] + ["." maybe] + [collection + ["." array]]] + [world + ["." binary (#+ Binary)]]] + ["." // (#+ Location Can-Read Can-Write Can-Close)]) + +(exception: #export (cannot-resolve-address {address //.Address}) + (ex.report ["Address" address])) + +(exception: #export (multiple-candidate-addresses {address //.Address}) + (ex.report ["Address" address])) + +(signature: #export (UDP !) + (: (Can-Read ! [Nat Location Binary]) + read) + + (: (Can-Write ! [Location Binary]) + write) + + (: (Can-Close !) + close)) + +(def: #export (async udp) + (-> (UDP IO) (UDP Promise)) + (`` (structure (~~ (template [<name> <forge>] + [(def: <name> (<forge> (|>> (!.use (:: udp <name>)) promise.future)))] + + [read //.can-read] + [write //.can-write] + [close //.can-close]))))) + +(with-expansions [<for-jvm> (as-is (import: java/lang/AutoCloseable + (close [] #io #try void)) + + (import: java/io/Flushable + (flush [] #io #try void)) + + (import: java/net/InetAddress + (#static getAllByName [String] #io #try [InetAddress]) + (getHostAddress [] String)) + + (import: java/net/DatagramPacket + (new #as new|send [[byte] int int InetAddress int]) + (new #as new|receive [[byte] int int]) + (getAddress [] InetAddress) + (getPort [] int) + (getLength [] int)) + + (import: java/net/DatagramSocket + (new #as new|client [] #io #try) + (new #as new|server [int] #io #try) + (receive [DatagramPacket] #io #try void) + (send [DatagramPacket] #io #try void)) + + (def: (resolve address) + (-> //.Address (IO (Error InetAddress))) + (do (error.with io.monad) + [addresses (InetAddress::getAllByName address)] + (: (IO (Error InetAddress)) + (case (array.size addresses) + 0 (io.io (ex.throw cannot-resolve-address address)) + 1 (wrap (maybe.assume (array.read 0 addresses))) + _ (io.io (ex.throw multiple-candidate-addresses address)))))) + + (def: (udp socket) + (-> DatagramSocket (UDP IO)) + (structure (def: read + (//.can-read + (function (read size) + (let [data (binary.create size) + packet (DatagramPacket::new|receive data +0 (.int size))] + (do (error.with io.monad) + [_ (DatagramSocket::receive packet socket) + #let [bytes-read (.nat (DatagramPacket::getLength packet))]] + (wrap [bytes-read + {#//.address (|> packet DatagramPacket::getAddress InetAddress::getHostAddress) + #//.port (.nat (DatagramPacket::getPort packet))} + data])))))) + + (def: write + (//.can-write + (function (write [location data]) + (do (error.with io.monad) + [address (resolve (get@ #//.address location))] + (DatagramSocket::send (DatagramPacket::new|send data +0 (.int (binary.size data)) address (.int (get@ #//.port location))) + socket))))) + + (def: close + (//.can-close + (function (close _) + (AutoCloseable::close socket)))))) + + (def: #export client + (IO (Error (UDP IO))) + (|> (DatagramSocket::new|client) + (:: (error.with io.monad) map ..udp))) + + (def: #export server + (-> //.Port (IO (Error (UDP IO)))) + (|>> .int + DatagramSocket::new|server + (:: (error.with io.monad) map ..udp))) + )] + (`` (for {(~~ (static @.old)) + (as-is <for-jvm>) + + (~~ (static @.jvm)) + (as-is <for-jvm>)}))) |