Skip to content

Commit e883f44

Browse files
committed
Add func DecodeTLBStructKeys to return orderd set of TL-B struct keys
1 parent 0771b49 commit e883f44

File tree

1 file changed

+86
-21
lines changed

1 file changed

+86
-21
lines changed

pkg/ton/debug/lib/lib.go

Lines changed: 86 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,38 @@ func NewMessageInfoFromCell(t string, msg *cell.Cell, tlbs map[uint64]interface{
9090
return NewMessageInfo(name, norm)
9191
}
9292

93+
func DecodeTLBCellToAny(c *cell.Cell, tlbs map[uint64]interface{}) (any, error) {
94+
if c == nil {
95+
return nil, errors.New("can't decode nil as cell")
96+
}
97+
98+
// Try to decode *cell.Cell as one of the TLBs type by reading the opcode
99+
r := c.BeginParse()
100+
if r.BitsLeft() == 0 {
101+
return nil, &UnknownMessageError{}
102+
}
103+
opCode, err := r.PreloadUInt(32)
104+
if err != nil {
105+
return nil, fmt.Errorf("failed to preload opcode: %w", err)
106+
}
107+
108+
i, ok := tlbs[opCode]
109+
if !ok {
110+
return nil, &UnknownMessageError{}
111+
}
112+
113+
// Create new instance of the candidate type
114+
rt := reflect.TypeOf(i)
115+
inst := reflect.New(rt).Interface() // pointer to zero value
116+
117+
// Attempt decode - replace tlb.FromCell with the actual decode API you have
118+
if err = tlb.LoadFromCell(inst, r); err != nil {
119+
return nil, fmt.Errorf("failed to decode message for opcode 0x%X: %w", opCode, err)
120+
}
121+
122+
return inst, nil
123+
}
124+
93125
func DecodeTLBStructToJSON(v interface{}, tlbs map[uint64]interface{}) (string, map[string]interface{}, error) {
94126
// Checks if a value is nil or if it's a reference type with a nil underlying value.
95127
if IsNil(v) {
@@ -98,28 +130,9 @@ func DecodeTLBStructToJSON(v interface{}, tlbs map[uint64]interface{}) (string,
98130

99131
switch t := v.(type) {
100132
case *cell.Cell:
101-
// Try to decode *cell.Cell as one of the TLBs type by reading the opcode
102-
r := t.BeginParse()
103-
if r.BitsLeft() == 0 {
104-
return "", nil, &UnknownMessageError{}
105-
}
106-
opCode, err := r.PreloadUInt(32)
133+
inst, err := DecodeTLBCellToAny(t, tlbs)
107134
if err != nil {
108-
return "", nil, fmt.Errorf("failed to preload opcode: %w", err)
109-
}
110-
111-
i, ok := tlbs[opCode]
112-
if !ok {
113-
return "", nil, &UnknownMessageError{}
114-
}
115-
116-
// Create new instance of the candidate type
117-
rt := reflect.TypeOf(i)
118-
inst := reflect.New(rt).Interface() // pointer to zero value
119-
120-
// Attempt decode - replace tlb.FromCell with the actual decode API you have
121-
if err = tlb.LoadFromCell(inst, r); err != nil {
122-
return "", nil, fmt.Errorf("failed to decode message for opcode 0x%X: %w", opCode, err)
135+
return "", nil, fmt.Errorf("failed to decode cell to struct type (any): %w", err)
123136
}
124137

125138
// Now decode loaded struct (internal *cell.Cell) fields recursively
@@ -237,6 +250,58 @@ func DecodeTLBValToJSON(v interface{}, tlbs map[uint64]interface{}) (string, int
237250
}
238251
}
239252

253+
// Returns ordered keys based TL-B annotated struct type
254+
func DecodeTLBStructKeys(v interface{}, tlbs map[uint64]interface{}) ([]string, error) {
255+
// Checks if a value is nil or if it's a reference type with a nil underlying value.
256+
if IsNil(v) {
257+
return nil, errors.New("can't decode nil as struct")
258+
}
259+
260+
switch t := v.(type) {
261+
case *cell.Cell:
262+
inst, err := DecodeTLBCellToAny(t, tlbs)
263+
if err != nil {
264+
return nil, fmt.Errorf("failed to decode cell to struct type (any): %w", err)
265+
}
266+
267+
// Now decode loaded struct (internal *cell.Cell) fields recursively
268+
return DecodeTLBStructKeys(inst, tlbs)
269+
default:
270+
// Iterate over the fields of the struct (reflect)
271+
rv := reflect.ValueOf(v)
272+
if rv.Kind() == reflect.Ptr {
273+
rv = rv.Elem()
274+
}
275+
if !rv.IsValid() {
276+
return nil, fmt.Errorf("failed to decode TLB struct - not valid value: type=%T; val=%v", t, rv)
277+
}
278+
279+
if rv.Kind() != reflect.Struct {
280+
return nil, fmt.Errorf("unable to decode as JSON map - not a structure: type=%T; val=%v", t, rv)
281+
}
282+
283+
out := make([]string, rv.NumField())
284+
rt := rv.Type()
285+
for i := 0; i < rv.NumField(); i++ {
286+
sf := rt.Field(i)
287+
// skip unexported fields (e.g. the magic field)
288+
if sf.PkgPath != "" {
289+
continue
290+
}
291+
292+
// check the json tag to determine the expected key
293+
k := sf.Name
294+
jsonTag := sf.Tag.Get("json")
295+
if jsonTag != "" {
296+
k = strings.Split(jsonTag, ",")[0] // parse json tag options (key)
297+
}
298+
299+
out = append(out, k)
300+
}
301+
return out, nil
302+
}
303+
}
304+
240305
// IsNil checks if a value is nil or if it's a reference type with a nil underlying value.
241306
// Notice: vendoring github:samber/lo
242307
func IsNil(x any) bool {

0 commit comments

Comments
 (0)