Skip to content

Commit

Permalink
Create sample
Browse files Browse the repository at this point in the history
  • Loading branch information
Gekctek committed Sep 9, 2022
1 parent b10b062 commit dc04ebd
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 25 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Usage

Example of `call_raw` usage:

```
func call_raw(p : Principal, m : Text, a : Blob) : async Blob {
// Parse parameters
let args: [Arg.Arg] = switch(Decoder.decode(a)) {
case (null) Debug.trap("Invalid candid");
case (?c) c;
};
// Validate request...
// Process request...
// Return result
let returnArgs: [Arg.Arg] = [
{
_type=#bool;
value=#bool(true)
}
];
Encoder.encode(returnArgs);
};
```

# Library Devlopment:

## First time setup
Expand All @@ -22,6 +47,7 @@ Currently there are no testing frameworks and testing will stop at the first bro

## TODO

- How to properly escape special words like 'func'. Currently doing '\_func'
- Opaque reference byte encoding/decoding
- Error messaging vs null return type for decoding
- Better/Documented error messages
Expand Down
22 changes: 22 additions & 0 deletions sample/main.mo
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import Encoder "../src/Encoder";
import Decoder "../src/Decoder";
import Arg "../src/Arg";
import Debug "mo:base/Debug";

actor Sample {
func call_raw(p : Principal, m : Text, a : Blob) : async Blob {

// Parse parameters
let args: [Arg.Arg] = switch(Decoder.decode(a)) {
case (null) Debug.trap("Invalid candid");
case (?c) c;
};

// Validate request...

// Process request...

// Return result
let returnArgs: [Arg.Arg] = [
{
_type=#bool;
value=#bool(true)
}
];
Encoder.encode(returnArgs);
};
};
10 changes: 10 additions & 0 deletions src/Arg.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Value "./Value";
import Type "./Type";


module {
public type Arg = {
value: Value.Value;
_type: Type.Type;
};
}
25 changes: 12 additions & 13 deletions src/Decoder.mo
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,16 @@ import Tag "./Tag";
import InternalTypes "./InternalTypes";
import TransparencyState "./TransparencyState";
import FuncMode "./FuncMode";
import Arg "./Arg";

module {

type Value = Value.Value;
type ShallowCompoundType<T> = InternalTypes.ShallowCompoundType<T>;
type Tag = Tag.Tag;
type ReferenceType = InternalTypes.ReferenceType;


// TODO change ? to be result with specific error messages
public func decode(candidBytes: Blob) : ?[(Value, Type.Type)] {
public func decode(candidBytes: Blob) : ?[Arg.Arg] {
do ? {
let bytes : Iter.Iter<Nat8> = Iter.fromArray(Blob.toArray(candidBytes));
let prefix1: Nat8 = bytes.next()!;
Expand All @@ -45,21 +44,21 @@ module {
};
let (compoundTypes: [ShallowCompoundType<ReferenceType>], argTypes: [Int]) = decodeTypes(bytes)!;
let types : [Type.Type] = buildTypes(compoundTypes, argTypes)!;
let values: [Value] = decodeValues(bytes, types)!;
let values: [Value.Value] = decodeValues(bytes, types)!;
var i = 0;
let valueTypes = Buffer.Buffer<(Value, Type.Type)>(types.size());
let valueTypes = Buffer.Buffer<Arg.Arg>(types.size());
for (t in Iter.fromArray(types)) {
let v = values[i];
valueTypes.add((v, t));
valueTypes.add({value=v; _type=t});
i += 1;
};
valueTypes.toArray();
};
};

private func decodeValues(bytes: Iter.Iter<Nat8>, types: [Type.Type]) : ?[Value] {
private func decodeValues(bytes: Iter.Iter<Nat8>, types: [Type.Type]) : ?[Value.Value] {
do ? {
let valueBuffer = Buffer.Buffer<Value>(types.size());
let valueBuffer = Buffer.Buffer<Value.Value>(types.size());
let referencedTypes = TrieMap.TrieMap<Text, Type.Type>(Text.equal, Text.hash);
for (t in Iter.fromArray(types)) {
addReferenceTypes(t, referencedTypes);
Expand Down Expand Up @@ -95,7 +94,7 @@ module {
}
};

private func decodeValue(bytes: Iter.Iter<Nat8>, t: Type.Type, referencedTypes: TrieMap.TrieMap<Text, Type.Type>) : ?Value {
private func decodeValue(bytes: Iter.Iter<Nat8>, t: Type.Type, referencedTypes: TrieMap.TrieMap<Text, Type.Type>) : ?Value.Value {
do ? {
switch (t) {
case (#int) #int(IntX.decodeInt(bytes, #signedLEB128)!);
Expand Down Expand Up @@ -146,13 +145,13 @@ module {
};
case (#vector(v)) {
let length : Nat = NatX.decodeNat(bytes, #unsignedLEB128)!;
let buffer = Buffer.Buffer<Value>(length);
let buffer = Buffer.Buffer<Value.Value>(length);
let innerType: Type.Type = switch (t) {
case (#vector(vv)) vv;
case (_) return null; // type definition doesnt match
};
for (i in Iter.range(0, length - 1)) {
let innerValue: Value = decodeValue(bytes, innerType, referencedTypes)!;
let innerValue: Value.Value = decodeValue(bytes, innerType, referencedTypes)!;
buffer.add(innerValue);
};
#vector(buffer.toArray());
Expand All @@ -164,7 +163,7 @@ module {
};
let buffer = Buffer.Buffer<Value.RecordFieldValue>(innerTypes.size());
for (innerType in Iter.fromArray(innerTypes)) {
let innerValue: Value = decodeValue(bytes, innerType._type, referencedTypes)!;
let innerValue: Value.Value = decodeValue(bytes, innerType._type, referencedTypes)!;
buffer.add({tag=innerType.tag; value=innerValue});
};
#record(buffer.toArray());
Expand All @@ -184,7 +183,7 @@ module {
};
let optionIndex = NatX.decodeNat(bytes, #unsignedLEB128)!; // Get index of option chosen
let innerType: Type.VariantOptionType = innerTypes[optionIndex];
let innerValue: Value = decodeValue(bytes, innerType._type, referencedTypes)!; // Get value of option chosen
let innerValue: Value.Value = decodeValue(bytes, innerType._type, referencedTypes)!; // Get value of option chosen
#variant({tag=innerType.tag; value=innerValue});
};
case (#recursiveType(rT)) {
Expand Down
18 changes: 13 additions & 5 deletions src/Encoder.mo
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import InternalTypes "./InternalTypes";
import TransparencyState "./TransparencyState";
import FuncMode "./FuncMode";
import TypeCode "./TypeCode";
import Arg "./Arg";

module {

Expand All @@ -36,22 +37,29 @@ module {
type VariantOptionReferenceType<T> = InternalTypes.VariantOptionReferenceType<T>;


public func encode(argTypes: [Type.Type], args : [Value.Value]) : Blob {
public func encode(args: [Arg.Arg]) : Blob {
let buffer = Buffer.Buffer<Nat8>(10);
encodeToBuffer(buffer, argTypes, args);
encodeToBuffer(buffer, args);
Blob.fromArray(buffer.toArray());
};

public func encodeToBuffer(buffer : Buffer.Buffer<Nat8>, argTypes: [Type.Type], args : [Value.Value]) {
public func encodeToBuffer(buffer : Buffer.Buffer<Nat8>, args : [Arg.Arg]) {
// "DIDL" prefix
buffer.add(0x44);
buffer.add(0x49);
buffer.add(0x44);
buffer.add(0x4c);

let table : CompoundTypeTable = getTypeInfo(argTypes);
let argTypes = Buffer.Buffer<Type.Type>(args.size());
let argValues = Buffer.Buffer<Value.Value>(args.size());
for (arg in Iter.fromArray(args)) {
argTypes.add(arg._type);
argValues.add(arg.value);
};

let table : CompoundTypeTable = getTypeInfo(argTypes.toArray());
encodeTypes(buffer, table); // Encode compound type table + primitive types
encodeValues(buffer, table, args); // Encode all the values for the types
encodeValues(buffer, table, argValues.toArray()); // Encode all the values for the types
};

type CompoundTypeTable = {
Expand Down
15 changes: 8 additions & 7 deletions test/EncoderTests.mo
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Nat8 "mo:base/Nat8";
import Principal "mo:base/Principal";
import Type "../src/Type";
import Value "../src/Value";
import Arg "../src/Arg";

module {
public func run() {
Expand Down Expand Up @@ -276,11 +277,11 @@ module {

private func test(bytes: [Nat8], t : Type.Type, arg: Value.Value) {
Debug.print("Testing...\nType: " # debug_show(t) # "\nValue: " # debug_show(arg) # "\nExpected Bytes: " # toHexString(bytes));
let actualBytes: [Nat8] = Blob.toArray(Encoder.encode([t], [arg]));
let actualBytes: [Nat8] = Blob.toArray(Encoder.encode([{value=arg; _type=t}]));
if (not areEqual(bytes, actualBytes)) {
Debug.trap("Failed Byte Check.\nExpected Bytes: " # toHexString(bytes) # "\nActual Bytes: " # toHexString(actualBytes) # "\nValue: " # debug_show(arg));
};
let args : ?[(Value.Value, Type.Type)] = Decoder.decode(Blob.fromArray(bytes));
let args : ?[Arg.Arg] = Decoder.decode(Blob.fromArray(bytes));
switch(args){
case (null) {
Debug.trap("Failed decoding.\nExpected Type: " # debug_show(t) # "\nExpected Value: " # debug_show(arg) # "\nBytes: " # toHexString(bytes))
Expand All @@ -289,12 +290,12 @@ module {
if (args.size() != 1) {
Debug.trap("Too many args: " # Nat.toText(args.size()));
};
let (actualValue: Value.Value, actualType: Type.Type) = args[0];
if (not Type.equal(t, actualType)) {
Debug.trap("Failed Type Check.\nExpected Type: " # debug_show(t) # "\nActual Type: " # debug_show(actualType));
let actualArg: Arg.Arg = args[0];
if (not Type.equal(t, actualArg._type)) {
Debug.trap("Failed Type Check.\nExpected Type: " # debug_show(t) # "\nActual Type: " # debug_show(actualArg._type));
};
if (not Value.equal(arg, actualValue)) {
Debug.trap("Failed Value Check.\nExpected Value: " # debug_show(arg) # "\nActual Value: " # debug_show(actualValue));
if (not Value.equal(arg, actualArg.value)) {
Debug.trap("Failed Value Check.\nExpected Value: " # debug_show(arg) # "\nActual Value: " # debug_show(actualArg.value));
};
Debug.print("Passed\n");
}
Expand Down

0 comments on commit dc04ebd

Please sign in to comment.