mirror of
https://github.com/miniflux/v2.git
synced 2025-07-22 17:18:37 +00:00
Add OAuth2 authentication
This commit is contained in:
parent
9877051f12
commit
cc6d272eb7
351 changed files with 81664 additions and 55 deletions
209
vendor/google.golang.org/appengine/search/doc.go
generated
vendored
Normal file
209
vendor/google.golang.org/appengine/search/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package search provides a client for App Engine's search service.
|
||||
|
||||
|
||||
Basic Operations
|
||||
|
||||
Indexes contain documents. Each index is identified by its name: a
|
||||
human-readable ASCII string.
|
||||
|
||||
Within an index, documents are associated with an ID, which is also
|
||||
a human-readable ASCII string. A document's contents are a mapping from
|
||||
case-sensitive field names to values. Valid types for field values are:
|
||||
- string,
|
||||
- search.Atom,
|
||||
- search.HTML,
|
||||
- time.Time (stored with millisecond precision),
|
||||
- float64 (value between -2,147,483,647 and 2,147,483,647 inclusive),
|
||||
- appengine.GeoPoint.
|
||||
|
||||
The Get and Put methods on an Index load and save a document.
|
||||
A document's contents are typically represented by a struct pointer.
|
||||
|
||||
Example code:
|
||||
|
||||
type Doc struct {
|
||||
Author string
|
||||
Comment string
|
||||
Creation time.Time
|
||||
}
|
||||
|
||||
index, err := search.Open("comments")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newID, err := index.Put(ctx, "", &Doc{
|
||||
Author: "gopher",
|
||||
Comment: "the truth of the matter",
|
||||
Creation: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
A single document can be retrieved by its ID. Pass a destination struct
|
||||
to Get to hold the resulting document.
|
||||
|
||||
var doc Doc
|
||||
err := index.Get(ctx, id, &doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Search and Listing Documents
|
||||
|
||||
Indexes have two methods for retrieving multiple documents at once: Search and
|
||||
List.
|
||||
|
||||
Searching an index for a query will result in an iterator. As with an iterator
|
||||
from package datastore, pass a destination struct to Next to decode the next
|
||||
result. Next will return Done when the iterator is exhausted.
|
||||
|
||||
for t := index.Search(ctx, "Comment:truth", nil); ; {
|
||||
var doc Doc
|
||||
id, err := t.Next(&doc)
|
||||
if err == search.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "%s -> %#v\n", id, doc)
|
||||
}
|
||||
|
||||
Search takes a string query to determine which documents to return. The query
|
||||
can be simple, such as a single word to match, or complex. The query
|
||||
language is described at
|
||||
https://cloud.google.com/appengine/docs/go/search/query_strings
|
||||
|
||||
Search also takes an optional SearchOptions struct which gives much more
|
||||
control over how results are calculated and returned.
|
||||
|
||||
Call List to iterate over all documents in an index.
|
||||
|
||||
for t := index.List(ctx, nil); ; {
|
||||
var doc Doc
|
||||
id, err := t.Next(&doc)
|
||||
if err == search.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "%s -> %#v\n", id, doc)
|
||||
}
|
||||
|
||||
|
||||
Fields and Facets
|
||||
|
||||
A document's contents can be represented by a variety of types. These are
|
||||
typically struct pointers, but they can also be represented by any type
|
||||
implementing the FieldLoadSaver interface. The FieldLoadSaver allows metadata
|
||||
to be set for the document with the DocumentMetadata type. Struct pointers are
|
||||
more strongly typed and are easier to use; FieldLoadSavers are more flexible.
|
||||
|
||||
A document's contents can be expressed in two ways: fields and facets.
|
||||
|
||||
Fields are the most common way of providing content for documents. Fields can
|
||||
store data in multiple types and can be matched in searches using query
|
||||
strings.
|
||||
|
||||
Facets provide a way to attach categorical information to a document. The only
|
||||
valid types for facets are search.Atom and float64. Facets allow search
|
||||
results to contain summaries of the categories matched in a search, and to
|
||||
restrict searches to only match against specific categories.
|
||||
|
||||
By default, for struct pointers, all of the struct fields are used as document
|
||||
fields, and the field name used is the same as on the struct (and hence must
|
||||
start with an upper case letter). Struct fields may have a
|
||||
`search:"name,options"` tag. The name must start with a letter and be
|
||||
composed only of word characters. A "-" tag name means that the field will be
|
||||
ignored. If options is "facet" then the struct field will be used as a
|
||||
document facet. If options is "" then the comma may be omitted. There are no
|
||||
other recognized options.
|
||||
|
||||
Example code:
|
||||
|
||||
// A and B are renamed to a and b.
|
||||
// A, C and I are facets.
|
||||
// D's tag is equivalent to having no tag at all (E).
|
||||
// F and G are ignored entirely by the search package.
|
||||
// I has tag information for both the search and json packages.
|
||||
type TaggedStruct struct {
|
||||
A float64 `search:"a,facet"`
|
||||
B float64 `search:"b"`
|
||||
C float64 `search:",facet"`
|
||||
D float64 `search:""`
|
||||
E float64
|
||||
F float64 `search:"-"`
|
||||
G float64 `search:"-,facet"`
|
||||
I float64 `search:",facet" json:"i"`
|
||||
}
|
||||
|
||||
|
||||
The FieldLoadSaver Interface
|
||||
|
||||
A document's contents can also be represented by any type that implements the
|
||||
FieldLoadSaver interface. This type may be a struct pointer, but it
|
||||
does not have to be. The search package will call Load when loading the
|
||||
document's contents, and Save when saving them. In addition to a slice of
|
||||
Fields, the Load and Save methods also use the DocumentMetadata type to
|
||||
provide additional information about a document (such as its Rank, or set of
|
||||
Facets). Possible uses for this interface include deriving non-stored fields,
|
||||
verifying fields or setting specific languages for string and HTML fields.
|
||||
|
||||
Example code:
|
||||
|
||||
type CustomFieldsExample struct {
|
||||
// Item's title and which language it is in.
|
||||
Title string
|
||||
Lang string
|
||||
// Mass, in grams.
|
||||
Mass int
|
||||
}
|
||||
|
||||
func (x *CustomFieldsExample) Load(fields []search.Field, meta *search.DocumentMetadata) error {
|
||||
// Load the title field, failing if any other field is found.
|
||||
for _, f := range fields {
|
||||
if f.Name != "title" {
|
||||
return fmt.Errorf("unknown field %q", f.Name)
|
||||
}
|
||||
s, ok := f.Value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported type %T for field %q", f.Value, f.Name)
|
||||
}
|
||||
x.Title = s
|
||||
x.Lang = f.Language
|
||||
}
|
||||
// Load the mass facet, failing if any other facet is found.
|
||||
for _, f := range meta.Facets {
|
||||
if f.Name != "mass" {
|
||||
return fmt.Errorf("unknown facet %q", f.Name)
|
||||
}
|
||||
m, ok := f.Value.(float64)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported type %T for facet %q", f.Value, f.Name)
|
||||
}
|
||||
x.Mass = int(m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CustomFieldsExample) Save() ([]search.Field, *search.DocumentMetadata, error) {
|
||||
fields := []search.Field{
|
||||
{Name: "title", Value: x.Title, Language: x.Lang},
|
||||
}
|
||||
meta := &search.DocumentMetadata{
|
||||
Facets: {
|
||||
{Name: "mass", Value: float64(x.Mass)},
|
||||
},
|
||||
}
|
||||
return fields, meta, nil
|
||||
}
|
||||
*/
|
||||
package search
|
82
vendor/google.golang.org/appengine/search/field.go
generated
vendored
Normal file
82
vendor/google.golang.org/appengine/search/field.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package search
|
||||
|
||||
// Field is a name/value pair. A search index's document can be loaded and
|
||||
// saved as a sequence of Fields.
|
||||
type Field struct {
|
||||
// Name is the field name. A valid field name matches /[A-Za-z][A-Za-z0-9_]*/.
|
||||
Name string
|
||||
// Value is the field value. The valid types are:
|
||||
// - string,
|
||||
// - search.Atom,
|
||||
// - search.HTML,
|
||||
// - time.Time (stored with millisecond precision),
|
||||
// - float64,
|
||||
// - GeoPoint.
|
||||
Value interface{}
|
||||
// Language is a two-letter ISO 639-1 code for the field's language,
|
||||
// defaulting to "en" if nothing is specified. It may only be specified for
|
||||
// fields of type string and search.HTML.
|
||||
Language string
|
||||
// Derived marks fields that were calculated as a result of a
|
||||
// FieldExpression provided to Search. This field is ignored when saving a
|
||||
// document.
|
||||
Derived bool
|
||||
}
|
||||
|
||||
// Facet is a name/value pair which is used to add categorical information to a
|
||||
// document.
|
||||
type Facet struct {
|
||||
// Name is the facet name. A valid facet name matches /[A-Za-z][A-Za-z0-9_]*/.
|
||||
// A facet name cannot be longer than 500 characters.
|
||||
Name string
|
||||
// Value is the facet value.
|
||||
//
|
||||
// When being used in documents (for example, in
|
||||
// DocumentMetadata.Facets), the valid types are:
|
||||
// - search.Atom,
|
||||
// - float64.
|
||||
//
|
||||
// When being used in SearchOptions.Refinements or being returned
|
||||
// in FacetResult, the valid types are:
|
||||
// - search.Atom,
|
||||
// - search.Range.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// DocumentMetadata is a struct containing information describing a given document.
|
||||
type DocumentMetadata struct {
|
||||
// Rank is an integer specifying the order the document will be returned in
|
||||
// search results. If zero, the rank will be set to the number of seconds since
|
||||
// 2011-01-01 00:00:00 UTC when being Put into an index.
|
||||
Rank int
|
||||
// Facets is the set of facets for this document.
|
||||
Facets []Facet
|
||||
}
|
||||
|
||||
// FieldLoadSaver can be converted from and to a slice of Fields
|
||||
// with additional document metadata.
|
||||
type FieldLoadSaver interface {
|
||||
Load([]Field, *DocumentMetadata) error
|
||||
Save() ([]Field, *DocumentMetadata, error)
|
||||
}
|
||||
|
||||
// FieldList converts a []Field to implement FieldLoadSaver.
|
||||
type FieldList []Field
|
||||
|
||||
// Load loads all of the provided fields into l.
|
||||
// It does not first reset *l to an empty slice.
|
||||
func (l *FieldList) Load(f []Field, _ *DocumentMetadata) error {
|
||||
*l = append(*l, f...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save returns all of l's fields as a slice of Fields.
|
||||
func (l *FieldList) Save() ([]Field, *DocumentMetadata, error) {
|
||||
return *l, nil, nil
|
||||
}
|
||||
|
||||
var _ FieldLoadSaver = (*FieldList)(nil)
|
1121
vendor/google.golang.org/appengine/search/search.go
generated
vendored
Normal file
1121
vendor/google.golang.org/appengine/search/search.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1000
vendor/google.golang.org/appengine/search/search_test.go
generated
vendored
Normal file
1000
vendor/google.golang.org/appengine/search/search_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
251
vendor/google.golang.org/appengine/search/struct.go
generated
vendored
Normal file
251
vendor/google.golang.org/appengine/search/struct.go
generated
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package search
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ErrFieldMismatch is returned when a field is to be loaded into a different
|
||||
// than the one it was stored from, or when a field is missing or unexported in
|
||||
// the destination struct.
|
||||
type ErrFieldMismatch struct {
|
||||
FieldName string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *ErrFieldMismatch) Error() string {
|
||||
return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
|
||||
}
|
||||
|
||||
// ErrFacetMismatch is returned when a facet is to be loaded into a different
|
||||
// type than the one it was stored from, or when a field is missing or
|
||||
// unexported in the destination struct. StructType is the type of the struct
|
||||
// pointed to by the destination argument passed to Iterator.Next.
|
||||
type ErrFacetMismatch struct {
|
||||
StructType reflect.Type
|
||||
FacetName string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *ErrFacetMismatch) Error() string {
|
||||
return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason)
|
||||
}
|
||||
|
||||
// structCodec defines how to convert a given struct to/from a search document.
|
||||
type structCodec struct {
|
||||
// byIndex returns the struct tag for the i'th struct field.
|
||||
byIndex []structTag
|
||||
|
||||
// fieldByName returns the index of the struct field for the given field name.
|
||||
fieldByName map[string]int
|
||||
|
||||
// facetByName returns the index of the struct field for the given facet name,
|
||||
facetByName map[string]int
|
||||
}
|
||||
|
||||
// structTag holds a structured version of each struct field's parsed tag.
|
||||
type structTag struct {
|
||||
name string
|
||||
facet bool
|
||||
ignore bool
|
||||
}
|
||||
|
||||
var (
|
||||
codecsMu sync.RWMutex
|
||||
codecs = map[reflect.Type]*structCodec{}
|
||||
)
|
||||
|
||||
func loadCodec(t reflect.Type) (*structCodec, error) {
|
||||
codecsMu.RLock()
|
||||
codec, ok := codecs[t]
|
||||
codecsMu.RUnlock()
|
||||
if ok {
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
codecsMu.Lock()
|
||||
defer codecsMu.Unlock()
|
||||
if codec, ok := codecs[t]; ok {
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
codec = &structCodec{
|
||||
fieldByName: make(map[string]int),
|
||||
facetByName: make(map[string]int),
|
||||
}
|
||||
|
||||
for i, I := 0, t.NumField(); i < I; i++ {
|
||||
f := t.Field(i)
|
||||
name, opts := f.Tag.Get("search"), ""
|
||||
if i := strings.Index(name, ","); i != -1 {
|
||||
name, opts = name[:i], name[i+1:]
|
||||
}
|
||||
ignore := false
|
||||
if name == "-" {
|
||||
ignore = true
|
||||
} else if name == "" {
|
||||
name = f.Name
|
||||
} else if !validFieldName(name) {
|
||||
return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name)
|
||||
}
|
||||
facet := opts == "facet"
|
||||
codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet, ignore: ignore})
|
||||
if facet {
|
||||
codec.facetByName[name] = i
|
||||
} else {
|
||||
codec.fieldByName[name] = i
|
||||
}
|
||||
}
|
||||
|
||||
codecs[t] = codec
|
||||
return codec, nil
|
||||
}
|
||||
|
||||
// structFLS adapts a struct to be a FieldLoadSaver.
|
||||
type structFLS struct {
|
||||
v reflect.Value
|
||||
codec *structCodec
|
||||
}
|
||||
|
||||
func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error {
|
||||
var err error
|
||||
for _, field := range fields {
|
||||
i, ok := s.codec.fieldByName[field.Name]
|
||||
if !ok {
|
||||
// Note the error, but keep going.
|
||||
err = &ErrFieldMismatch{
|
||||
FieldName: field.Name,
|
||||
Reason: "no such struct field",
|
||||
}
|
||||
continue
|
||||
|
||||
}
|
||||
f := s.v.Field(i)
|
||||
if !f.CanSet() {
|
||||
// Note the error, but keep going.
|
||||
err = &ErrFieldMismatch{
|
||||
FieldName: field.Name,
|
||||
Reason: "cannot set struct field",
|
||||
}
|
||||
continue
|
||||
}
|
||||
v := reflect.ValueOf(field.Value)
|
||||
if ft, vt := f.Type(), v.Type(); ft != vt {
|
||||
err = &ErrFieldMismatch{
|
||||
FieldName: field.Name,
|
||||
Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
|
||||
}
|
||||
continue
|
||||
}
|
||||
f.Set(v)
|
||||
}
|
||||
if meta == nil {
|
||||
return err
|
||||
}
|
||||
for _, facet := range meta.Facets {
|
||||
i, ok := s.codec.facetByName[facet.Name]
|
||||
if !ok {
|
||||
// Note the error, but keep going.
|
||||
if err == nil {
|
||||
err = &ErrFacetMismatch{
|
||||
StructType: s.v.Type(),
|
||||
FacetName: facet.Name,
|
||||
Reason: "no matching field found",
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
f := s.v.Field(i)
|
||||
if !f.CanSet() {
|
||||
// Note the error, but keep going.
|
||||
if err == nil {
|
||||
err = &ErrFacetMismatch{
|
||||
StructType: s.v.Type(),
|
||||
FacetName: facet.Name,
|
||||
Reason: "unable to set unexported field of struct",
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
v := reflect.ValueOf(facet.Value)
|
||||
if ft, vt := f.Type(), v.Type(); ft != vt {
|
||||
if err == nil {
|
||||
err = &ErrFacetMismatch{
|
||||
StructType: s.v.Type(),
|
||||
FacetName: facet.Name,
|
||||
Reason: fmt.Sprintf("type mismatch: %v for %d data", ft, vt),
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
f.Set(v)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
|
||||
fields := make([]Field, 0, len(s.codec.fieldByName))
|
||||
var facets []Facet
|
||||
for i, tag := range s.codec.byIndex {
|
||||
if tag.ignore {
|
||||
continue
|
||||
}
|
||||
f := s.v.Field(i)
|
||||
if !f.CanSet() {
|
||||
continue
|
||||
}
|
||||
if tag.facet {
|
||||
facets = append(facets, Facet{Name: tag.name, Value: f.Interface()})
|
||||
} else {
|
||||
fields = append(fields, Field{Name: tag.name, Value: f.Interface()})
|
||||
}
|
||||
}
|
||||
return fields, &DocumentMetadata{Facets: facets}, nil
|
||||
}
|
||||
|
||||
// newStructFLS returns a FieldLoadSaver for the struct pointer p.
|
||||
func newStructFLS(p interface{}) (FieldLoadSaver, error) {
|
||||
v := reflect.ValueOf(p)
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
|
||||
return nil, ErrInvalidDocumentType
|
||||
}
|
||||
codec, err := loadCodec(v.Elem().Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return structFLS{v.Elem(), codec}, nil
|
||||
}
|
||||
|
||||
func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error {
|
||||
x, err := newStructFLS(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return x.Load(f, meta)
|
||||
}
|
||||
|
||||
func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) {
|
||||
x, err := newStructFLS(src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return x.Save()
|
||||
}
|
||||
|
||||
// LoadStruct loads the fields from f to dst. dst must be a struct pointer.
|
||||
func LoadStruct(dst interface{}, f []Field) error {
|
||||
return loadStructWithMeta(dst, f, nil)
|
||||
}
|
||||
|
||||
// SaveStruct returns the fields from src as a slice of Field.
|
||||
// src must be a struct pointer.
|
||||
func SaveStruct(src interface{}) ([]Field, error) {
|
||||
f, _, err := saveStructWithMeta(src)
|
||||
return f, err
|
||||
}
|
213
vendor/google.golang.org/appengine/search/struct_test.go
generated
vendored
Normal file
213
vendor/google.golang.org/appengine/search/struct_test.go
generated
vendored
Normal file
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package search
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadingStruct(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
fields []Field
|
||||
meta *DocumentMetadata
|
||||
want interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
desc: "Basic struct",
|
||||
fields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
},
|
||||
want: &struct {
|
||||
Name string
|
||||
Legs float64
|
||||
}{"Gopher", 4},
|
||||
},
|
||||
{
|
||||
desc: "Struct with tags",
|
||||
fields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
{Name: "about", Value: "Likes slide rules."},
|
||||
},
|
||||
meta: &DocumentMetadata{Facets: []Facet{
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
{Name: "Fur", Value: Atom("furry")},
|
||||
}},
|
||||
want: &struct {
|
||||
Name string
|
||||
Info string `search:"about"`
|
||||
Legs float64 `search:",facet"`
|
||||
Fuzz Atom `search:"Fur,facet"`
|
||||
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
|
||||
},
|
||||
{
|
||||
desc: "Bad field from tag",
|
||||
want: &struct {
|
||||
AlphaBeta string `search:"αβ"`
|
||||
}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Ignore missing field",
|
||||
fields: []Field{
|
||||
{Name: "Meaning", Value: float64(42)},
|
||||
},
|
||||
want: &struct{}{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Ignore unsettable field",
|
||||
fields: []Field{
|
||||
{Name: "meaning", Value: float64(42)},
|
||||
},
|
||||
want: &struct{ meaning float64 }{}, // field not populated.
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Error on missing facet",
|
||||
meta: &DocumentMetadata{Facets: []Facet{
|
||||
{Name: "Set", Value: Atom("yes")},
|
||||
{Name: "Missing", Value: Atom("no")},
|
||||
}},
|
||||
want: &struct {
|
||||
Set Atom `search:",facet"`
|
||||
}{Atom("yes")},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Error on unsettable facet",
|
||||
meta: &DocumentMetadata{Facets: []Facet{
|
||||
{Name: "Set", Value: Atom("yes")},
|
||||
{Name: "unset", Value: Atom("no")},
|
||||
}},
|
||||
want: &struct {
|
||||
Set Atom `search:",facet"`
|
||||
}{Atom("yes")},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Error setting ignored field",
|
||||
fields: []Field{
|
||||
{Name: "Set", Value: "yes"},
|
||||
{Name: "Ignored", Value: "no"},
|
||||
},
|
||||
want: &struct {
|
||||
Set string
|
||||
Ignored string `search:"-"`
|
||||
}{Set: "yes"},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
desc: "Error setting ignored facet",
|
||||
meta: &DocumentMetadata{Facets: []Facet{
|
||||
{Name: "Set", Value: Atom("yes")},
|
||||
{Name: "Ignored", Value: Atom("no")},
|
||||
}},
|
||||
want: &struct {
|
||||
Set Atom `search:",facet"`
|
||||
Ignored Atom `search:"-,facet"`
|
||||
}{Set: Atom("yes")},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
// Make a pointer to an empty version of what want points to.
|
||||
dst := reflect.New(reflect.TypeOf(tt.want).Elem()).Interface()
|
||||
err := loadStructWithMeta(dst, tt.fields, tt.meta)
|
||||
if err != nil != tt.wantErr {
|
||||
t.Errorf("%s: got err %v; want err %t", tt.desc, err, tt.wantErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(dst, tt.want) {
|
||||
t.Errorf("%s: doesn't match\ngot: %v\nwant: %v", tt.desc, dst, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSavingStruct(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
doc interface{}
|
||||
wantFields []Field
|
||||
wantFacets []Facet
|
||||
}{
|
||||
{
|
||||
desc: "Basic struct",
|
||||
doc: &struct {
|
||||
Name string
|
||||
Legs float64
|
||||
}{"Gopher", 4},
|
||||
wantFields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Struct with tags",
|
||||
doc: &struct {
|
||||
Name string
|
||||
Info string `search:"about"`
|
||||
Legs float64 `search:",facet"`
|
||||
Fuzz Atom `search:"Fur,facet"`
|
||||
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
|
||||
wantFields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
{Name: "about", Value: "Likes slide rules."},
|
||||
},
|
||||
wantFacets: []Facet{
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
{Name: "Fur", Value: Atom("furry")},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ignore unexported struct fields",
|
||||
doc: &struct {
|
||||
Name string
|
||||
info string
|
||||
Legs float64 `search:",facet"`
|
||||
fuzz Atom `search:",facet"`
|
||||
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
|
||||
wantFields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
},
|
||||
wantFacets: []Facet{
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ignore fields marked -",
|
||||
doc: &struct {
|
||||
Name string
|
||||
Info string `search:"-"`
|
||||
Legs float64 `search:",facet"`
|
||||
Fuzz Atom `search:"-,facet"`
|
||||
}{"Gopher", "Likes slide rules.", 4, Atom("furry")},
|
||||
wantFields: []Field{
|
||||
{Name: "Name", Value: "Gopher"},
|
||||
},
|
||||
wantFacets: []Facet{
|
||||
{Name: "Legs", Value: float64(4)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testCases {
|
||||
fields, meta, err := saveStructWithMeta(tt.doc)
|
||||
if err != nil {
|
||||
t.Errorf("%s: got err %v; want nil", tt.desc, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(fields, tt.wantFields) {
|
||||
t.Errorf("%s: fields don't match\ngot: %v\nwant: %v", tt.desc, fields, tt.wantFields)
|
||||
}
|
||||
if facets := meta.Facets; !reflect.DeepEqual(facets, tt.wantFacets) {
|
||||
t.Errorf("%s: facets don't match\ngot: %v\nwant: %v", tt.desc, facets, tt.wantFacets)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue