Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
// Copyright 2025 The JSON Schema Go Project Authors. All rights reserved.
|
|
// Use of this source code is governed by an MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// This file implements JSON Pointers.
|
|
// A JSON Pointer is a path that refers to one JSON value within another.
|
|
// If the path is empty, it refers to the root value.
|
|
// Otherwise, it is a sequence of slash-prefixed strings, like "/points/1/x",
|
|
// selecting successive properties (for JSON objects) or items (for JSON arrays).
|
|
// For example, when applied to this JSON value:
|
|
// {
|
|
// "points": [
|
|
// {"x": 1, "y": 2},
|
|
// {"x": 3, "y": 4}
|
|
// ]
|
|
// }
|
|
//
|
|
// the JSON Pointer "/points/1/x" refers to the number 3.
|
|
// See the spec at https://datatracker.ietf.org/doc/html/rfc6901.
|
|
|
|
package jsonschema
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
jsonPointerEscaper = strings.NewReplacer("~", "~0", "/", "~1")
|
|
jsonPointerUnescaper = strings.NewReplacer("~0", "~", "~1", "/")
|
|
)
|
|
|
|
func escapeJSONPointerSegment(s string) string {
|
|
return jsonPointerEscaper.Replace(s)
|
|
}
|
|
|
|
func unescapeJSONPointerSegment(s string) string {
|
|
return jsonPointerUnescaper.Replace(s)
|
|
}
|
|
|
|
// parseJSONPointer splits a JSON Pointer into a sequence of segments. It doesn't
|
|
// convert strings to numbers, because that depends on the traversal: a segment
|
|
// is treated as a number when applied to an array, but a string when applied to
|
|
// an object. See section 4 of the spec.
|
|
func parseJSONPointer(ptr string) (segments []string, err error) {
|
|
if ptr == "" {
|
|
return nil, nil
|
|
}
|
|
if ptr[0] != '/' {
|
|
return nil, fmt.Errorf("JSON Pointer %q does not begin with '/'", ptr)
|
|
}
|
|
// Unlike file paths, consecutive slashes are not coalesced.
|
|
// Split is nicer than Cut here, because it gets a final "/" right.
|
|
segments = strings.Split(ptr[1:], "/")
|
|
if strings.Contains(ptr, "~") {
|
|
// Undo the simple escaping rules that allow one to include a slash in a segment.
|
|
for i := range segments {
|
|
segments[i] = unescapeJSONPointerSegment(segments[i])
|
|
}
|
|
}
|
|
return segments, nil
|
|
}
|
|
|
|
// dereferenceJSONPointer returns the Schema that sptr points to within s,
|
|
// or an error if none.
|
|
// This implementation suffices for JSON Schema: pointers are applied only to Schemas,
|
|
// and refer only to Schemas.
|
|
func dereferenceJSONPointer(s *Schema, sptr string) (_ *Schema, err error) {
|
|
defer wrapf(&err, "JSON Pointer %q", sptr)
|
|
|
|
segments, err := parseJSONPointer(sptr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v := reflect.ValueOf(s)
|
|
for _, seg := range segments {
|
|
switch v.Kind() {
|
|
case reflect.Pointer:
|
|
v = v.Elem()
|
|
if !v.IsValid() {
|
|
return nil, errors.New("navigated to nil reference")
|
|
}
|
|
fallthrough // if valid, can only be a pointer to a Schema
|
|
|
|
case reflect.Struct:
|
|
// The segment must refer to a field in a Schema.
|
|
if v.Type() != reflect.TypeFor[Schema]() {
|
|
return nil, fmt.Errorf("navigated to non-Schema %s", v.Type())
|
|
}
|
|
v = lookupSchemaField(v, seg)
|
|
if !v.IsValid() {
|
|
return nil, fmt.Errorf("no schema field %q", seg)
|
|
}
|
|
case reflect.Slice, reflect.Array:
|
|
// The segment must be an integer without leading zeroes that refers to an item in the
|
|
// slice or array.
|
|
if seg == "-" {
|
|
return nil, errors.New("the JSON Pointer array segment '-' is not supported")
|
|
}
|
|
if len(seg) > 1 && seg[0] == '0' {
|
|
return nil, fmt.Errorf("segment %q has leading zeroes", seg)
|
|
}
|
|
n, err := strconv.Atoi(seg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid int: %q", seg)
|
|
}
|
|
if n < 0 || n >= v.Len() {
|
|
return nil, fmt.Errorf("index %d is out of bounds for array of length %d", n, v.Len())
|
|
}
|
|
v = v.Index(n)
|
|
// Cannot be invalid.
|
|
case reflect.Map:
|
|
// The segment must be a key in the map.
|
|
v = v.MapIndex(reflect.ValueOf(seg))
|
|
if !v.IsValid() {
|
|
return nil, fmt.Errorf("no key %q in map", seg)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("value %s (%s) is not a schema, slice or map", v, v.Type())
|
|
}
|
|
}
|
|
if s, ok := v.Interface().(*Schema); ok {
|
|
return s, nil
|
|
}
|
|
return nil, fmt.Errorf("does not refer to a schema, but to a %s", v.Type())
|
|
}
|
|
|
|
// lookupSchemaField returns the value of the field with the given name in v,
|
|
// or the zero value if there is no such field or it is not of type Schema or *Schema.
|
|
func lookupSchemaField(v reflect.Value, name string) reflect.Value {
|
|
if name == "type" {
|
|
// The "type" keyword may refer to Type or Types.
|
|
// At most one will be non-zero.
|
|
if t := v.FieldByName("Type"); !t.IsZero() {
|
|
return t
|
|
}
|
|
return v.FieldByName("Types")
|
|
}
|
|
if sf, ok := schemaFieldMap[name]; ok {
|
|
return v.FieldByIndex(sf.Index)
|
|
}
|
|
return reflect.Value{}
|
|
}
|