README.md

# Elixir XDR
![Build Badge](https://img.shields.io/github/workflow/status/kommitters/elixir_xdr/ElixirCI/master?style=for-the-badge)
[![Coverage Status](https://img.shields.io/coveralls/github/kommitters/elixir_xdr?style=for-the-badge)](https://coveralls.io/github/kommitters/elixir_xdr)
[![Version Badge](https://img.shields.io/hexpm/v/elixir_xdr?style=for-the-badge)](https://hexdocs.pm/elixir_xdr)
![Downloads Badge](https://img.shields.io/hexpm/dt/elixir_xdr?style=for-the-badge)
[![License badge](https://img.shields.io/hexpm/l/elixir_xdr.svg?style=for-the-badge)](https://github.com/kommitters/elixir_xdr/blob/master/LICENSE.md)

Process XDR types based on the [RFC4506](https://www.ietf.org/rfc/rfc4506.txt). Extend with ease to other XDR types.

## Installation
[Available in Hex](https://hex.pm/packages/elixir_xdr), Add `elixir_xdr` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:elixir_xdr, "~> 0.1.0"}
  ]
end
```

## Implemented types
The following XDR types are completely implemented in this library:

```elixir
# Basic types
XDR.Int                  # Section 4.1
XDR.UInt                 # Section 4.2
XDR.Bool                 # Section 4.4
XDR.HyperInt             # Section 4.5
XDR.HyperUInt            # Section 4.5
XDR.Float                # Section 4.6
XDR.DoubleFloat          # Section 4.7
XDR.Void                 # Section 4.16

# Complex types
XDR.Enum                 # Section 4.3
XDR.FixedOpaque          # Section 4.9
XDR.VariableOpaque       # Section 4.10
XDR.String               # Section 4.11
XDR.FixedArray           # Section 4.12
XDR.VariableArray        # Section 4.13
XDR.Struct               # Section 4.14
XDR.Union                # Section 4.15
XDR.Optional             # Section 4.19
```

**The following types were not implemented**
```
XDR.QuadFloat 	         # Section 4.8, not supported for 128-byte size.
XDR.Const 		         # Section 4.17, can be replaced with elixir constants.
XDR.Typedef 			 # Section 4.18, may be implemented with elixir modules. More info bellow in this guude.
```

## How to implement a XDR type?
**Behavior is the key**. When implementing a new XDR type follow this [Behavior's Declaration](https://github.com/kommitters/elixir_xdr/blob/develop/lib/xdr/declaration.ex).

## Decoding output
Encoded binaries may overflow the byte(s) size. That's why the returning value for decoding functions is set to be a tuple. Second element holds the remaining binary after decoding. **This applies to all XDR types**.
```elixir
iex> XDR.Int.decode_xdr!(<<0, 0, 4, 210, 5>>)
{%XDR.Int{datum: 1234}, <<5>>}
```

## Usage examples
### XDR.Int
For encoding integers use `encode_xdr/2` or use the raising version of the function `encode_xdr!/2`.
```elixir
iex> XDR.Int.new(1234) |> XDR.Int.encode_xdr()
{:ok, <<0, 0, 4, 210>>}

iex> XDR.Int.new(1234) |> XDR.Int.encode_xdr!()
<<0, 0, 4, 210>>
```
For decoding use `decode_xdr/2` or `decode_xdr!/2`.
```elixir
iex> XDR.Int.decode_xdr(<<0, 0, 4, 210>>)
{:ok, {%XDR.Int{datum: 1234}, <<>>}}

iex> XDR.Int.decode_xdr!(<<0, 0, 4, 210>>)
{%XDR.Int{datum: 1234}, <<>>}
```

### XDR.Bool
As mentioned before all the XDR types follow the same [Behavior's Declaration](https://github.com/kommitters/elixir_xdr/blob/develop/lib/xdr/declaration.ex)
```elixir
iex> XDR.Bool.new(true) |> XDR.Bool.encode_xdr()
{:ok, <<0, 0, 0, 0>>}

iex> XDR.Bool.new(true) |> XDR.Bool.encode_xdr!()
<<0, 0, 0, 0>>
```
For decoding the binary use `decode_xdr/2` or `decode_xdr!/2`..
```elixir
iex> XDR.Bool.decode_xdr(<<0, 0, 0, 1>>)
{:ok, {%XDR.Bool{declarations: [false: 0, true: 1], identifier: true}, ""}}

iex> XDR.Bool.decode_xdr!(<<0, 0, 0, 1>>)
{%XDR.Bool{declarations: [false: 0, true: 1], identifier: true}, ""}
```

### XDR.Enum
Enums are keywords lists containing a set of **declarations (statically defined)** and a **indentifier** with the key of the selected declaration.

The [XDR.Bool](https://github.com/kommitters/elixir_xdr/blob/develop/lib/xdr/bool.ex) is a clear example of an Enum implementation.
```elixir
iex> XDR.Bool.decode_xdr!<<0, 0, 0, 1>>)
{%XDR.Bool{declarations: [false: 0, true: 1], identifier: true}, ""}
```

### XDR.FixedOpaque
FixedOpaque is used for fixed-length uninterpreted data that needs to be passed among machines, in other words, let's think on a string that must match a fixed length.

```elixir
iex> ComplementBinarySize.new(<<1,2,3,4,5>>) |> ComplementBinarySize.encode_xdr()
{:ok, <<1, 2, 3, 4, 5, 0, 0, 0>>}
```
An example is available here: [FixedOpaque Type](https://github.com/kommitters/elixir_xdr/wiki/FixedOpaque-example).

### XDR.String
For econding strings.
```elixir
iex> XDR.String.new("The little prince") |> XDR.String.encode_xdr()
{:ok,
 <<0, 0, 0, 17, 84, 104, 101, 32, 108, 105, 116, 116, 108, 101, 32, 112, 114,
   105, 110, 99, 101, 0, 0, 0>>}

iex> XDR.String.new("The little prince") |> XDR.String.encode_xdr!()
<<0, 0, 0, 17, 84, 104, 101, 32, 108, 105, 116, 116, 108, 101, 32, 112, 114,
  105, 110, 99, 101, 0, 0, 0>>
```
For decoding strings.
```elixir
iex> XDR.String.decode_xdr(<<0, 0, 0, 17, 84, 104, 101, 32, 108, 105, 116, 116, 108, 101, 32, 112, 114,
  105, 110, 99, 101, 0, 0, 0>>)
{:ok, {%XDR.String{max_length: 4294967295, string: "The little prince"}, ""}}

iex> XDR.String.decode_xdr!(<<0, 0, 0, 17, 84, 104, 101, 32, 108, 105, 116, 116, 108, 101, 32, 112, 114,
  105, 110, 99, 101, 0, 0, 0>>)
{%XDR.String{max_length: 4294967295, string: "The little prince"}, ""}
```

### XDR.Struct
```elixir
iex(1)> name = XDR.String.new("The little prince")
%XDR.String{max_length: 4294967295, string: "The little prince"}

iex(2)> size = XDR.Int.new(298)
%XDR.Int{datum: 298}

iex(3)> Book.new(name, size) |> Book.encode_xdr()
{:ok,
 <<0, 0, 0, 17, 84, 104, 101, 32, 108, 105, 116, 116, 108, 101, 32, 112, 114,
   105, 110, 99, 101, 0, 0, 0, 0, 0, 1, 42>>}
```
An example is available here: [Struct Type](https://github.com/kommitters/elixir_xdr/wiki/Struct-example).


### XDR.Union
A union is a type composed of a discriminant **(Statement)** followed by a type selected from a set of prearranged types **(UnionStatement)**. The type of discriminant is either "Int", "Unsigned Int", or an Enumerated type, such as "Bool". The **(UnionStatement)** types are called "arms" of the union and are preceded by the value of the discriminant that implies their encoding.

```elixir
iex(1)> XDR.UnionStatement.new(:ST_NOMINATE) |> XDR.UnionStatement.encode_xdr()
{:ok, <<0, 0, 0, 3, 64, 93, 112, 164>>}

iex(3)> XDR.UnionStatement.decode_xdr(<<0, 0, 0, 3, 64, 93, 112, 164>>)
{:ok, {{:ST_NOMINATE, %XDR.Float{float: 3.4600000381469727}}, ""}}
```

An example is available here: [Union Example](https://github.com/kommitters/elixir_xdr/wiki/Union-example)

### XDR.Optional
```elixir
iex(1)> XDR.String.new("Hello") |> OptionalString.new() |> OptionalString.encode_xdr()
{:ok, <<0, 0, 0, 1, 0, 0, 0, 5, 72, 101, 108, 108, 111, 0, 0, 0>>}

iex(2)> OptionalString.decode_xdr(<<0, 0, 0, 1, 0, 0, 0, 5, 72, 101, 108, 108, 111, 0, 0, 0>>)
{:ok,
 {%XDR.Optional{type: %XDR.String{max_length: 4294967295, string: "Hello"}}, ""}}

iex(3)> OptionalString.new(nil) |> OptionalString.encode_xdr()
{:ok, <<0, 0, 0, 0>>}

iex(4)> OptionalString.decode_xdr(<<0, 0, 0, 0>>)
{:ok, {nil, ""}}
```

An example is available here: [Optional Type Example](https://github.com/kommitters/elixir_xdr/wiki/Optional-example)

## Contributing and Development
See [CONTRIBUTING.md](https://github.com/kommitters/elixir_xdr/blob/master/CONTRIBUTING.md)
for guidance on how to develop for this library.

Bug reports and pull requests are welcome on GitHub at https://github.com/kommitters/elixir_xdr.

Everyone is welcome to participate in the project.

## Changelog
See the [CHANGELOG](https://github.com/kommitters/elixir_xdr/blob/master/CHENGELOG.md) for versions details.

## License
See [LICENSE](https://github.com/kommitters/elixir_xdr/blob/master/LICENSE) for details.

## Credits
Made with 💙 from [kommit](https://kommit.co)