p1: /v2/scrape, /v1/fetch, auth, CLI, contract tests
Single Fetcher.Fetch on the Guard that holds DenyPrefixes. skipTlsVerification is ignored. No 502. keys add writes the secret only to --out. Release binary has no -test-allow-prefix.
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
||||
package gojq
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Marshal returns the jq-flavored JSON encoding of v.
|
||||
//
|
||||
// This method accepts only limited types (nil, bool, int, float64, *big.Int,
|
||||
// json.Number, string, []any, and map[string]any) because these are the
|
||||
// possible types a gojq iterator can emit. This method marshals NaN to null,
|
||||
// truncates infinities to (+|-) math.MaxFloat64, uses \b and \f in strings,
|
||||
// and does not escape '<', '>', '&', '\u2028', and '\u2029'. These behaviors
|
||||
// are based on the marshaler of jq command, and different from json.Marshal in
|
||||
// the Go standard library. Note that the result is not safe to embed in HTML.
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
(&encoder{w: &b}).encode(v)
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func jsonMarshal(v any) string {
|
||||
var sb strings.Builder
|
||||
(&encoder{w: &sb}).encode(v)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func jsonEncodeString(sb *strings.Builder, v string) {
|
||||
(&encoder{w: sb}).encodeString(v)
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
w interface {
|
||||
io.Writer
|
||||
io.ByteWriter
|
||||
io.StringWriter
|
||||
}
|
||||
buf [64]byte
|
||||
}
|
||||
|
||||
func (e *encoder) encode(v any) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
e.w.WriteString("null")
|
||||
case bool:
|
||||
if v {
|
||||
e.w.WriteString("true")
|
||||
} else {
|
||||
e.w.WriteString("false")
|
||||
}
|
||||
case int:
|
||||
e.w.Write(strconv.AppendInt(e.buf[:0], int64(v), 10))
|
||||
case float64:
|
||||
e.encodeFloat64(v)
|
||||
case *big.Int:
|
||||
e.w.Write(v.Append(e.buf[:0], 10))
|
||||
case json.Number:
|
||||
e.w.WriteString(v.String())
|
||||
case string:
|
||||
e.encodeString(v)
|
||||
case []any:
|
||||
e.encodeArray(v)
|
||||
case map[string]any:
|
||||
e.encodeObject(v)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v))
|
||||
}
|
||||
}
|
||||
|
||||
// ref: floatEncoder in encoding/json
|
||||
func (e *encoder) encodeFloat64(f float64) {
|
||||
if math.IsNaN(f) {
|
||||
e.w.WriteString("null")
|
||||
return
|
||||
}
|
||||
f = min(max(f, -math.MaxFloat64), math.MaxFloat64)
|
||||
format := byte('f')
|
||||
if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 {
|
||||
format = 'e'
|
||||
}
|
||||
buf := strconv.AppendFloat(e.buf[:0], f, format, -1, 64)
|
||||
if format == 'e' {
|
||||
// clean up e-09 to e-9
|
||||
if n := len(buf); n >= 4 && buf[n-4] == 'e' && buf[n-3] == '-' && buf[n-2] == '0' {
|
||||
buf[n-2] = buf[n-1]
|
||||
buf = buf[:n-1]
|
||||
}
|
||||
}
|
||||
e.w.Write(buf)
|
||||
}
|
||||
|
||||
// ref: encodeState#string in encoding/json
|
||||
func (e *encoder) encodeString(s string) {
|
||||
e.w.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if ' ' <= b && b <= '~' && b != '"' && b != '\\' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
e.w.WriteString(s[start:i])
|
||||
}
|
||||
switch b {
|
||||
case '"':
|
||||
e.w.WriteString(`\"`)
|
||||
case '\\':
|
||||
e.w.WriteString(`\\`)
|
||||
case '\b':
|
||||
e.w.WriteString(`\b`)
|
||||
case '\f':
|
||||
e.w.WriteString(`\f`)
|
||||
case '\n':
|
||||
e.w.WriteString(`\n`)
|
||||
case '\r':
|
||||
e.w.WriteString(`\r`)
|
||||
case '\t':
|
||||
e.w.WriteString(`\t`)
|
||||
default:
|
||||
const hex = "0123456789abcdef"
|
||||
e.w.WriteString(`\u00`)
|
||||
e.w.WriteByte(hex[b>>4])
|
||||
e.w.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRuneInString(s[i:])
|
||||
if c == utf8.RuneError && size == 1 {
|
||||
if start < i {
|
||||
e.w.WriteString(s[start:i])
|
||||
}
|
||||
e.w.WriteString(`\ufffd`)
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
e.w.WriteString(s[start:])
|
||||
}
|
||||
e.w.WriteByte('"')
|
||||
}
|
||||
|
||||
func (e *encoder) encodeArray(vs []any) {
|
||||
e.w.WriteByte('[')
|
||||
for i, v := range vs {
|
||||
if i > 0 {
|
||||
e.w.WriteByte(',')
|
||||
}
|
||||
e.encode(v)
|
||||
}
|
||||
e.w.WriteByte(']')
|
||||
}
|
||||
|
||||
func (e *encoder) encodeObject(vs map[string]any) {
|
||||
e.w.WriteByte('{')
|
||||
type keyVal struct {
|
||||
key string
|
||||
val any
|
||||
}
|
||||
kvs := make([]keyVal, len(vs))
|
||||
var i int
|
||||
for k, v := range vs {
|
||||
kvs[i] = keyVal{k, v}
|
||||
i++
|
||||
}
|
||||
sort.Slice(kvs, func(i, j int) bool {
|
||||
return kvs[i].key < kvs[j].key
|
||||
})
|
||||
for i, kv := range kvs {
|
||||
if i > 0 {
|
||||
e.w.WriteByte(',')
|
||||
}
|
||||
e.encodeString(kv.key)
|
||||
e.w.WriteByte(':')
|
||||
e.encode(kv.val)
|
||||
}
|
||||
e.w.WriteByte('}')
|
||||
}
|
||||
Reference in New Issue
Block a user