Move release/1 from Conn to it's own protocol.

This commit is contained in:
James Harton 2020-01-12 16:55:54 +13:00
parent b5181189ef
commit a40af03ff5
15 changed files with 133 additions and 38 deletions

View file

@ -23,12 +23,18 @@ defmodule Wafer.Conn do
with {:ok, conn} <- Driver.acquire(bus_name: bus, address: address),
do: {:ok, %HTS221{conn: conn}}
end
def release(%HTS221{conn: conn}), do: Driver.release(conn)
end
```
"""
defmacro __using__(_env) do
quote do
@behaviour Wafer.Conn
end
Protocol.assert_impl!(Wafer.Release, __MODULE__)
end
@type t :: any
@type options :: [option]
@type option :: {atom, any}
@ -37,9 +43,4 @@ defmodule Wafer.Conn do
Acquire a connection to a peripheral using the provided driver.
"""
@callback acquire(options) :: {:ok, t} | {:error, reason :: any}
@doc """
Release all resources associated with this connection.
"""
@callback release(module) :: :ok | {:error, reason :: any}
end

View file

@ -36,14 +36,19 @@ defmodule Wafer.Driver.Circuits.GPIO do
{:error, reason} -> {:error, reason}
end
end
end
defimpl Wafer.Release, for: Wafer.Driver.Circuits.GPIO do
alias Wafer.Driver.Circuits.GPIO.Wrapper
alias Wafer.Driver.Circuits.GPIO
@doc """
Release all resources related to this GPIO pin connection.
Note that other connections may still be using the pin.
"""
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{ref: ref} = _conn) when is_reference(ref), do: Wrapper.close(ref)
@spec release(GPIO.t()) :: :ok | {:error, reason :: any}
def release(%GPIO{ref: ref} = _conn) when is_reference(ref), do: Wrapper.close(ref)
end
defimpl Wafer.GPIO, for: Wafer.Driver.Circuits.GPIO do

View file

@ -38,9 +38,17 @@ defmodule Wafer.Driver.Circuits.I2C do
{:error, reason}
end
end
end
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{ref: ref} = _conn), do: Wrapper.close(ref)
defimpl Wafer.Release, for: Wafer.Driver.Circuits.I2C do
alias Wafer.Driver.Circuits.I2C.Wrapper
alias Wafer.Driver.Circuits.I2C
@doc """
Release all resources associated with this device.
"""
@spec release(I2C.t()) :: :ok | {:error, reason :: any}
def release(%I2C{ref: ref} = _conn), do: Wrapper.close(ref)
end
defimpl Wafer.Chip, for: Wafer.Driver.Circuits.I2C do

View file

@ -34,12 +34,17 @@ defmodule Wafer.Driver.Circuits.SPI do
{:error, reason} -> {:error, reason}
end
end
end
defimpl Wafer.Release, for: Wafer.Driver.Circuits.SPI do
alias Wafer.Driver.Circuits.SPI.Wrapper
alias Wafer.Driver.Circuits.SPI
@doc """
Close the SPI bus connection.
"""
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{ref: ref} = _conn) when is_reference(ref), do: Wrapper.close(ref)
@spec release(SPI.t()) :: :ok | {:error, reason :: any}
def release(%SPI{ref: ref} = _conn) when is_reference(ref), do: Wrapper.close(ref)
end
defimpl Wafer.SPI, for: Wafer.Driver.Circuits.SPI do

View file

@ -35,14 +35,19 @@ defmodule Wafer.Driver.ElixirALE.GPIO do
{:error, reason} -> {:error, reason}
end
end
end
defimpl Wafer.Release, for: Wafer.Driver.ElixirALE.GPIO do
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
alias Wafer.Driver.ElixirALE.GPIO
@doc """
Release all resources related to this GPIO pin connection.
Note that other connections may still be using the pin.
"""
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{pid: pid} = _conn), do: Wrapper.release(pid)
@spec release(GPIO.t()) :: :ok | {:error, reason :: any}
def release(%GPIO{pid: pid} = _conn), do: Wrapper.release(pid)
end
defimpl Wafer.GPIO, for: Wafer.Driver.ElixirALE.GPIO do

View file

@ -38,9 +38,17 @@ defmodule Wafer.Driver.ElixirALE.I2C do
{:error, reason}
end
end
end
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{pid: pid} = _conn) when is_pid(pid), do: Wrapper.release(pid)
defimpl Wafer.Release, for: Wafer.Driver.ElixirALE.I2C do
alias Wafer.Driver.ElixirALE.I2C.Wrapper
alias Wafer.Driver.ElixirALE.I2C
@doc """
Release all resources associated with this I2C device.
"""
@spec release(I2C.t()) :: :ok | {:error, reason :: any}
def release(%I2C{pid: pid} = _conn) when is_pid(pid), do: Wrapper.release(pid)
end
defimpl Wafer.Chip, for: Wafer.Driver.ElixirALE.I2C do

View file

@ -35,12 +35,17 @@ defmodule Wafer.Driver.ElixirALE.SPI do
{:error, reason} -> {:error, reason}
end
end
end
defimpl Wafer.Release, for: Wafer.Driver.ElixirALE.SPI do
alias Wafer.Driver.ElixirALE.SPI.Wrapper
alias Wafer.Driver.ElixirALE.SPI
@doc """
Close the SPI bus connection.
"""
@spec release(t) :: :ok | {:error, reason :: any}
def release(%__MODULE__{pid: pid} = _conn) when is_pid(pid), do: Wrapper.release(pid)
@spec release(SPI.t()) :: :ok | {:error, reason :: any}
def release(%SPI{pid: pid} = _conn) when is_pid(pid), do: Wrapper.release(pid)
end
defimpl Wafer.SPI, for: Wafer.Driver.ElixirALE.SPI do

View file

@ -22,9 +22,6 @@ defmodule Wafer.Driver.Fake do
{:ok, %__MODULE__{opts: opts}}
end
@impl Wafer.Conn
def release(%__MODULE__{}), do: :ok
defp emit_warning do
:wafer
|> Application.get_env(__MODULE__)
@ -32,6 +29,10 @@ defmodule Wafer.Driver.Fake do
end
end
defimpl Wafer.Release, for: Wafer.Driver.Fake do
def release(%Wafer.Driver.Fake{}), do: :ok
end
defimpl Wafer.Chip, for: Wafer.Driver.Fake do
import Wafer.Guards

55
lib/wafer/release.ex Normal file
View file

@ -0,0 +1,55 @@
defprotocol Wafer.Release do
alias Wafer.Conn
@moduledoc """
A protocol for releasing connections. The opposite of `Conn`'s `acquire/1`.
## Deriving
If you're implementing your own `Conn` type that simply delegates to one of
the lower level drivers that you can derive this protocol automatically:
```elixir
defstruct MyConn do
@derive Wafer.Release
defstruct [:conn]
end
```
If your type uses a key other than `conn` for the inner connection you can specify it while deriving:
```elixir
defstruct MyConn do
@derive {Wafer.Release, key: :pin_conn}
defstruct [:pin_conn]
end
```
"""
@doc """
Release all resources associated with the connection. Usually in preparation
for shutdown.
"""
@spec release(Conn.t()) :: :ok
def release(conn)
end
defimpl Wafer.Release, for: Any do
defmacro __deriving__(module, struct, options) do
key = Keyword.get(options, :key, :conn)
unless Map.has_key?(struct, key) do
raise(
"Unable to derive `Wafer.Release` for `#{module}`: key `#{inspect(key)}` not present in struct."
)
end
quote do
defimpl Wafer.Release, for: unquote(module) do
def release(%{unquote(key) => inner_conn}), do: Wafer.Release.release(inner_conn)
end
end
end
def release(unknown), do: {:error, "`Wafer.Release` not implemented for #{inspect(unknown)}"}
end

View file

@ -5,6 +5,7 @@ defmodule WaferDriverCircuits.GPIOTest do
alias Wafer.Driver.Circuits.GPIO.Dispatcher, as: Dispatcher
alias Wafer.Driver.Circuits.GPIO.Wrapper
alias Wafer.GPIO, as: GPIO
alias Wafer.Release, as: Release
@moduledoc false
describe "acquire/1" do
@ -25,7 +26,7 @@ defmodule WaferDriverCircuits.GPIOTest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the pin" do
conn = conn()
@ -35,7 +36,7 @@ defmodule WaferDriverCircuits.GPIOTest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end

View file

@ -4,7 +4,7 @@ defmodule WaferCircuits.I2CTest do
alias Wafer.Chip
alias Wafer.Driver.Circuits.I2C, as: Subject
alias Wafer.Driver.Circuits.I2C.Wrapper
alias Wafer.I2C
alias Wafer.{Release, I2C}
@moduledoc false
describe "acquire/1" do
@ -72,7 +72,7 @@ defmodule WaferCircuits.I2CTest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the bus connection" do
conn = conn()
@ -82,7 +82,7 @@ defmodule WaferCircuits.I2CTest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end

View file

@ -3,7 +3,7 @@ defmodule WaferCircuits.SPITest do
use Mimic
alias Wafer.Driver.Circuits.SPI, as: Subject
alias Wafer.Driver.Circuits.SPI.Wrapper
alias Wafer.SPI
alias Wafer.{Release, SPI}
@moduledoc false
describe "acquire/1" do
@ -23,7 +23,7 @@ defmodule WaferCircuits.SPITest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the bus connection" do
conn = conn()
@ -33,7 +33,7 @@ defmodule WaferCircuits.SPITest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end

View file

@ -5,6 +5,7 @@ defmodule WaferDriverElixirALE.GPIOTest do
alias Wafer.Driver.ElixirALE.GPIO.Dispatcher, as: Dispatcher
alias Wafer.Driver.ElixirALE.GPIO.Wrapper
alias Wafer.GPIO, as: GPIO
alias Wafer.Release, as: Release
@moduledoc false
describe "acquire/1" do
@ -25,7 +26,7 @@ defmodule WaferDriverElixirALE.GPIOTest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the pin" do
conn = conn()
@ -35,7 +36,7 @@ defmodule WaferDriverElixirALE.GPIOTest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end

View file

@ -4,7 +4,7 @@ defmodule WaferElixirALE.I2CTest do
alias Wafer.Chip
alias Wafer.Driver.ElixirALE.I2C, as: Subject
alias Wafer.Driver.ElixirALE.I2C.Wrapper
alias Wafer.I2C
alias Wafer.{Release, I2C}
@moduledoc false
describe "acquire/1" do
@ -75,7 +75,7 @@ defmodule WaferElixirALE.I2CTest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the bus connection" do
conn = conn()
@ -85,7 +85,7 @@ defmodule WaferElixirALE.I2CTest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end

View file

@ -3,7 +3,7 @@ defmodule WaferElixirALE.SPITest do
use Mimic
alias Wafer.Driver.ElixirALE.SPI, as: Subject
alias Wafer.Driver.ElixirALE.SPI.Wrapper
alias Wafer.SPI
alias Wafer.{Release, SPI}
@moduledoc false
describe "acquire/1" do
@ -24,7 +24,7 @@ defmodule WaferElixirALE.SPITest do
end
end
describe "release/1" do
describe "Release.release/1" do
test "closes the bus connection" do
conn = conn()
@ -34,7 +34,7 @@ defmodule WaferElixirALE.SPITest do
:ok
end)
assert :ok = Subject.release(conn)
assert :ok = Release.release(conn)
end
end