Skip to content

Commit 50e8ce2

Browse files
committed
Update GPIO code to use gpio_specs
1 parent fb220c9 commit 50e8ce2

9 files changed

Lines changed: 71 additions & 85 deletions

File tree

config/config.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ config :circuits_sim,
1414
{CircuitsSim.Device.SGP30, bus_name: "i2c-1", address: 0x58},
1515
{CircuitsSim.Device.BMP3XX, bus_name: "i2c-1", address: 0x77},
1616
{CircuitsSim.Device.TM1620, bus_name: "spidev0.0", render: :binary_clock},
17-
{CircuitsSim.Device.GPIOLED, pin_spec: 10},
18-
{CircuitsSim.Device.GPIOButton, pin_spec: 11}
17+
{CircuitsSim.Device.GPIOLED, gpio_spec: 10},
18+
{CircuitsSim.Device.GPIOButton, gpio_spec: 11}
1919
]
2020

2121
# Default to simulated versions of I2C, SPI, and GPIO

lib/circuits_sim.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ defmodule CircuitsSim do
4646

4747
defp gpio_info() do
4848
DeviceRegistry.bus_names(:gpio)
49-
|> Enum.map(&pin_spec_info/1)
49+
|> Enum.map(&gpio_spec_info/1)
5050
end
5151

52-
defp pin_spec_info(pin_spec) do
53-
result = GPIOServer.render(pin_spec)
54-
["=== GPIO ", inspect(pin_spec), " ===\n", result]
52+
defp gpio_spec_info(gpio_spec) do
53+
result = GPIOServer.render(gpio_spec)
54+
["=== GPIO ", inspect(gpio_spec), " ===\n", result]
5555
end
5656
end

lib/circuits_sim/device/gpio_button.ex

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,18 @@ defmodule CircuitsSim.Device.GPIOButton do
4444
Pass in a duration in milliseconds to automatically release the button after
4545
a timeout.
4646
"""
47-
@spec press(GPIO.pin_spec(), non_neg_integer() | :infinity) :: :ok
48-
def press(pin_spec, duration \\ :infinity)
47+
@spec press(GPIO.gpio_spec(), non_neg_integer() | :infinity) :: :ok
48+
def press(gpio_spec, duration \\ :infinity)
4949
when duration == :infinity or (duration > 0 and duration < 10000) do
50-
GPIOServer.send_message(pin_spec, {:press, duration})
50+
GPIOServer.send_message(gpio_spec, {:press, duration})
5151
end
5252

5353
@doc """
5454
Release the button
5555
"""
56-
@spec release(GPIO.pin_spec()) :: :ok
57-
def release(pin_spec) do
58-
GPIOServer.send_message(pin_spec, :release)
56+
@spec release(GPIO.gpio_spec()) :: :ok
57+
def release(gpio_spec) do
58+
GPIOServer.send_message(gpio_spec, :release)
5959
end
6060

6161
defimpl GPIODevice do

lib/circuits_sim/device_registry.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ defmodule CircuitsSim.DeviceRegistry do
66

77
@type type() :: :i2c | :spi | :gpio | :all
88

9-
@spec via_name(type(), String.t() | GPIO.pin_spec(), I2C.address()) :: {:via, Registry, tuple()}
9+
@spec via_name(type(), String.t() | GPIO.gpio_spec(), I2C.address()) ::
10+
{:via, Registry, tuple()}
1011
def via_name(type, bus, address) do
1112
{:via, Registry, {CircuitSim.DeviceRegistry, {type, bus, address}}}
1213
end
1314

14-
@spec bus_names(type()) :: [String.t() | GPIO.pin_spec()]
15+
@spec bus_names(type()) :: [String.t() | GPIO.gpio_spec()]
1516
def bus_names(type) do
1617
# The select returns [{{:i2c, "i2c-0", 32}}]
1718
Registry.select(CircuitSim.DeviceRegistry, [{{:"$1", :_, :_}, [], [{{:"$1"}}]}])

lib/circuits_sim/gpio/backend.ex

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ defmodule CircuitsSim.GPIO.Backend do
44
"""
55
@behaviour Circuits.GPIO.Backend
66

7-
alias Circuits.GPIO
87
alias Circuits.GPIO.Backend
98
alias CircuitsSim.DeviceRegistry
109
alias CircuitsSim.GPIO.GPIOServer
@@ -14,25 +13,21 @@ defmodule CircuitsSim.GPIO.Backend do
1413
Open an GPIO handle
1514
"""
1615
@impl Backend
17-
def open(pin_spec, direction, options) do
18-
if pin_spec in pin_specs(options) do
19-
handle = %Handle{pin_spec: pin_spec}
20-
21-
with :ok <- GPIOServer.set_direction(pin_spec, direction),
22-
:ok <- set_pull_mode(pin_spec, options[:pull_mode]),
23-
:ok <- set_initial_value(pin_spec, options[:initial_value]) do
24-
{:ok, handle}
25-
end
26-
else
27-
{:error, "Unknown GPIO pin_spec #{inspect(pin_spec)}"}
16+
def open(gpio_spec, direction, options) do
17+
with {:ok, identifiers} <- identifiers(gpio_spec, options),
18+
handle = %Handle{gpio_spec: identifiers.gpio_spec},
19+
:ok <- GPIOServer.set_direction(identifiers.gpio_spec, direction),
20+
:ok <- set_pull_mode(identifiers.gpio_spec, options[:pull_mode]),
21+
:ok <- set_initial_value(identifiers.gpio_spec, options[:initial_value]) do
22+
{:ok, handle}
2823
end
2924
end
3025

31-
defp set_pull_mode(_pin_spec, :not_set), do: :ok
32-
defp set_pull_mode(pin_spec, pull_mode), do: GPIOServer.set_pull_mode(pin_spec, pull_mode)
26+
defp set_pull_mode(_gpio_spec, :not_set), do: :ok
27+
defp set_pull_mode(gpio_spec, pull_mode), do: GPIOServer.set_pull_mode(gpio_spec, pull_mode)
3328

34-
defp set_initial_value(_pin_spec, :not_set), do: :ok
35-
defp set_initial_value(pin_spec, value), do: GPIOServer.write(pin_spec, value)
29+
defp set_initial_value(_gpio_spec, :not_set), do: :ok
30+
defp set_initial_value(gpio_spec, value), do: GPIOServer.write(gpio_spec, value)
3631

3732
@doc """
3833
Return information about this backend
@@ -41,9 +36,4 @@ defmodule CircuitsSim.GPIO.Backend do
4136
def info() do
4237
%{backend: __MODULE__}
4338
end
44-
45-
@spec pin_specs(keyword()) :: [GPIO.pin_spec()]
46-
def pin_specs(_options) do
47-
DeviceRegistry.bus_names(:gpio)
48-
end
4939
end

lib/circuits_sim/gpio/gpio_server.ex

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ defmodule CircuitsSim.GPIO.GPIOServer do
99

1010
require Logger
1111

12-
defstruct pin_spec: nil,
12+
defstruct gpio_spec: nil,
1313
device: nil,
1414
direction: :input,
1515
pull_mode: :none,
1616
cached_value: 0,
1717
interrupt_receiver: nil,
1818
interrupt_trigger: :none
1919

20-
@type init_args :: [pin_spec: GPIO.pin_spec()]
20+
@type init_args :: [gpio_spec: GPIO.gpio_spec()]
2121

2222
@doc """
2323
Helper for creating child_specs for simple I2C implementations
@@ -27,9 +27,9 @@ defmodule CircuitsSim.GPIO.GPIOServer do
2727
:start => {__MODULE__, :start_link, [[any()], ...]}
2828
}
2929
def child_spec_helper(device, args) do
30-
pin_spec = Keyword.fetch!(args, :pin_spec)
30+
gpio_spec = Keyword.fetch!(args, :gpio_spec)
3131

32-
combined_args = Keyword.merge([device: device, name: via_name(pin_spec)], args)
32+
combined_args = Keyword.merge([device: device, name: via_name(gpio_spec)], args)
3333

3434
%{
3535
id: __MODULE__,
@@ -39,64 +39,59 @@ defmodule CircuitsSim.GPIO.GPIOServer do
3939

4040
@spec start_link(keyword()) :: GenServer.on_start()
4141
def start_link(init_args) do
42-
pin_spec = Keyword.fetch!(init_args, :pin_spec)
42+
gpio_spec = Keyword.fetch!(init_args, :gpio_spec)
4343

44-
GenServer.start_link(__MODULE__, init_args, name: via_name(pin_spec))
44+
GenServer.start_link(__MODULE__, init_args, name: via_name(gpio_spec))
4545
end
4646

4747
# Helper for constructing the via_name for GPIODevice servers
48-
defp via_name(pin_spec) do
49-
DeviceRegistry.via_name(:gpio, pin_spec, 0)
48+
defp via_name(gpio_spec) do
49+
DeviceRegistry.via_name(:gpio, gpio_spec, 0)
5050
end
5151

52-
@spec write(GPIO.pin_spec(), GPIO.value()) :: :ok
53-
def write(pin_spec, value) do
54-
GenServer.call(via_name(pin_spec), {:write, value})
52+
@spec write(GPIO.gpio_spec(), GPIO.value()) :: :ok
53+
def write(gpio_spec, value) do
54+
GenServer.call(via_name(gpio_spec), {:write, value})
5555
end
5656

57-
@spec read(GPIO.pin_spec()) :: GPIO.value()
58-
def read(pin_spec) do
59-
GenServer.call(via_name(pin_spec), :read)
57+
@spec read(GPIO.gpio_spec()) :: GPIO.value()
58+
def read(gpio_spec) do
59+
GenServer.call(via_name(gpio_spec), :read)
6060
end
6161

62-
@spec set_direction(GPIO.pin_spec(), GPIO.pin_direction()) :: :ok | {:error, atom()}
63-
def set_direction(pin_spec, direction) do
64-
GenServer.call(via_name(pin_spec), {:set_direction, direction})
62+
@spec set_direction(GPIO.gpio_spec(), GPIO.pin_direction()) :: :ok | {:error, atom()}
63+
def set_direction(gpio_spec, direction) do
64+
GenServer.call(via_name(gpio_spec), {:set_direction, direction})
6565
end
6666

67-
@spec set_interrupts(GPIO.pin_spec(), GPIO.trigger(), GPIO.interrupt_options()) ::
67+
@spec set_interrupts(GPIO.gpio_spec(), GPIO.trigger(), GPIO.interrupt_options()) ::
6868
:ok | {:error, atom()}
69-
def set_interrupts(pin_spec, trigger, options) do
69+
def set_interrupts(gpio_spec, trigger, options) do
7070
receiver = options[:receiver] || self()
71-
GenServer.call(via_name(pin_spec), {:set_interrupts, trigger, receiver})
71+
GenServer.call(via_name(gpio_spec), {:set_interrupts, trigger, receiver})
7272
end
7373

74-
@spec set_pull_mode(GPIO.pin_spec(), GPIO.pull_mode()) :: :ok | {:error, atom()}
75-
def set_pull_mode(pin_spec, pull_mode) do
76-
GenServer.call(via_name(pin_spec), {:set_pull_mode, pull_mode})
74+
@spec set_pull_mode(GPIO.gpio_spec(), GPIO.pull_mode()) :: :ok | {:error, atom()}
75+
def set_pull_mode(gpio_spec, pull_mode) do
76+
GenServer.call(via_name(gpio_spec), {:set_pull_mode, pull_mode})
7777
end
7878

79-
@spec info(GPIO.pin_spec()) :: map()
80-
def info(pin_spec) do
81-
GenServer.call(via_name(pin_spec), :info)
79+
@spec render(GPIO.gpio_spec()) :: IO.ANSI.ansidata()
80+
def render(gpio_spec) do
81+
GenServer.call(via_name(gpio_spec), :render)
8282
end
8383

84-
@spec render(GPIO.pin_spec()) :: IO.ANSI.ansidata()
85-
def render(pin_spec) do
86-
GenServer.call(via_name(pin_spec), :render)
87-
end
88-
89-
@spec send_message(GPIO.pin_spec(), any()) :: any()
90-
def send_message(pin_spec, message) do
91-
GenServer.call(via_name(pin_spec), {:send_message, message})
84+
@spec send_message(GPIO.gpio_spec(), any()) :: any()
85+
def send_message(gpio_spec, message) do
86+
GenServer.call(via_name(gpio_spec), {:send_message, message})
9287
end
9388

9489
@impl GenServer
9590
def init(init_args) do
96-
pin_spec = Keyword.fetch!(init_args, :pin_spec)
91+
gpio_spec = Keyword.fetch!(init_args, :gpio_spec)
9792
device = Keyword.fetch!(init_args, :device)
9893

99-
{:ok, %__MODULE__{pin_spec: pin_spec, device: device}}
94+
{:ok, %__MODULE__{gpio_spec: gpio_spec, device: device}}
10095
end
10196

10297
@impl GenServer
@@ -109,7 +104,7 @@ defmodule CircuitsSim.GPIO.GPIOServer do
109104
{:reply, :ok, %{state | device: new_device, cached_value: value}}
110105

111106
:input ->
112-
Logger.warning("Ignoring write to input GPIO #{inspect(state.pin_spec)}")
107+
Logger.warning("Ignoring write to input GPIO #{inspect(state.gpio_spec)}")
113108
{:reply, :ok, state}
114109
end
115110
end
@@ -183,7 +178,7 @@ defmodule CircuitsSim.GPIO.GPIOServer do
183178

184179
defp process_read(state, :hi_z) do
185180
Logger.warning(
186-
"GPIO #{inspect(state.pin_spec)} is in high impedance state. Set pull mode to reliably read."
181+
"GPIO #{inspect(state.gpio_spec)} is in high impedance state. Set pull mode to reliably read."
187182
)
188183

189184
0
@@ -211,6 +206,6 @@ defmodule CircuitsSim.GPIO.GPIOServer do
211206
defp send_interrupt_message(state, value) do
212207
{uptime_ms, _} = :erlang.statistics(:wall_clock)
213208
uptime_ns = uptime_ms * 1_000_000
214-
send(state.interrupt_receiver, {:circuits_gpio, state.pin_spec, uptime_ns, value})
209+
send(state.interrupt_receiver, {:circuits_gpio, state.gpio_spec, uptime_ns, value})
215210
end
216211
end

lib/circuits_sim/gpio/handle.ex

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ defmodule CircuitsSim.GPIO.Handle do
44
alias Circuits.GPIO.Handle
55
alias CircuitsSim.GPIO.GPIOServer
66

7-
defstruct [:pin_spec]
7+
defstruct [:gpio_spec]
88

99
@type t() :: %__MODULE__{
10-
pin_spec: Circuits.GPIO.pin_spec()
10+
gpio_spec: Circuits.GPIO.gpio_spec()
1111
}
1212

1313
@spec render(t()) :: String.t()
1414
def render(%__MODULE__{} = handle) do
15-
GPIOServer.render(handle.pin_spec)
15+
GPIOServer.render(handle.gpio_spec)
1616
|> IO.ANSI.format()
1717
|> IO.chardata_to_string()
1818
end
@@ -22,27 +22,27 @@ defmodule CircuitsSim.GPIO.Handle do
2222

2323
@impl Handle
2424
def read(%CircuitsSim.GPIO.Handle{} = handle) do
25-
GPIOServer.read(handle.pin_spec)
25+
GPIOServer.read(handle.gpio_spec)
2626
end
2727

2828
@impl Handle
2929
def write(%CircuitsSim.GPIO.Handle{} = handle, value) do
30-
GPIOServer.write(handle.pin_spec, value)
30+
GPIOServer.write(handle.gpio_spec, value)
3131
end
3232

3333
@impl Handle
3434
def set_direction(%CircuitsSim.GPIO.Handle{} = handle, direction) do
35-
GPIOServer.set_direction(handle.pin_spec, direction)
35+
GPIOServer.set_direction(handle.gpio_spec, direction)
3636
end
3737

3838
@impl Handle
3939
def set_interrupts(%CircuitsSim.GPIO.Handle{} = handle, trigger, options) do
40-
GPIOServer.set_interrupts(handle.pin_spec, trigger, options)
40+
GPIOServer.set_interrupts(handle.gpio_spec, trigger, options)
4141
end
4242

4343
@impl Handle
4444
def set_pull_mode(%CircuitsSim.GPIO.Handle{} = handle, pull_mode) do
45-
GPIOServer.set_pull_mode(handle.pin_spec, pull_mode)
45+
GPIOServer.set_pull_mode(handle.gpio_spec, pull_mode)
4646
end
4747

4848
@impl Handle

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ defmodule CircuitsSim.MixProject do
4545
[
4646
{:circuits_i2c, "~> 2.0", override: true},
4747
{:circuits_spi, "~> 2.0"},
48-
{:circuits_gpio, "~> 2.0.0-pre.0"},
48+
{:circuits_gpio, "~> 2.0"},
4949
{:bmp280, "~> 0.2.12", only: [:dev, :test]},
5050
{:bmp3xx, "~> 0.1.5", only: [:dev, :test]},
5151
{:sgp30, github: "jjcarstens/sgp30", branch: "main", only: [:dev, :test]},

mix.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
"bmp3xx": {:hex, :bmp3xx, "0.1.6", "305a20e9f684ce7efc59185f20c17ddd3f9a5ce5fbf5fbeca1c0d17d58042d05", [:mix], [{:circuits_i2c, "~> 0.3 or ~> 1.0 or ~> 2.0", [hex: :circuits_i2c, repo: "hexpm", optional: false]}], "hexpm", "4b4f1e0eb62a9d0b8c7ffaba456e386848fa75328bc66a65e9cfa77a28e9be9d"},
55
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
66
"cerlc": {:hex, :cerlc, "0.2.1", "cfe0880aa049ebcca079ca49578055aa48e7f2e9ed8ae08bd1f919d59015d03f", [:rebar3], [], "hexpm", "37f0d74a4277dcbaf64c7c47e9953dcc8060d26d86bb466ab2a86928e0181a7d"},
7-
"circuits_gpio": {:hex, :circuits_gpio, "2.0.0-pre.0", "122a76a90f49cc2e83483157012fb7eff194ad80787567a2a6c1bd5133b989a7", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "454449f11527ff70da428765f725ef84263f5d68919dbb830a4868315ed0823e"},
7+
"circuits_gpio": {:hex, :circuits_gpio, "2.0.2", "84225714b0d698fb8a7dfbbc900bbc1bbf57f4ad14dca3af15174f701a5b9a45", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "697e80b0e7ed4fd14bbab6c7e8b8b68d43f58fc1eff0ee800262c4f9b31f56c6"},
88
"circuits_i2c": {:hex, :circuits_i2c, "2.0.1", "e9ab22f078b403cafd2c5006fff448f9a790f73c04e9135b7ac22f45f2e63c5c", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "a1567004389b49637fe34d9dd8bdf36c26a53c269e654753860b5c09532bc3c4"},
99
"circuits_spi": {:hex, :circuits_spi, "2.0.0", "9198c15c7ffce5bc353e13b7c82df3fe99d7332bd7c28fb9a62c60ce1e7567dc", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2eddec1fe6486a16f77f72eca91d50a806f9365033d96a517381b61d747612c2"},
1010
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
1111
"credo_binary_patterns": {:hex, :credo_binary_patterns, "0.2.3", "0dabadbe3cfd8db14b69ff346c112bfadde9bf65dc7aea19c39743c8d2ed07fa", [:mix], [{:credo, "~> 1.6", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "3c333a564ed3e27f5c9f69985a921b88ef90f131bf722d085957cc4b25b7a085"},
1212
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
1313
"earmark_parser": {:hex, :earmark_parser, "1.4.38", "b42252eddf63bda05554ba8be93a1262dc0920c721f1aaf989f5de0f73a2e367", [:mix], [], "hexpm", "2cd0907795aaef0c7e8442e376633c5b3bd6edc8dbbdc539b22f095501c1cdb6"},
14-
"elixir_make": {:hex, :elixir_make, "0.7.7", "7128c60c2476019ed978210c245badf08b03dbec4f24d05790ef791da11aa17c", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "5bc19fff950fad52bbe5f211b12db9ec82c6b34a9647da0c2224b8b8464c7e6c"},
14+
"elixir_make": {:hex, :elixir_make, "0.7.8", "505026f266552ee5aabca0b9f9c229cbb496c689537c9f922f3eb5431157efc7", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "7a71945b913d37ea89b06966e1342c85cfe549b15e6d6d081e8081c493062c07"},
1515
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
1616
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
1717
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},

0 commit comments

Comments
 (0)