Skip to content

Commit 8cdf0d8

Browse files
author
RECTOR
committed
docs: Add multi-language type mapping documentation (#116)
- Update types.md with 5-language type mapping table (Rust, TypeScript, Python, Go, Ruby) - Add Borsh library installation guide for each language - Add language-specific code examples New documentation pages: - python-types.md: Python dataclasses with borsh-construct - go-types.md: Go structs with Borsh serialization - ruby-types.md: Ruby classes with borsh-rb Covers: - Complete type mapping reference - Serialization/deserialization examples - Enum support patterns - Best practices for each language - Solana integration examples
1 parent c27fe7c commit 8cdf0d8

File tree

4 files changed

+1107
-3
lines changed

4 files changed

+1107
-3
lines changed

src/content/docs/api/go-types.md

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
---
2+
title: Go Types
3+
description: Go type mappings and Borsh serialization for LUMOS
4+
---
5+
6+
LUMOS generates Go structs with struct tags for Borsh serialization.
7+
8+
## Type Mapping Reference
9+
10+
| LUMOS Type | Go Type | Notes |
11+
|------------|---------|-------|
12+
| `u8` | `uint8` | |
13+
| `u16` | `uint16` | |
14+
| `u32` | `uint32` | |
15+
| `u64` | `uint64` | |
16+
| `u128` | `[16]byte` | Go lacks native 128-bit integers |
17+
| `i8` | `int8` | |
18+
| `i16` | `int16` | |
19+
| `i32` | `int32` | |
20+
| `i64` | `int64` | |
21+
| `i128` | `[16]byte` | Little-endian byte array |
22+
| `bool` | `bool` | |
23+
| `String` | `string` | |
24+
| `PublicKey` | `[32]byte` | Fixed 32-byte array |
25+
| `Signature` | `[64]byte` | Fixed 64-byte array |
26+
| `Vec<T>` | `[]T` | Slice |
27+
| `Option<T>` | `*T` | Pointer (nil = None) |
28+
| `[T; N]` | `[N]T` | Fixed-size array |
29+
30+
---
31+
32+
## Generated Code Example
33+
34+
### LUMOS Schema
35+
36+
```rust
37+
#[solana]
38+
struct PlayerAccount {
39+
wallet: PublicKey,
40+
level: u16,
41+
experience: u64,
42+
username: String,
43+
guild: Option<PublicKey>,
44+
inventory: [u8],
45+
}
46+
```
47+
48+
### Generated Go
49+
50+
```go
51+
// Code generated by LUMOS - DO NOT EDIT
52+
// Schema: player_account.lumos
53+
package schema
54+
55+
// PlayerAccount represents the player account data
56+
type PlayerAccount struct {
57+
Wallet [32]byte `borsh:"wallet"` // PublicKey
58+
Level uint16 `borsh:"level"` // u16
59+
Experience uint64 `borsh:"experience"` // u64
60+
Username string `borsh:"username"` // String
61+
Guild *[32]byte `borsh:"guild"` // Option<PublicKey>
62+
Inventory []uint8 `borsh:"inventory"` // Vec<u8>
63+
}
64+
```
65+
66+
---
67+
68+
## Borsh Serialization
69+
70+
Go doesn't have a standard Borsh library, but you can use struct tags with custom serialization:
71+
72+
### Basic Serialization
73+
74+
```go
75+
package schema
76+
77+
import (
78+
"encoding/binary"
79+
"bytes"
80+
)
81+
82+
// Serialize serializes PlayerAccount to Borsh format
83+
func (p *PlayerAccount) Serialize() ([]byte, error) {
84+
buf := new(bytes.Buffer)
85+
86+
// Wallet (32 bytes, fixed)
87+
buf.Write(p.Wallet[:])
88+
89+
// Level (u16, little-endian)
90+
binary.Write(buf, binary.LittleEndian, p.Level)
91+
92+
// Experience (u64, little-endian)
93+
binary.Write(buf, binary.LittleEndian, p.Experience)
94+
95+
// Username (length-prefixed string)
96+
binary.Write(buf, binary.LittleEndian, uint32(len(p.Username)))
97+
buf.WriteString(p.Username)
98+
99+
// Guild (Option: 0x00 for None, 0x01 + value for Some)
100+
if p.Guild == nil {
101+
buf.WriteByte(0x00)
102+
} else {
103+
buf.WriteByte(0x01)
104+
buf.Write(p.Guild[:])
105+
}
106+
107+
// Inventory (Vec: length prefix + elements)
108+
binary.Write(buf, binary.LittleEndian, uint32(len(p.Inventory)))
109+
buf.Write(p.Inventory)
110+
111+
return buf.Bytes(), nil
112+
}
113+
```
114+
115+
### Deserialization
116+
117+
```go
118+
// Deserialize deserializes Borsh bytes to PlayerAccount
119+
func DeserializePlayerAccount(data []byte) (*PlayerAccount, error) {
120+
r := bytes.NewReader(data)
121+
p := &PlayerAccount{}
122+
123+
// Wallet
124+
if _, err := r.Read(p.Wallet[:]); err != nil {
125+
return nil, err
126+
}
127+
128+
// Level
129+
if err := binary.Read(r, binary.LittleEndian, &p.Level); err != nil {
130+
return nil, err
131+
}
132+
133+
// Experience
134+
if err := binary.Read(r, binary.LittleEndian, &p.Experience); err != nil {
135+
return nil, err
136+
}
137+
138+
// Username
139+
var strLen uint32
140+
binary.Read(r, binary.LittleEndian, &strLen)
141+
strBytes := make([]byte, strLen)
142+
r.Read(strBytes)
143+
p.Username = string(strBytes)
144+
145+
// Guild (Option)
146+
optByte, _ := r.ReadByte()
147+
if optByte == 0x01 {
148+
guild := [32]byte{}
149+
r.Read(guild[:])
150+
p.Guild = &guild
151+
}
152+
153+
// Inventory (Vec)
154+
var vecLen uint32
155+
binary.Read(r, binary.LittleEndian, &vecLen)
156+
p.Inventory = make([]uint8, vecLen)
157+
r.Read(p.Inventory)
158+
159+
return p, nil
160+
}
161+
```
162+
163+
---
164+
165+
## Enum Support
166+
167+
### Unit Enums
168+
169+
```go
170+
// GameStatus represents the game state
171+
type GameStatus uint8
172+
173+
const (
174+
GameStatusActive GameStatus = 0
175+
GameStatusPaused GameStatus = 1
176+
GameStatusTerminated GameStatus = 2
177+
)
178+
179+
func (s GameStatus) String() string {
180+
switch s {
181+
case GameStatusActive:
182+
return "Active"
183+
case GameStatusPaused:
184+
return "Paused"
185+
case GameStatusTerminated:
186+
return "Terminated"
187+
default:
188+
return "Unknown"
189+
}
190+
}
191+
```
192+
193+
### Complex Enums (Interface Pattern)
194+
195+
```go
196+
// Instruction is the enum interface
197+
type Instruction interface {
198+
isInstruction()
199+
Discriminant() uint8
200+
}
201+
202+
// Transfer variant
203+
type Transfer struct {
204+
Recipient [32]byte
205+
Amount uint64
206+
}
207+
208+
func (Transfer) isInstruction() {}
209+
func (Transfer) Discriminant() uint8 { return 0 }
210+
211+
// Stake variant
212+
type Stake struct {
213+
Validator [32]byte
214+
Amount uint64
215+
}
216+
217+
func (Stake) isInstruction() {}
218+
func (Stake) Discriminant() uint8 { return 1 }
219+
```
220+
221+
---
222+
223+
## 128-bit Integer Handling
224+
225+
Go lacks native 128-bit integers, so LUMOS uses `[16]byte`:
226+
227+
```go
228+
import "math/big"
229+
230+
// U128 represents a 128-bit unsigned integer
231+
type U128 [16]byte
232+
233+
// ToBigInt converts U128 to *big.Int
234+
func (u U128) ToBigInt() *big.Int {
235+
// Reverse for little-endian
236+
reversed := make([]byte, 16)
237+
for i := 0; i < 16; i++ {
238+
reversed[15-i] = u[i]
239+
}
240+
return new(big.Int).SetBytes(reversed)
241+
}
242+
243+
// FromBigInt creates U128 from *big.Int
244+
func FromBigInt(n *big.Int) U128 {
245+
var u U128
246+
bytes := n.Bytes()
247+
// Copy in little-endian order
248+
for i := 0; i < len(bytes) && i < 16; i++ {
249+
u[i] = bytes[len(bytes)-1-i]
250+
}
251+
return u
252+
}
253+
```
254+
255+
---
256+
257+
## Integration with Solana
258+
259+
### Using solana-go
260+
261+
```go
262+
import (
263+
"github.com/gagliardetto/solana-go"
264+
"github.com/gagliardetto/solana-go/rpc"
265+
)
266+
267+
func GetAccountData(client *rpc.Client, pubkey solana.PublicKey) (*PlayerAccount, error) {
268+
resp, err := client.GetAccountInfo(context.Background(), pubkey)
269+
if err != nil {
270+
return nil, err
271+
}
272+
273+
data := resp.Value.Data.GetBinary()
274+
// Skip 8-byte Anchor discriminator
275+
return DeserializePlayerAccount(data[8:])
276+
}
277+
```
278+
279+
---
280+
281+
## Best Practices
282+
283+
### 1. PublicKey Helper Functions
284+
285+
```go
286+
import "github.com/mr-tron/base58"
287+
288+
func PubkeyToBase58(pk [32]byte) string {
289+
return base58.Encode(pk[:])
290+
}
291+
292+
func Base58ToPubkey(s string) ([32]byte, error) {
293+
var pk [32]byte
294+
decoded, err := base58.Decode(s)
295+
if err != nil {
296+
return pk, err
297+
}
298+
copy(pk[:], decoded)
299+
return pk, nil
300+
}
301+
```
302+
303+
### 2. Option Handling
304+
305+
```go
306+
// Safe option access
307+
func (p *PlayerAccount) GetGuild() (string, bool) {
308+
if p.Guild == nil {
309+
return "", false
310+
}
311+
return PubkeyToBase58(*p.Guild), true
312+
}
313+
```
314+
315+
### 3. Error Handling
316+
317+
```go
318+
// Always handle serialization errors
319+
data, err := account.Serialize()
320+
if err != nil {
321+
return fmt.Errorf("failed to serialize: %w", err)
322+
}
323+
```
324+
325+
---
326+
327+
## See Also
328+
329+
- [Type System](/api/types) - Full type reference
330+
- [Python Types](/api/python-types) - Python type mappings
331+
- [Ruby Types](/api/ruby-types) - Ruby type mappings

0 commit comments

Comments
 (0)