Enable Grafana extensions at build time. (#11752)
* extensions: import and build * bus: use predefined error * enterprise: build script for enterprise packages * poc: auto registering services and dependency injection (cherry picked from commit b5b1ef875f905473af41e49f8071cb9028edc845) * poc: backend services registry progress (cherry picked from commit 97be69725881241bfbf1e7adf0e66801d6b0af3d) * poc: minor update (cherry picked from commit 03d7a6888b81403f458b94305792e075568f0794) * ioc: introduce manuel ioc * enterprise: adds setting for enterprise * build: test and build specific ee commit * cleanup: test testing code * removes example hello service
This commit is contained in:
committed by
Torkel Ödegaard
parent
afce0feb05
commit
28f7b6dad1
+576
@@ -0,0 +1,576 @@
|
||||
// Package inject provides a reflect based injector. A large application built
|
||||
// with dependency injection in mind will typically involve the boring work of
|
||||
// setting up the object graph. This library attempts to take care of this
|
||||
// boring work by creating and connecting the various objects. Its use involves
|
||||
// you seeding the object graph with some (possibly incomplete) objects, where
|
||||
// the underlying types have been tagged for injection. Given this, the
|
||||
// library will populate the objects creating new ones as necessary. It uses
|
||||
// singletons by default, supports optional private instances as well as named
|
||||
// instances.
|
||||
//
|
||||
// It works using Go's reflection package and is inherently limited in what it
|
||||
// can do as opposed to a code-gen system with respect to private fields.
|
||||
//
|
||||
// The usage pattern for the library involves struct tags. It requires the tag
|
||||
// format used by the various standard libraries, like json, xml etc. It
|
||||
// involves tags in one of the three forms below:
|
||||
//
|
||||
// `inject:""`
|
||||
// `inject:"private"`
|
||||
// `inject:"dev logger"`
|
||||
//
|
||||
// The first no value syntax is for the common case of a singleton dependency
|
||||
// of the associated type. The second triggers creation of a private instance
|
||||
// for the associated type. Finally the last form is asking for a named
|
||||
// dependency called "dev logger".
|
||||
package inject
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
|
||||
"github.com/facebookgo/structtag"
|
||||
)
|
||||
|
||||
// Logger allows for simple logging as inject traverses and populates the
|
||||
// object graph.
|
||||
type Logger interface {
|
||||
Debugf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// Populate is a short-hand for populating a graph with the given incomplete
|
||||
// object values.
|
||||
func Populate(values ...interface{}) error {
|
||||
var g Graph
|
||||
for _, v := range values {
|
||||
if err := g.Provide(&Object{Value: v}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return g.Populate()
|
||||
}
|
||||
|
||||
// An Object in the Graph.
|
||||
type Object struct {
|
||||
Value interface{}
|
||||
Name string // Optional
|
||||
Complete bool // If true, the Value will be considered complete
|
||||
Fields map[string]*Object // Populated with the field names that were injected and their corresponding *Object.
|
||||
reflectType reflect.Type
|
||||
reflectValue reflect.Value
|
||||
private bool // If true, the Value will not be used and will only be populated
|
||||
created bool // If true, the Object was created by us
|
||||
embedded bool // If true, the Object is an embedded struct provided internally
|
||||
}
|
||||
|
||||
// String representation suitable for human consumption.
|
||||
func (o *Object) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, o.reflectType)
|
||||
if o.Name != "" {
|
||||
fmt.Fprintf(&buf, " named %s", o.Name)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (o *Object) addDep(field string, dep *Object) {
|
||||
if o.Fields == nil {
|
||||
o.Fields = make(map[string]*Object)
|
||||
}
|
||||
o.Fields[field] = dep
|
||||
}
|
||||
|
||||
// The Graph of Objects.
|
||||
type Graph struct {
|
||||
Logger Logger // Optional, will trigger debug logging.
|
||||
unnamed []*Object
|
||||
unnamedType map[reflect.Type]bool
|
||||
named map[string]*Object
|
||||
}
|
||||
|
||||
// Provide objects to the Graph. The Object documentation describes
|
||||
// the impact of various fields.
|
||||
func (g *Graph) Provide(objects ...*Object) error {
|
||||
for _, o := range objects {
|
||||
o.reflectType = reflect.TypeOf(o.Value)
|
||||
o.reflectValue = reflect.ValueOf(o.Value)
|
||||
|
||||
if o.Fields != nil {
|
||||
return fmt.Errorf(
|
||||
"fields were specified on object %s when it was provided",
|
||||
o,
|
||||
)
|
||||
}
|
||||
|
||||
if o.Name == "" {
|
||||
if !isStructPtr(o.reflectType) {
|
||||
return fmt.Errorf(
|
||||
"expected unnamed object value to be a pointer to a struct but got type %s "+
|
||||
"with value %v",
|
||||
o.reflectType,
|
||||
o.Value,
|
||||
)
|
||||
}
|
||||
|
||||
if !o.private {
|
||||
if g.unnamedType == nil {
|
||||
g.unnamedType = make(map[reflect.Type]bool)
|
||||
}
|
||||
|
||||
if g.unnamedType[o.reflectType] {
|
||||
return fmt.Errorf(
|
||||
"provided two unnamed instances of type *%s.%s",
|
||||
o.reflectType.Elem().PkgPath(), o.reflectType.Elem().Name(),
|
||||
)
|
||||
}
|
||||
g.unnamedType[o.reflectType] = true
|
||||
}
|
||||
g.unnamed = append(g.unnamed, o)
|
||||
} else {
|
||||
if g.named == nil {
|
||||
g.named = make(map[string]*Object)
|
||||
}
|
||||
|
||||
if g.named[o.Name] != nil {
|
||||
return fmt.Errorf("provided two instances named %s", o.Name)
|
||||
}
|
||||
g.named[o.Name] = o
|
||||
}
|
||||
|
||||
if g.Logger != nil {
|
||||
if o.created {
|
||||
g.Logger.Debugf("created %s", o)
|
||||
} else if o.embedded {
|
||||
g.Logger.Debugf("provided embedded %s", o)
|
||||
} else {
|
||||
g.Logger.Debugf("provided %s", o)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Populate the incomplete Objects.
|
||||
func (g *Graph) Populate() error {
|
||||
for _, o := range g.named {
|
||||
if o.Complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := g.populateExplicit(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// We append and modify our slice as we go along, so we don't use a standard
|
||||
// range loop, and do a single pass thru each object in our graph.
|
||||
i := 0
|
||||
for {
|
||||
if i == len(g.unnamed) {
|
||||
break
|
||||
}
|
||||
|
||||
o := g.unnamed[i]
|
||||
i++
|
||||
|
||||
if o.Complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := g.populateExplicit(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// A Second pass handles injecting Interface values to ensure we have created
|
||||
// all concrete types first.
|
||||
for _, o := range g.unnamed {
|
||||
if o.Complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := g.populateUnnamedInterface(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, o := range g.named {
|
||||
if o.Complete {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := g.populateUnnamedInterface(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graph) populateExplicit(o *Object) error {
|
||||
// Ignore named value types.
|
||||
if o.Name != "" && !isStructPtr(o.reflectType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
StructLoop:
|
||||
for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
|
||||
field := o.reflectValue.Elem().Field(i)
|
||||
fieldType := field.Type()
|
||||
fieldTag := o.reflectType.Elem().Field(i).Tag
|
||||
fieldName := o.reflectType.Elem().Field(i).Name
|
||||
tag, err := parseTag(string(fieldTag))
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"unexpected tag format `%s` for field %s in type %s",
|
||||
string(fieldTag),
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Skip fields without a tag.
|
||||
if tag == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Cannot be used with unexported fields.
|
||||
if !field.CanSet() {
|
||||
return fmt.Errorf(
|
||||
"inject requested on unexported field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Inline tag on anything besides a struct is considered invalid.
|
||||
if tag.Inline && fieldType.Kind() != reflect.Struct {
|
||||
return fmt.Errorf(
|
||||
"inline requested on non inlined field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Don't overwrite existing values.
|
||||
if !isNilOrZero(field, fieldType) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Named injects must have been explicitly provided.
|
||||
if tag.Name != "" {
|
||||
existing := g.named[tag.Name]
|
||||
if existing == nil {
|
||||
return fmt.Errorf(
|
||||
"did not find object named %s required by field %s in type %s",
|
||||
tag.Name,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
if !existing.reflectType.AssignableTo(fieldType) {
|
||||
return fmt.Errorf(
|
||||
"object named %s of type %s is not assignable to field %s (%s) in type %s",
|
||||
tag.Name,
|
||||
fieldType,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
existing.reflectType,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
field.Set(reflect.ValueOf(existing.Value))
|
||||
if g.Logger != nil {
|
||||
g.Logger.Debugf(
|
||||
"assigned %s to field %s in %s",
|
||||
existing,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o,
|
||||
)
|
||||
}
|
||||
o.addDep(fieldName, existing)
|
||||
continue StructLoop
|
||||
}
|
||||
|
||||
// Inline struct values indicate we want to traverse into it, but not
|
||||
// inject itself. We require an explicit "inline" tag for this to work.
|
||||
if fieldType.Kind() == reflect.Struct {
|
||||
if tag.Private {
|
||||
return fmt.Errorf(
|
||||
"cannot use private inject on inline struct on field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
if !tag.Inline {
|
||||
return fmt.Errorf(
|
||||
"inline struct on field %s in type %s requires an explicit \"inline\" tag",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
err := g.Provide(&Object{
|
||||
Value: field.Addr().Interface(),
|
||||
private: true,
|
||||
embedded: o.reflectType.Elem().Field(i).Anonymous,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Interface injection is handled in a second pass.
|
||||
if fieldType.Kind() == reflect.Interface {
|
||||
continue
|
||||
}
|
||||
|
||||
// Maps are created and required to be private.
|
||||
if fieldType.Kind() == reflect.Map {
|
||||
if !tag.Private {
|
||||
return fmt.Errorf(
|
||||
"inject on map field %s in type %s must be named or private",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
field.Set(reflect.MakeMap(fieldType))
|
||||
if g.Logger != nil {
|
||||
g.Logger.Debugf(
|
||||
"made map for field %s in %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o,
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Can only inject Pointers from here on.
|
||||
if !isStructPtr(fieldType) {
|
||||
return fmt.Errorf(
|
||||
"found inject tag on unsupported field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Unless it's a private inject, we'll look for an existing instance of the
|
||||
// same type.
|
||||
if !tag.Private {
|
||||
for _, existing := range g.unnamed {
|
||||
if existing.private {
|
||||
continue
|
||||
}
|
||||
if existing.reflectType.AssignableTo(fieldType) {
|
||||
field.Set(reflect.ValueOf(existing.Value))
|
||||
if g.Logger != nil {
|
||||
g.Logger.Debugf(
|
||||
"assigned existing %s to field %s in %s",
|
||||
existing,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o,
|
||||
)
|
||||
}
|
||||
o.addDep(fieldName, existing)
|
||||
continue StructLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newValue := reflect.New(fieldType.Elem())
|
||||
newObject := &Object{
|
||||
Value: newValue.Interface(),
|
||||
private: tag.Private,
|
||||
created: true,
|
||||
}
|
||||
|
||||
// Add the newly ceated object to the known set of objects.
|
||||
err = g.Provide(newObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finally assign the newly created object to our field.
|
||||
field.Set(newValue)
|
||||
if g.Logger != nil {
|
||||
g.Logger.Debugf(
|
||||
"assigned newly created %s to field %s in %s",
|
||||
newObject,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o,
|
||||
)
|
||||
}
|
||||
o.addDep(fieldName, newObject)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graph) populateUnnamedInterface(o *Object) error {
|
||||
// Ignore named value types.
|
||||
if o.Name != "" && !isStructPtr(o.reflectType) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < o.reflectValue.Elem().NumField(); i++ {
|
||||
field := o.reflectValue.Elem().Field(i)
|
||||
fieldType := field.Type()
|
||||
fieldTag := o.reflectType.Elem().Field(i).Tag
|
||||
fieldName := o.reflectType.Elem().Field(i).Name
|
||||
tag, err := parseTag(string(fieldTag))
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"unexpected tag format `%s` for field %s in type %s",
|
||||
string(fieldTag),
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Skip fields without a tag.
|
||||
if tag == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// We only handle interface injection here. Other cases including errors
|
||||
// are handled in the first pass when we inject pointers.
|
||||
if fieldType.Kind() != reflect.Interface {
|
||||
continue
|
||||
}
|
||||
|
||||
// Interface injection can't be private because we can't instantiate new
|
||||
// instances of an interface.
|
||||
if tag.Private {
|
||||
return fmt.Errorf(
|
||||
"found private inject tag on interface field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
|
||||
// Don't overwrite existing values.
|
||||
if !isNilOrZero(field, fieldType) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Named injects must have already been handled in populateExplicit.
|
||||
if tag.Name != "" {
|
||||
panic(fmt.Sprintf("unhandled named instance with name %s", tag.Name))
|
||||
}
|
||||
|
||||
// Find one, and only one assignable value for the field.
|
||||
var found *Object
|
||||
for _, existing := range g.unnamed {
|
||||
if existing.private {
|
||||
continue
|
||||
}
|
||||
if existing.reflectType.AssignableTo(fieldType) {
|
||||
if found != nil {
|
||||
return fmt.Errorf(
|
||||
"found two assignable values for field %s in type %s. one type "+
|
||||
"%s with value %v and another type %s with value %v",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
found.reflectType,
|
||||
found.Value,
|
||||
existing.reflectType,
|
||||
existing.reflectValue,
|
||||
)
|
||||
}
|
||||
found = existing
|
||||
field.Set(reflect.ValueOf(existing.Value))
|
||||
if g.Logger != nil {
|
||||
g.Logger.Debugf(
|
||||
"assigned existing %s to interface field %s in %s",
|
||||
existing,
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o,
|
||||
)
|
||||
}
|
||||
o.addDep(fieldName, existing)
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find an assignable value, we're missing something.
|
||||
if found == nil {
|
||||
return fmt.Errorf(
|
||||
"found no assignable value for field %s in type %s",
|
||||
o.reflectType.Elem().Field(i).Name,
|
||||
o.reflectType,
|
||||
)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Objects returns all known objects, named as well as unnamed. The returned
|
||||
// elements are not in a stable order.
|
||||
func (g *Graph) Objects() []*Object {
|
||||
objects := make([]*Object, 0, len(g.unnamed)+len(g.named))
|
||||
for _, o := range g.unnamed {
|
||||
if !o.embedded {
|
||||
objects = append(objects, o)
|
||||
}
|
||||
}
|
||||
for _, o := range g.named {
|
||||
if !o.embedded {
|
||||
objects = append(objects, o)
|
||||
}
|
||||
}
|
||||
// randomize to prevent callers from relying on ordering
|
||||
for i := 0; i < len(objects); i++ {
|
||||
j := rand.Intn(i + 1)
|
||||
objects[i], objects[j] = objects[j], objects[i]
|
||||
}
|
||||
return objects
|
||||
}
|
||||
|
||||
var (
|
||||
injectOnly = &tag{}
|
||||
injectPrivate = &tag{Private: true}
|
||||
injectInline = &tag{Inline: true}
|
||||
)
|
||||
|
||||
type tag struct {
|
||||
Name string
|
||||
Inline bool
|
||||
Private bool
|
||||
}
|
||||
|
||||
func parseTag(t string) (*tag, error) {
|
||||
found, value, err := structtag.Extract("inject", t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, nil
|
||||
}
|
||||
if value == "" {
|
||||
return injectOnly, nil
|
||||
}
|
||||
if value == "inline" {
|
||||
return injectInline, nil
|
||||
}
|
||||
if value == "private" {
|
||||
return injectPrivate, nil
|
||||
}
|
||||
return &tag{Name: value}, nil
|
||||
}
|
||||
|
||||
func isStructPtr(t reflect.Type) bool {
|
||||
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func isNilOrZero(v reflect.Value, t reflect.Type) bool {
|
||||
switch v.Kind() {
|
||||
default:
|
||||
return reflect.DeepEqual(v.Interface(), reflect.Zero(t).Interface())
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
BSD License
|
||||
|
||||
For inject software
|
||||
|
||||
Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name Facebook nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
Additional Grant of Patent Rights Version 2
|
||||
|
||||
"Software" means the inject software distributed by Facebook, Inc.
|
||||
|
||||
Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
|
||||
("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
|
||||
(subject to the termination provision below) license under any Necessary
|
||||
Claims, to make, have made, use, sell, offer to sell, import, and otherwise
|
||||
transfer the Software. For avoidance of doubt, no license is granted under
|
||||
Facebook’s rights in any patent claims that are infringed by (i) modifications
|
||||
to the Software made by you or any third party or (ii) the Software in
|
||||
combination with any software or other technology.
|
||||
|
||||
The license granted hereunder will terminate, automatically and without notice,
|
||||
if you (or any of your subsidiaries, corporate affiliates or agents) initiate
|
||||
directly or indirectly, or take a direct financial interest in, any Patent
|
||||
Assertion: (i) against Facebook or any of its subsidiaries or corporate
|
||||
affiliates, (ii) against any party if such Patent Assertion arises in whole or
|
||||
in part from any software, technology, product or service of Facebook or any of
|
||||
its subsidiaries or corporate affiliates, or (iii) against any party relating
|
||||
to the Software. Notwithstanding the foregoing, if Facebook or any of its
|
||||
subsidiaries or corporate affiliates files a lawsuit alleging patent
|
||||
infringement against you in the first instance, and you respond by filing a
|
||||
patent infringement counterclaim in that lawsuit against that party that is
|
||||
unrelated to the Software, the license granted hereunder will not terminate
|
||||
under section (i) of this paragraph due to such counterclaim.
|
||||
|
||||
A "Necessary Claim" is a claim of a patent owned by Facebook that is
|
||||
necessarily infringed by the Software standing alone.
|
||||
|
||||
A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
|
||||
or contributory infringement or inducement to infringe any patent, including a
|
||||
cross-claim or counterclaim.
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Package structtag provides parsing of the defacto struct tag style.
|
||||
package structtag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var errInvalidTag = errors.New("invalid tag")
|
||||
|
||||
// Extract the quoted value for the given name returning it if it is found. The
|
||||
// found boolean helps differentiate between the "empty and found" vs "empty
|
||||
// and not found" nature of default empty strings.
|
||||
func Extract(name, tag string) (found bool, value string, err error) {
|
||||
for tag != "" {
|
||||
// skip leading space
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// scan to colon.
|
||||
// a space or a quote is a syntax error
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' {
|
||||
i++
|
||||
}
|
||||
if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
return false, "", errInvalidTag
|
||||
}
|
||||
foundName := string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// scan quoted string to find value
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
return false, "", errInvalidTag
|
||||
}
|
||||
qvalue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
|
||||
if foundName == name {
|
||||
value, err := strconv.Unquote(qvalue)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
return true, value, nil
|
||||
}
|
||||
}
|
||||
return false, "", nil
|
||||
}
|
||||
Reference in New Issue
Block a user