github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/market/contact.go (about)

     1  /*
     2   * Copyright (C) 2017 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package market
    19  
    20  import "encoding/json"
    21  
    22  // ContactList is list of Contact structures, to have custom JSON marshaling
    23  type ContactList []Contact
    24  
    25  // MarshalJSON encodes in such manner that `null` situation avoided in JSON
    26  func (list ContactList) MarshalJSON() ([]byte, error) {
    27  	if list == nil {
    28  		return json.Marshal([]Contact{})
    29  	}
    30  	return json.Marshal([]Contact(list))
    31  }
    32  
    33  // Contact represents contact object with type and concrete definition according to type
    34  type Contact struct {
    35  	Type       string            `json:"type"`
    36  	Definition ContactDefinition `json:"definition"`
    37  }
    38  
    39  // ContactDefinition is interface for contacts of all types
    40  type ContactDefinition interface {
    41  }
    42  
    43  // UnsupportedContactType is a contact which is returned by unserializer when encountering unregistered types of contact
    44  type UnsupportedContactType struct {
    45  }
    46  
    47  var _ ContactDefinition = UnsupportedContactType{}
    48  
    49  // ContactDefinitionUnserializer represents function which called for concrete contact type to unserialize
    50  type ContactDefinitionUnserializer func(*json.RawMessage) (ContactDefinition, error)
    51  
    52  // contact unserializer registry
    53  // TODO avoid global map variables and wrap this functionality into some kind of component?
    54  var contactDefinitionMap = make(map[string]ContactDefinitionUnserializer)
    55  
    56  // RegisterContactUnserializer registers unserializer for specified contact type.
    57  func RegisterContactUnserializer(contactType string, unserializer func(*json.RawMessage) (ContactDefinition, error)) {
    58  	contactDefinitionMap[contactType] = unserializer
    59  }
    60  
    61  func unserializeContacts(message *json.RawMessage) (contactList ContactList) {
    62  	contactList = ContactList{}
    63  	if message == nil {
    64  		return
    65  	}
    66  
    67  	// get an array of raw definitions
    68  	var contacts []struct {
    69  		Type       string           `json:"type"`
    70  		Definition *json.RawMessage `json:"definition"`
    71  	}
    72  	if err := json.Unmarshal([]byte(*message), &contacts); err != nil {
    73  		return
    74  	}
    75  
    76  	for _, contactItem := range contacts {
    77  		contactList = append(contactList, Contact{
    78  			Type:       contactItem.Type,
    79  			Definition: unserializeContact(contactItem.Type, contactItem.Definition),
    80  		})
    81  	}
    82  
    83  	return
    84  }
    85  
    86  func unserializeContact(contactType string, rawMessage *json.RawMessage) ContactDefinition {
    87  	fn, ok := contactDefinitionMap[contactType]
    88  	if !ok {
    89  		return UnsupportedContactType{}
    90  	}
    91  	definition, err := fn(rawMessage)
    92  	if err != nil {
    93  		return UnsupportedContactType{}
    94  	}
    95  
    96  	return definition
    97  }