File: parse.go

package info (click to toggle)
golang-github-google-martian 3.3.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,324 kB
  • sloc: makefile: 5
file content (147 lines) | stat: -rw-r--r-- 4,109 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package parse constructs martian modifiers from JSON messages.
package parse

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/google/martian/v3"
)

// ModifierType is the HTTP message type.
type ModifierType string

const (
	// Request modifies an HTTP request.
	Request ModifierType = "request"
	// Response modifies an HTTP response.
	Response ModifierType = "response"
)

// Result holds the parsed modifier and its type.
type Result struct {
	reqmod martian.RequestModifier
	resmod martian.ResponseModifier
}

// NewResult returns a new parse.Result for a given interface{} that implements a modifier
// and a slice of scopes to generate the result for.
//
// Returns nil, error if a given modifier does not support a given scope
func NewResult(mod interface{}, scope []ModifierType) (*Result, error) {
	reqmod, reqOk := mod.(martian.RequestModifier)
	resmod, resOk := mod.(martian.ResponseModifier)
	result := &Result{}
	if scope == nil {
		result.reqmod = reqmod
		result.resmod = resmod
		return result, nil
	}

	for _, s := range scope {
		switch s {
		case Request:
			if !reqOk {
				return nil, fmt.Errorf("parse: invalid scope %q for modifier", "request")
			}

			result.reqmod = reqmod
		case Response:
			if !resOk {
				return nil, fmt.Errorf("parse: invalid scope %q for modifier", "response")
			}

			result.resmod = resmod
		default:
			return nil, fmt.Errorf("parse: invalid scope: %s not in [%q, %q]", s, "request", "response")
		}
	}

	return result, nil
}

// RequestModifier returns the parsed RequestModifier.
//
// Returns nil if the message has no request modifier.
func (r *Result) RequestModifier() martian.RequestModifier {
	return r.reqmod
}

// ResponseModifier returns the parsed ResponseModifier.
//
// Returns nil if the message has no response modifier.
func (r *Result) ResponseModifier() martian.ResponseModifier {
	return r.resmod
}

var (
	parseMu    sync.RWMutex
	parseFuncs = make(map[string]func(b []byte) (*Result, error))
)

// ErrUnknownModifier is the error returned when the message does not
// contain a field representing a known modifier type.
type ErrUnknownModifier struct {
	name string
}

// Error returns a formatted error message for an ErrUnknownModifier.
func (e ErrUnknownModifier) Error() string {
	return fmt.Sprintf("parse: unknown modifier: %s", e.name)
}

// Register registers a parsing function for name that will be used to unmarshal
// a JSON message into the appropriate modifier.
func Register(name string, parseFunc func(b []byte) (*Result, error)) {
	parseMu.Lock()
	defer parseMu.Unlock()

	parseFuncs[name] = parseFunc
}

// FromJSON parses a Modifier JSON message by looking up the named modifier in parseFuncs
// and passing its modifier to the registered parseFunc. Returns a parse.Result containing
// the top-level parsed modifier. If no parser has been registered with the given name
// it returns an error of type ErrUnknownModifier.
func FromJSON(b []byte) (*Result, error) {
	msg := make(map[string]json.RawMessage)
	if err := json.Unmarshal(b, &msg); err != nil {
		return nil, err
	}

	if len(msg) != 1 {
		ks := ""
		for k := range msg {
			ks += ", " + k
		}

		return nil, fmt.Errorf("parse: expected one modifier, received %d: %s", len(msg), ks)
	}

	parseMu.RLock()
	defer parseMu.RUnlock()
	for k, m := range msg {
		parseFunc, ok := parseFuncs[k]
		if !ok {
			return nil, ErrUnknownModifier{name: k}
		}
		return parseFunc(m)
	}

	return nil, fmt.Errorf("parse: no modifiers found: %v", msg)
}