Skip to content

Commit

Permalink
Checkpoint commit, can roundtrip simple structs
Browse files Browse the repository at this point in the history
  • Loading branch information
nullstyle committed Feb 3, 2015
1 parent 3ec19d7 commit d0e72ff
Show file tree
Hide file tree
Showing 27 changed files with 347 additions and 34 deletions.
3 changes: 2 additions & 1 deletion Rakefile
Expand Up @@ -5,4 +5,5 @@ begin
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
rescue LoadError
end
end

77 changes: 45 additions & 32 deletions docs/codegen/union.md
@@ -1,57 +1,70 @@
```xdr

enum LedgerType {
ACCOUNT,
TRUSTLINE,
OFFER
enum MyUnionType {
TYPE_1,
TYPE_2,
TYPE_3,
TYPE_4
};

union LedgerEntry switch (LedgerType type)
union MyUnion switch (MyUnionType type)
{
case ACCOUNT:
AccountEntry account;
case TYPE_1:
hyper typeOneVal;

case TRUSTLINE:
TrustLineEntry trustLine;
case TYPE_2:
case TYPE_3:
int typeTwoVal;

case OFFER:
OfferEntry offer;
case TYPE_4:
void;

default:
CustomType typeThreeVal;
};

```

becomes:

```ruby
module LedgerType
ACCOUNT = 0
TRUSTLINE = 1
OFFER = 2
module MyUnionType
TYPE_1 = 0
TYPE_2 = 1
TYPE_3 = 2
TYPE_4 = 3
end

class LedgerEntry
include XDR::Union
class MyUnion < XDR::Union

switch_on :type, MyUnionType

switch MyUnionType::TYPE_1, :type_one_val
switch MyUnionType::TYPE_2, :type_two_val
switch MyUnionType::TYPE_3, :type_two_val
switch MyUnionType::TYPE_4 # void
switch :type_three_val # default

arm :account, LedgerType::ACCOUNT, AccountEntry
arm :trust_line, LedgerType::TRUSTLINE, TrustLineEntry
arm :offer, LedgerType::OFFER, OfferEntry
attribute :type_one_val, XDR::Hyper
attribute :type_two_val, XDR::Int
attribute :type_three_val, CustomType
end
```

usage:

```ruby
LedgerEntry.new # => raise

entry = LedgerEntry.new(:account, AccountEntry.new)
entry.arm # => 0
entry.get # => #<AccountEntry:...>
entry.account! # => #<AccountEntry:...>
entry.trust_line! # => raise ArmNotSetError

entry.set :trust_line, TrustLineEntry.new
entry.arm # => 1
entry.get # => #<TrustLineEntry:...>
entry.trust_line! # => #<TrustLineEntry:...>
MyUnion.new # => raise

val = MyUnion.new(:type_one_val, 100)
val.arm # => 0
val.get # => 100
val.type_one_val! # => 100
val.type_two_val! # => raise ArmNotSetError

val.set :default, CustomType.new
val.arm # => :default
val.get # => #<CustomType:...>
val.type_three_val! # => #<CustomType:...>

```
23 changes: 23 additions & 0 deletions lib/xdr.rb
@@ -1,18 +1,41 @@
require "xdr/version"
require "active_model"
require "active_support/dependencies/autoload"
require "active_support/core_ext/object/blank"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/class/attribute.rb"
require "active_support/logger"
require "active_support/ordered_hash"

module XDR
extend ActiveSupport::Autoload

autoload :Primitives
autoload :RPC
autoload :DSL

# Compound Type
autoload :Struct
autoload :Union
autoload :Enum

# Primitive Types
autoload :Option
autoload :Int
autoload :Bool
autoload :Opaque
autoload :VarOpaque
autoload :VarArray
autoload :String

# Validators
autoload :StructValidator
autoload :UnionValidator

module Concerns
extend ActiveSupport::Autoload
autoload :ReadsBytes
autoload :ConvertsToXDR
end

class Error < StandardError ; end
Expand Down
9 changes: 9 additions & 0 deletions lib/xdr/bool.rb
@@ -0,0 +1,9 @@
module XDR::Bool
include XDR::Concerns::ConvertsToXDR

def converter
XDR::Primitives::BOOL
end

extend self
end
15 changes: 15 additions & 0 deletions lib/xdr/concerns/converts_to_xdr.rb
@@ -0,0 +1,15 @@
module XDR::Concerns::ConvertsToXDR
def to_xdr(val)
xdr_serializer.to_xdr(val)
end

def from_xdr(io)
io = io.is_a?(String) ? StringIO.new(io) : io

xdr_serializer.from_xdr(io)
end

def xdr_serializer
raise NotImplementedError, "implement in including class"
end
end
5 changes: 5 additions & 0 deletions lib/xdr/concerns/reads_bytes.rb
Expand Up @@ -5,4 +5,9 @@ def read_bytes(io, length)
raise EOFError if bytes.nil? || bytes.length != length
end
end

def write_bytes(io, content)
io.write(content)
content
end
end
6 changes: 6 additions & 0 deletions lib/xdr/dsl.rb
@@ -0,0 +1,6 @@
module XDR::DSL
extend ActiveSupport::Autoload

autoload :Struct
autoload :Union
end
11 changes: 11 additions & 0 deletions lib/xdr/dsl/struct.rb
@@ -0,0 +1,11 @@
module XDR::DSL::Struct
def attribute(name, type)

raise ArgumentError, "#{type} does not convert to xdr" unless type.is_a?(XDR::Concerns::ConvertsToXDR)

self.fields = self.fields.merge(name => type)

attr_accessor name
define_attribute_methods name
end
end
12 changes: 12 additions & 0 deletions lib/xdr/dsl/union.rb
@@ -0,0 +1,12 @@
module XDR::DSL::Union
def switch_on(type)
end

def switch(discriminant, arm)
puts "switch #{discriminant} => #{arm}"
end

def arm(name, type)
puts "arm #{name} => #{type}"
end
end
9 changes: 9 additions & 0 deletions lib/xdr/int.rb
@@ -0,0 +1,9 @@
module XDR::Int
extend XDR::Concerns::ConvertsToXDR

def self.xdr_serializer
XDR::Primitives::INT32
end

extend self
end
14 changes: 14 additions & 0 deletions lib/xdr/opaque.rb
@@ -0,0 +1,14 @@
class XDR::Opaque
def self.[] (length)
new(length)
end

def initialize(length)
@length = length
end

def valid?(input)
input.is_a?(String) && input.length == @length
end

end
5 changes: 5 additions & 0 deletions lib/xdr/option.rb
@@ -0,0 +1,5 @@
class XDR::Option
def self.[] (child_type)

end
end
10 changes: 10 additions & 0 deletions lib/xdr/primitives/base.rb
Expand Up @@ -2,6 +2,16 @@
class XDR::Primitives::Base
include XDR::Concerns::ReadsBytes

def to_xdr(val)
raise NotImplementedError, "implement in subclass"
end

def read(io)
raise NotImplementedError, "implement in subclass"
end

alias from_xdr read

private

def padding_for(length)
Expand Down
4 changes: 4 additions & 0 deletions lib/xdr/primitives/int32.rb
@@ -1,4 +1,8 @@
class XDR::Primitives::Int32 < XDR::Primitives::Base
def to_xdr(val)
# TODO: check bounds
[val].pack("l>")
end

def read(io)
read_bytes(io, 4).unpack("l>").first
Expand Down
5 changes: 5 additions & 0 deletions lib/xdr/primitives/string.rb
@@ -1,4 +1,9 @@
class XDR::Primitives::String < XDR::Primitives::VarOpaque

def to_xdr(val)
super val.encode("ASCII")
end

def read(io)
super(io).tap do |bytes|
bytes.force_encoding("ASCII")
Expand Down
13 changes: 13 additions & 0 deletions lib/xdr/primitives/struct.rb
Expand Up @@ -4,6 +4,19 @@ def initialize(*fields)
@fields = fields
end

def to_xdr(val)
StringIO.new.tap do |out|
# write the length
out.write XDR::Primitives::INT32.to_xdr(val.fields.length)

# write the fields
val.
fields.
map{|n,v| v.to_xdr(val.public_send n)}.
each{|xdr| out.write(xdr)}
end.string
end

def read(io)
@fields.map{|f| f.read(io) }
end
Expand Down
11 changes: 11 additions & 0 deletions lib/xdr/primitives/var_opaque.rb
Expand Up @@ -5,6 +5,17 @@ def initialize(max=DEFAULT_MAX)
@max = max
end

def to_xdr(val)
length = val.bytesize
raise XDR::WriteError, "Value length #{length} exceeds max #{@max}" if length > @max

StringIO.new.tap do |out|
out.write XDR::Primitives::INT32.to_xdr(length)
out.write val
out.write "\x00" * padding_for(length)
end.string
end

def read(io)
length = XDR::Primitives::INT32.read(io)
raise XDR::ReadError if length > @max
Expand Down
15 changes: 15 additions & 0 deletions lib/xdr/string.rb
@@ -0,0 +1,15 @@
class XDR::String
include XDR::Concerns::ConvertsToXDR

def self.[] (max_length)
new(max_length)
end

def initialize(max_length)
@max_length = max_length
end

def xdr_serializer
XDR::Primitives::String.new(@max_length)
end
end
25 changes: 25 additions & 0 deletions lib/xdr/struct.rb
@@ -0,0 +1,25 @@
class XDR::Struct
include ActiveModel::Model
include ActiveModel::AttributeMethods

extend XDR::Concerns::ConvertsToXDR
extend XDR::DSL::Struct

class_attribute :fields
self.fields = ActiveSupport::OrderedHash.new

validates_with XDR::StructValidator

def self.xdr_serializer
# TODO: raise if `struct` aint an `XDR::Struct`
return @xdr_serializer if defined? @xdr_serializer

field_converters = self.fields.values.map(&:xdr_serializer)

@xdr_serializer = XDR::Primitives::Struct.new(*field_converters)
end

def to_xdr
self.class.to_xdr(self)
end
end
6 changes: 6 additions & 0 deletions lib/xdr/struct_validator.rb
@@ -0,0 +1,6 @@
class XDR::StructValidator < ActiveModel::Validator
def validate(struct)
# validate each field
# TODO
end
end
7 changes: 7 additions & 0 deletions lib/xdr/union.rb
@@ -0,0 +1,7 @@
class XDR::Union
include ActiveModel::Model
include ActiveModel::AttributeMethods
extend XDR::DSL::Union

validates_with XDR::UnionValidator
end

0 comments on commit d0e72ff

Please sign in to comment.