@@ -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+
93125func 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
242307func IsNil (x any ) bool {
0 commit comments