summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorEdward Sankey <esnk86@gmail.com>2022-09-29 22:13:14 +1300
committerEdward Sankey <esnk86@gmail.com>2022-09-29 22:13:14 +1300
commitd05c17e3750538f97598a0d546a2b1b84045ec13 (patch)
tree9550209d9f095c7602f3dd70bb4556bc5c23d124
downloadping-d05c17e3750538f97598a0d546a2b1b84045ec13.tar.gz
first commit
-rw-r--r--LICENSE15
-rw-r--r--main.ex134
2 files changed, 149 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0b27807
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2022 Edward Sankey
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/main.ex b/main.ex
new file mode 100644
index 0000000..acee7a0
--- /dev/null
+++ b/main.ex
@@ -0,0 +1,134 @@
+defmodule Message do
+    defstruct sender_name: "",
+              sender_pid:  nil,
+              type:        nil,
+              args:        []
+end
+
+defmodule Info do
+    defstruct name:   "",
+              others: %{}
+end
+
+defmodule Server do
+    defstruct pid:        nil,
+              client_pid: nil,
+              handler:    nil,
+              info:       %Info{}
+
+    def run(name, handler) do
+        state = %Server {
+            pid:     self(),
+            handler: handler,
+            info:    %Info{name: name}
+        }
+
+        server(state)
+    end
+
+    defp server(state) do
+        state = receive do msg ->
+            case msg.type do
+                :start -> start(state, msg)
+                :ping  -> pong(state, msg)
+                _      -> state.handler.(state, msg)
+            end
+        end
+
+        server(state)
+    end
+
+    defp start(state, msg) do
+        [others|_] = msg.args
+        state = put_in(state.info.others, others)
+
+        client_pid = spawn(Client, :run, [self(), state.info])
+        state = put_in(state.client_pid, client_pid)
+
+        state
+    end
+
+    defp pong(state, msg) do
+        IO.puts "<#{state.info.name}> #{msg.sender_name}: PONG!"
+        state
+    end
+end
+
+defmodule Client do
+    defstruct pid:        nil,
+              server_pid: nil,
+              info:       %Info{}
+
+    def run(server_pid, info) do
+        me = %Client{
+            pid:        self(),
+            server_pid: server_pid,
+            info:       info,
+        }
+
+        Enum.each info.others, fn other ->
+            ping(me, other)
+        end
+    end
+
+    defp ping(me, {recp_name, recp_pid}) do
+        msg = %Message{
+            sender_name: me.info.name,
+            sender_pid:  me.server_pid,
+            type:        :ping,
+            args:        []
+        }
+
+        IO.puts "<#{me.info.name}> #{recp_name}: PING!"
+        send(recp_pid, msg)
+    end
+end
+
+defmodule Person do
+    def run(name, handler) do
+        spawn(Server, :run, [name, handler])
+    end
+end
+
+defmodule Ref do
+    def run() do
+        get_names(4) |> get_people() |> start_people()
+    end
+
+    defp get_names(n) do
+        Enum.map (1..n), &(String.to_atom inspect &1)
+    end
+
+    defp get_people(names) do
+        default_handler = fn state, msg ->
+            IO.puts "unhandled #{inspect msg.type}"
+            state
+        end
+
+        Keyword.new names, fn name ->
+            {name, Person.run(name, default_handler)}
+        end
+    end
+
+    defp start_people(people) do
+        Enum.each people, fn person = {_name, server} ->
+            others = Enum.filter people, fn other ->
+                other != person
+            end
+
+            msg = %Message{
+                sender_name: "ref",
+                sender_pid:  self(),
+                type:        :start,
+                args:        [others]}
+
+            send(server, msg)
+        end
+
+        people
+    end
+end
+
+defmodule Main do
+    Ref.run()
+end