main 98594d0

schema.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package template
4
5import (
6	"fmt"
7	"strings"
8)
9
10// DefinitionType is the type of a schema [Definition].
11type DefinitionType int
12
13// The definition types.
14const (
15	DefNull DefinitionType = iota
16	DefBoolean
17	DefNumber
18	DefString
19	DefSequence
20	DefMapping
21	DefOneOf
22)
23
24// Property is a well-known key of a mapping definition.
25type Property struct {
26	// Type is the name of the definition of the value.
27	Type        string
28	Required    bool
29	Description string
30}
31
32// Definition is a named type of the schema. Which fields are meaningful
33// depends on Type.
34type Definition struct {
35	Name        string
36	Type        DefinitionType
37	Description string
38	// Context lists the named contexts and functions ("github",
39	// "success(0,MAX)") expressions in values of this type may use.
40	Context []string
41
42	// String definitions.
43	Constant        string
44	IgnoreCase      bool
45	RequireNonEmpty bool
46	IsExpression    bool
47
48	// Sequence definitions.
49	ItemType string
50
51	// Mapping definitions: Properties in schema order.
52	PropertyNames  []string
53	Properties     map[string]Property
54	LooseKeyType   string
55	LooseValueType string
56
57	// One-of definitions: the names of the alternatives.
58	OneOf []string
59}
60
61// Schema is a set of named definitions.
62type Schema struct {
63	Version     string
64	definitions map[string]*Definition
65}
66
67// Definition returns the definition called name, or nil.
68func (s *Schema) Definition(name string) *Definition { return s.definitions[name] }
69
70// get is the reference's getDefinition, which throws when the name is unknown.
71func (s *Schema) get(name string) *Definition {
72	d := s.definitions[name]
73	if d == nil {
74		fatalf("Schema definition '%s' not found", name)
75	}
76	return d
77}
78
79// scalarDefinitions expands one-of definitions and returns all scalar ones.
80func (s *Schema) scalarDefinitions(d *Definition) []*Definition {
81	switch d.Type {
82	case DefNull, DefBoolean, DefNumber, DefString:
83		return []*Definition{d}
84	case DefOneOf:
85		var out []*Definition
86		for _, n := range d.OneOf {
87			out = append(out, s.scalarDefinitions(s.get(n))...)
88		}
89		return out
90	}
91	return nil
92}
93
94// definitionsOfType expands a one-of definition one level and returns the
95// alternatives of type t.
96func (s *Schema) definitionsOfType(d *Definition, t DefinitionType) []*Definition {
97	if d.Type == t {
98		return []*Definition{d}
99	}
100	var out []*Definition
101	if d.Type == DefOneOf {
102		for _, n := range d.OneOf {
103			if nd := s.get(n); nd.Type == t {
104				out = append(out, nd)
105			}
106		}
107	}
108	return out
109}
110
111// isMatch reports whether a literal token is accepted by a scalar definition.
112func (d *Definition) isMatch(t Token) bool {
113	switch d.Type {
114	case DefNull:
115		return t.Kind() == KindNull
116	case DefBoolean:
117		return t.Kind() == KindBoolean
118	case DefNumber:
119		return t.Kind() == KindNumber
120	case DefString:
121		s, ok := t.(*String)
122		if !ok {
123			return false
124		}
125		switch {
126		case d.Constant != "":
127			if d.IgnoreCase {
128				return strings.EqualFold(d.Constant, s.Value)
129			}
130			return d.Constant == s.Value
131		case d.RequireNonEmpty:
132			return s.Value != ""
133		}
134		return true
135	}
136	return false
137}
138
139// matchPropertyAndFilter looks for a property called name in any of defs; when
140// found in some only, defs is narrowed to those. It returns the property, if
141// any, and the possibly narrowed list.
142func matchPropertyAndFilter(defs []*Definition, name string) (*Property, []*Definition) {
143	var result *Property
144	notFoundInSome := false
145	for _, d := range defs {
146		if p, ok := d.Properties[name]; ok {
147			p := p
148			result = &p
149		} else {
150			notFoundInSome = true
151		}
152	}
153	if result != nil && notFoundInSome {
154		kept := defs[:0:0]
155		for _, d := range defs {
156			if _, ok := d.Properties[name]; ok {
157				kept = append(kept, d)
158			}
159		}
160		defs = kept
161	}
162	return result, defs
163}
164
165// fatal is the panic value used for errors that abort a read, which the
166// reference throws as exceptions.
167type fatal struct{ msg string }
168
169func fatalf(format string, a ...any) { panic(fatal{fmt.Sprintf(format, a...)}) }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 3

↑↓ moveenter openesc close