github.com/google/martian/v3@v3.3.3/parse/parse.go (about)

     1  // Copyright 2015 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package parse constructs martian modifiers from JSON messages.
    16  package parse
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/google/martian/v3"
    24  )
    25  
    26  // ModifierType is the HTTP message type.
    27  type ModifierType string
    28  
    29  const (
    30  	// Request modifies an HTTP request.
    31  	Request ModifierType = "request"
    32  	// Response modifies an HTTP response.
    33  	Response ModifierType = "response"
    34  )
    35  
    36  // Result holds the parsed modifier and its type.
    37  type Result struct {
    38  	reqmod martian.RequestModifier
    39  	resmod martian.ResponseModifier
    40  }
    41  
    42  // NewResult returns a new parse.Result for a given interface{} that implements a modifier
    43  // and a slice of scopes to generate the result for.
    44  //
    45  // Returns nil, error if a given modifier does not support a given scope
    46  func NewResult(mod interface{}, scope []ModifierType) (*Result, error) {
    47  	reqmod, reqOk := mod.(martian.RequestModifier)
    48  	resmod, resOk := mod.(martian.ResponseModifier)
    49  	result := &Result{}
    50  	if scope == nil {
    51  		result.reqmod = reqmod
    52  		result.resmod = resmod
    53  		return result, nil
    54  	}
    55  
    56  	for _, s := range scope {
    57  		switch s {
    58  		case Request:
    59  			if !reqOk {
    60  				return nil, fmt.Errorf("parse: invalid scope %q for modifier", "request")
    61  			}
    62  
    63  			result.reqmod = reqmod
    64  		case Response:
    65  			if !resOk {
    66  				return nil, fmt.Errorf("parse: invalid scope %q for modifier", "response")
    67  			}
    68  
    69  			result.resmod = resmod
    70  		default:
    71  			return nil, fmt.Errorf("parse: invalid scope: %s not in [%q, %q]", s, "request", "response")
    72  		}
    73  	}
    74  
    75  	return result, nil
    76  }
    77  
    78  // RequestModifier returns the parsed RequestModifier.
    79  //
    80  // Returns nil if the message has no request modifier.
    81  func (r *Result) RequestModifier() martian.RequestModifier {
    82  	return r.reqmod
    83  }
    84  
    85  // ResponseModifier returns the parsed ResponseModifier.
    86  //
    87  // Returns nil if the message has no response modifier.
    88  func (r *Result) ResponseModifier() martian.ResponseModifier {
    89  	return r.resmod
    90  }
    91  
    92  var (
    93  	parseMu    sync.RWMutex
    94  	parseFuncs = make(map[string]func(b []byte) (*Result, error))
    95  )
    96  
    97  // ErrUnknownModifier is the error returned when the message does not
    98  // contain a field representing a known modifier type.
    99  type ErrUnknownModifier struct {
   100  	name string
   101  }
   102  
   103  // Error returns a formatted error message for an ErrUnknownModifier.
   104  func (e ErrUnknownModifier) Error() string {
   105  	return fmt.Sprintf("parse: unknown modifier: %s", e.name)
   106  }
   107  
   108  // Register registers a parsing function for name that will be used to unmarshal
   109  // a JSON message into the appropriate modifier.
   110  func Register(name string, parseFunc func(b []byte) (*Result, error)) {
   111  	parseMu.Lock()
   112  	defer parseMu.Unlock()
   113  
   114  	parseFuncs[name] = parseFunc
   115  }
   116  
   117  // FromJSON parses a Modifier JSON message by looking up the named modifier in parseFuncs
   118  // and passing its modifier to the registered parseFunc. Returns a parse.Result containing
   119  // the top-level parsed modifier. If no parser has been registered with the given name
   120  // it returns an error of type ErrUnknownModifier.
   121  func FromJSON(b []byte) (*Result, error) {
   122  	msg := make(map[string]json.RawMessage)
   123  	if err := json.Unmarshal(b, &msg); err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	if len(msg) != 1 {
   128  		ks := ""
   129  		for k := range msg {
   130  			ks += ", " + k
   131  		}
   132  
   133  		return nil, fmt.Errorf("parse: expected one modifier, received %d: %s", len(msg), ks)
   134  	}
   135  
   136  	parseMu.RLock()
   137  	defer parseMu.RUnlock()
   138  	for k, m := range msg {
   139  		parseFunc, ok := parseFuncs[k]
   140  		if !ok {
   141  			return nil, ErrUnknownModifier{name: k}
   142  		}
   143  		return parseFunc(m)
   144  	}
   145  
   146  	return nil, fmt.Errorf("parse: no modifiers found: %v", msg)
   147  }