Rebus.Interface (rebus v0.4.0)
View SourceA validated declaration of a D-Bus interface: its methods, signals, properties and annotations.
One declaration describes an interface everywhere Rebus needs it: checking
the arguments of inbound calls, typing replies, signals and property values,
and generating introspection XML with Rebus.Introspection.
Example
calculator =
Rebus.Interface.new!("org.example.Calculator",
methods: [
{"Add", in: [a: "i", b: "i"], out: [sum: "i"]},
{"Log", in: [line: "s"], no_reply: true}
],
signals: [{"Computed", args: [sum: "i"]}],
properties: [
{"Precision", "u", :readwrite},
{"Version", "s", :read, emits_changed: :const}
]
)Declarations
new/2 takes the interface name and these options, each a list:
:methods-{name, opts}entries.optstakes:inand:outargument lists,:no_reply(a boolean, defaultfalse) and:annotations.:signals-{name, opts}entries.optstakes:argsand:annotations.:properties-{name, signature, access}or{name, signature, access, opts}entries, whereaccessis:read,:writeor:readwrite.optstakes:emits_changedand:annotations.:annotations-{name, value}pairs for the interface itself.
An argument list holds {name, signature} pairs, where the name is an atom
or a binary, so [a: "i", b: "i"] declares two named int32 arguments. A
bare signature declares an unnamed argument. Every argument and property
signature must be a single complete type.
no_reply: true adds the org.freedesktop.DBus.Method.NoReply annotation,
and :emits_changed adds org.freedesktop.DBus.Property.EmitsChangedSignal
when it differs from the default, so introspection reports both. Either
annotation may be given directly instead. An EmitsChangedSignal
annotation on the interface sets the default for its properties, as the
D-Bus specification describes; otherwise the default is true.
Summary
Types
Whether a property can be read, written, or both.
An introspection annotation: a name and a string value.
A reason why wrap_properties/2 could not wrap a value.
A declared D-Bus interface.
A reason why new/2 rejected a declaration.
Functions
The standard org.freedesktop.DBus.Introspectable interface.
Builds an interface declaration, validating every name, signature and annotation.
Builds an interface declaration, raising ArgumentError when it is invalid.
The standard org.freedesktop.DBus.ObjectManager interface.
The standard org.freedesktop.DBus.Peer interface.
The standard org.freedesktop.DBus.Properties interface.
Builds an org.freedesktop.DBus.Properties.PropertiesChanged signal for
properties declared by interface.
Builds a signal message for a signal declared by interface, typed by its
declaration.
Wraps property values as the a{sv} body element that GetAll replies and
PropertiesChanged signals carry, typing each value by its declaration.
Wraps property values as wrap_properties/2 does, raising ArgumentError
for a property the interface does not declare.
Types
@type access() :: :read | :write | :readwrite
Whether a property can be read, written, or both.
An introspection annotation: a name and a string value.
@type property_error() :: {:unknown_property, term()}
A reason why wrap_properties/2 could not wrap a value.
@type t() :: %Rebus.Interface{ annotations: [annotation()], methods: %{required(binary()) => Rebus.Interface.Method.t()}, name: binary(), properties: %{required(binary()) => Rebus.Interface.Property.t()}, signals: %{required(binary()) => Rebus.Interface.Signal.t()} }
A declared D-Bus interface.
@type validation_error() :: {:invalid_option, term()} | {:invalid_interface_name, term()} | {:invalid_member_name, term()} | {:invalid_method, term()} | {:invalid_signal, term()} | {:invalid_property, term()} | {:invalid_arg, term()} | {:invalid_arg_name, term()} | {:invalid_signature, term()} | {:invalid_access, term()} | {:invalid_emits_changed, term()} | {:invalid_annotation, term()} | {:conflicting_annotation, {member :: binary(), annotation :: binary()}} | {:duplicate_member, binary()} | {:duplicate_property, binary()} | {:duplicate_annotation, binary()} | {:unix_fd_in_no_reply, binary()}
A reason why new/2 rejected a declaration.
Each reason carries the offending value, or the name of the member it belongs to.
Functions
@spec introspectable() :: t()
The standard org.freedesktop.DBus.Introspectable interface.
@spec new( binary(), keyword() ) :: {:ok, t()} | {:error, validation_error()}
Builds an interface declaration, validating every name, signature and annotation.
Names follow the D-Bus grammar: the interface name is checked as an
interface name, and method, signal, property and argument names as member
names. Annotation names follow the interface-name grammar, and annotation
values must be strings that XML can carry. Duplicate methods, signals,
properties or annotations are rejected, as is a no-reply method with a Unix
file descriptor (h) among its out arguments.
Examples
iex> {:ok, interface} =
...> Rebus.Interface.new("org.example.Calculator",
...> methods: [{"Add", in: [a: "i", b: "i"], out: [sum: "i"]}]
...> )
iex> interface.methods["Add"].in_signature
"ii"
iex> Rebus.Interface.new("Calculator")
{:error, {:invalid_interface_name, "Calculator"}}
Builds an interface declaration, raising ArgumentError when it is invalid.
@spec object_manager() :: t()
The standard org.freedesktop.DBus.ObjectManager interface.
@spec peer() :: t()
The standard org.freedesktop.DBus.Peer interface.
@spec properties() :: t()
The standard org.freedesktop.DBus.Properties interface.
@spec properties_changed( t(), binary(), %{required(binary()) => term()}, [binary()], keyword() ) :: {:ok, Rebus.Message.t()} | {:error, property_error() | {:invalid_property_change, binary()} | {:invalid_option, term()} | Rebus.Message.construction_error()}
Builds an org.freedesktop.DBus.Properties.PropertiesChanged signal for
properties declared by interface.
changed maps property names to their new values, which are typed by the
declaration. invalidated names properties that changed without their
values. Each property's emits_changed decides where it may appear: one
that emits true only in changed, one that emits :invalidates only in
invalidated, and one that emits false or :const in neither.
opts takes :destination, :flags and :fds, as signal/5 does, so a
value of a property declared with a Unix file descriptor type can refer to
a descriptor.
Returns {:error, {:unknown_property, name}} for a property the interface
does not declare, {:error, {:invalid_property_change, name}} for one whose
declaration does not allow it where it appears, and Rebus.Message.new/2's
errors otherwise.
@spec signal(t(), binary(), binary(), [term()], keyword()) :: {:ok, Rebus.Message.t()} | {:error, :unknown_signal | {:invalid_option, term()} | Rebus.Message.construction_error()}
Builds a signal message for a signal declared by interface, typed by its
declaration.
opts takes :destination, :flags and :fds, as Rebus.Message.new/2
does. Returns {:error, :unknown_signal} when the interface does not
declare member, and Rebus.Message.new/2's errors otherwise; a body that
does not fit the declared signature is {:error, :invalid_body}.
Example
iex> interface =
...> Rebus.Interface.new!("org.example.Calculator",
...> signals: [{"Computed", args: [sum: "i"]}]
...> )
iex> {:ok, message} =
...> Rebus.Interface.signal(interface, "/org/example/Calc", "Computed", [42])
iex> Rebus.Message.signature(message)
"i"
@spec wrap_properties(t(), %{required(binary()) => term()}) :: {:ok, [{binary(), {binary(), term()}}]} | {:error, property_error()}
Wraps property values as the a{sv} body element that GetAll replies and
PropertiesChanged signals carry, typing each value by its declaration.
Entries are sorted by property name. Values are not checked against their
signatures here; the message they are placed in reports one that does not
fit as :invalid_body.
Example
iex> interface =
...> Rebus.Interface.new!("org.example.Calculator",
...> properties: [{"Precision", "u", :readwrite}, {"Version", "s", :read}]
...> )
iex> Rebus.Interface.wrap_properties(interface, %{"Version" => "1.0", "Precision" => 3})
{:ok, [{"Precision", {"u", 3}}, {"Version", {"s", "1.0"}}]}
Wraps property values as wrap_properties/2 does, raising ArgumentError
for a property the interface does not declare.