README.md

# ExSharp

Call C# code from Elixir!

Note: requires C# Interactive (csi.exe) to be installed and included in your path

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed as:

  1. Add ex_sharp to your list of dependencies in `mix.exs`:

        def deps do
          [{:ex_sharp, "~> 0.0.1"}]
        end

  2. Ensure ex_sharp is started before your application:

        def application do
          [applications: [:ex_sharp]]
        end
        
## Usage
  
Using the following C# code in `Foo.csx`:
 
    using System;
    using ExSharp;
    
    [ExSharpModule("Foo")]
    public static class Foo 
    {
      [ExSharpFunction("pi", 0)]
      public static ElixirTerm Pi(ElixirTerm[] argv, int argc) 
      {
        return ElixirTerm.MakeDouble(3.14159);
      }
      
      [ExSharpFunction("echo", 1)]
      public static ElixirTerm Baz(ElixirTerm[] argv, int argc) 
      {
        var str = ElixirTerm.GetUTF8String(argv[0]);
        return ElixirTerm.MakeUTF8String($"from csharp: {str}");
      }
    }
    
    var runner = new ExSharp.Runner();
    runner.Run();
        
Create an `ExSharp.Roslyn` process with it's first argument being the path to said `.csx` script.
It is recommended that this process be supervised.

      Path.expand("../priv/Foo.csx", __DIR__)
      |> ExSharp.Roslyn.start_link
  
The following functions will become available at runtime:
  
  1. Foo.pi/0
  2. Foo.echo/1

## Supported Types

So far the following Elixir types are able to be retrieved/returned from C# code:

  * Integer
  * Float
  * Atom
  * Byte String
  * PID
  * Reference
  * Empty List
  * Tuple
  * Byte List ("String")
  * List
  * Export
  
## C# API

### Declare Module and Functions

    [ExSharpModule(module_name)]
    public static class ModuleName
    {
      [ExSharFunction(function_name, arity)]
      public static ElixirTerm Foo(ElixirTerm[] argv, int argc) 
      {
        ...
      }
      
      [ExSharFunction(function_name, arity)]
      public static void Foo(ElixirTerm[] argv, int argc) 
      {
        (functions that return void will return `:ok`)
        ...
      }
    }

### Type Conversions

  * Byte:
  
      ```
      byte? b = ElixirTerm.GetByte(argv[0]);
      ElixirTerm t = ElixirTerm.MakeByte(b);
      ```
      
  * Int:
      
      ```
      int? i = ElixirTerm.GetInt(argv[0]);
      ElixirTerm t = ElixirTerm.MakeInt(i);
      ```
      
  * Double:  
      
      ```
      double? d = ElixirTerm.GetDouble(argv[0]);
      ElixirTerm t = ElixirTerm.MakeDouble(d);
      ```
      
  * Atom:
      
      ```
      string a = ElixirTerm.GetAtom(argv[0]);
      ElixirTerm t = ElixirTerm.MakeAtom(a);
      ```
      
  * Byte String: 
      
      ```
      string s = ElixirTerm.GetUTF8String(argv[0]);
      ElixirTerm t = ElixirTerm.MakeUTF8String(s);
      ```
      
  * PID:
      
      ```        
      PID p = ElixirTerm.GetPID(argv[0]);
      ElixirTerm t = ElixirTerm.MakePID(p);
      ```
      
  * Reference:
      
      ```  
      Reference r = ElixirTerm.GetReference(argv[0]);
      ElixirTerm t = ElixirTerm.MakeReference(r);
      ```
      
  * Empty List:  
      
      ```  
      EmptyList e = ElixirTerm.GetEmptyList(argv[0]);
      ElixirTerm t = ElixirTerm.MakeEmptyList();
      ```
      
  * Tuple:
      
      ```
      Tuple t = ElixirTerm.GetTuple(argv[0]);
      ElixirTerm t = ElixirTerm.MakeTuple(new ElixirTerm[]{
        ElixirTerm.MakeAtom("error"), 
        ElixirTerm.MakeAtom("timeout")
      });
      ```
  
  * Byte List:
  
      ```
      byte[] b ElixirTerm.GetByteString(argv[0]);
      ElixirTerm t = ElixirTerm.MakeByteList(b);
      ```
  
  * List:
      
      ```
      ElixirTerm[] l = ElixirTerm.GetTuple(argv[0]);
      ElixirTerm t = ElixirTerm.MakeList(new ElixirTerm[]{
        ElixirTerm.MakeAtom("option1"), 
        ElixirTerm.MakeAtom("option2"),
        ElixirTerm.MakeEmptyList()
      });
      ```
      
  * Export:
  
      ```
      Export e1 = ElixirTerm.GetExport(argv[0]);
      Export e2 = new Export("String", "downcase", 1);
      ElixirTerm t = ElixirTerm.MakeExport(e2);
      ```

### Custom C# Types

  Read only properties:
  
  * PID
    * Node (string)
    * ID (int)
    * Serial (int)
    * Creation (byte)
  * Reference
    * Node (string)
    * Creation (byte)
    * ID (int[])
  * Tuple
    * Arity (int)
  * Export
    * Module (string)
    * Function (string)
    * Arity (byte)
  
  
  #### Tuple Usage
  
      Tuple t = ElixirTerm.GetTuple(argv[0]);
      string a = ElixirTerm.GetAtom(t[0]);
      string s = ElixirTerm.GetUTF8String(t[1]);

### Elixir Callbacks From C#

  You can call elixir functions or set up events to trigger them by using `ExSharp.Runner.ElixirCallback(fun, args)`.
  
  `fun` is an ElixirTerm with tag `TagType.NEW_FUN` or `TagType.EXPORT`.
  
  #### Examples  
  
      [ExSharpFunction("call", 0)]
      public static void Call(ElixirTerm[] argv, int argc)
      {
        ExSharp.Runner.ElixirCallback(argv[0], new ElixirTerm[0]);
      }      
      
      iex> fun = fn -> IO.puts "Hi!" end
      iex> Foo.call(fun)
      iex> :ok
      iex> HI