github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/identity/identity.go (about)

     1  // Copyright 2023 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package identity
    15  
    16  import (
    17  	"path/filepath"
    18  	"strings"
    19  	"sync"
    20  	"sync/atomic"
    21  )
    22  
    23  // NewManager creates a new Manager starting at id.
    24  func NewManager(id Provider) Manager {
    25  	return &identityManager{
    26  		Provider: id,
    27  		ids:      Identities{id.GetIdentity(): id},
    28  	}
    29  }
    30  
    31  // NewPathIdentity creates a new Identity with the two identifiers
    32  // type and path.
    33  func NewPathIdentity(typ, pat string) PathIdentity {
    34  	pat = strings.ToLower(strings.TrimPrefix(filepath.ToSlash(pat), "/"))
    35  	return PathIdentity{Type: typ, Path: pat}
    36  }
    37  
    38  // Identities stores identity providers.
    39  type Identities map[Identity]Provider
    40  
    41  func (ids Identities) search(depth int, id Identity) Provider {
    42  	if v, found := ids[id.GetIdentity()]; found {
    43  		return v
    44  	}
    45  
    46  	depth++
    47  
    48  	// There may be infinite recursion in templates.
    49  	if depth > 100 {
    50  		// Bail out.
    51  		return nil
    52  	}
    53  
    54  	for _, v := range ids {
    55  		switch t := v.(type) {
    56  		case IdentitiesProvider:
    57  			if nested := t.GetIdentities().search(depth, id); nested != nil {
    58  				return nested
    59  			}
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  // IdentitiesProvider provides all Identities.
    66  type IdentitiesProvider interface {
    67  	GetIdentities() Identities
    68  }
    69  
    70  // Identity represents an thing that can provide an identify. This can be
    71  // any Go type, but the Identity returned by GetIdentify must be hashable.
    72  type Identity interface {
    73  	Provider
    74  	Name() string
    75  }
    76  
    77  // Manager manages identities, and is itself a Provider of Identity.
    78  type Manager interface {
    79  	SearchProvider
    80  	Add(ids ...Provider)
    81  	Reset()
    82  }
    83  
    84  // SearchProvider provides access to the chained set of identities.
    85  type SearchProvider interface {
    86  	Provider
    87  	IdentitiesProvider
    88  	Search(id Identity) Provider
    89  }
    90  
    91  // A PathIdentity is a common identity identified by a type and a path, e.g. "layouts" and "_default/single.html".
    92  type PathIdentity struct {
    93  	Type string
    94  	Path string
    95  }
    96  
    97  // GetIdentity returns itself.
    98  func (id PathIdentity) GetIdentity() Identity {
    99  	return id
   100  }
   101  
   102  // Name returns the Path.
   103  func (id PathIdentity) Name() string {
   104  	return id.Path
   105  }
   106  
   107  // A KeyValueIdentity a general purpose identity.
   108  type KeyValueIdentity struct {
   109  	Key   string
   110  	Value string
   111  }
   112  
   113  // GetIdentity returns itself.
   114  func (id KeyValueIdentity) GetIdentity() Identity {
   115  	return id
   116  }
   117  
   118  // Name returns the Key.
   119  func (id KeyValueIdentity) Name() string {
   120  	return id.Key
   121  }
   122  
   123  // Provider provides the comparable Identity.
   124  type Provider interface {
   125  	// GetIdentity is for internal use.
   126  	GetIdentity() Identity
   127  }
   128  
   129  type identityManager struct {
   130  	sync.Mutex
   131  	Provider
   132  	ids Identities
   133  }
   134  
   135  func (im *identityManager) Add(ids ...Provider) {
   136  	im.Lock()
   137  	for _, id := range ids {
   138  		im.ids[id.GetIdentity()] = id
   139  	}
   140  	im.Unlock()
   141  }
   142  
   143  func (im *identityManager) Reset() {
   144  	im.Lock()
   145  	id := im.GetIdentity()
   146  	im.ids = Identities{id.GetIdentity(): id}
   147  	im.Unlock()
   148  }
   149  
   150  // TODO(bep) these identities are currently only read on server reloads
   151  // so there should be no concurrency issues, but that may change.
   152  func (im *identityManager) GetIdentities() Identities {
   153  	im.Lock()
   154  	defer im.Unlock()
   155  	return im.ids
   156  }
   157  
   158  func (im *identityManager) Search(id Identity) Provider {
   159  	im.Lock()
   160  	defer im.Unlock()
   161  	return im.ids.search(0, id.GetIdentity())
   162  }
   163  
   164  // Incrementer increments and returns the value.
   165  // Typically used for IDs.
   166  type Incrementer interface {
   167  	Incr() int
   168  }
   169  
   170  // IncrementByOne implements Incrementer adding 1 every time Incr is called.
   171  type IncrementByOne struct {
   172  	counter uint64
   173  }
   174  
   175  func (c *IncrementByOne) Incr() int {
   176  	return int(atomic.AddUint64(&c.counter, uint64(1)))
   177  }