github.com/MontFerret/ferret@v0.18.0/pkg/drivers/cookies.go (about)

     1  package drivers
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"hash/fnv"
     7  	"sort"
     8  
     9  	"github.com/MontFerret/ferret/pkg/runtime/core"
    10  	"github.com/MontFerret/ferret/pkg/runtime/values"
    11  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    12  
    13  	"github.com/wI2L/jettison"
    14  )
    15  
    16  type HTTPCookies struct {
    17  	values map[string]HTTPCookie
    18  }
    19  
    20  func NewHTTPCookies() *HTTPCookies {
    21  	return NewHTTPCookiesWith(make(map[string]HTTPCookie))
    22  }
    23  
    24  func NewHTTPCookiesWith(values map[string]HTTPCookie) *HTTPCookies {
    25  	return &HTTPCookies{values}
    26  }
    27  
    28  func (c *HTTPCookies) MarshalJSON() ([]byte, error) {
    29  	return jettison.MarshalOpts(c.values, jettison.NoHTMLEscaping())
    30  }
    31  
    32  func (c *HTTPCookies) Type() core.Type {
    33  	return HTTPCookiesType
    34  }
    35  
    36  func (c *HTTPCookies) String() string {
    37  	j, err := c.MarshalJSON()
    38  
    39  	if err != nil {
    40  		return "{}"
    41  	}
    42  
    43  	return string(j)
    44  }
    45  
    46  func (c *HTTPCookies) Compare(other core.Value) int64 {
    47  	if other.Type() != HTTPCookiesType {
    48  		return Compare(HTTPCookiesType, other.Type())
    49  	}
    50  
    51  	oc := other.(*HTTPCookies)
    52  
    53  	switch {
    54  	case len(c.values) > len(oc.values):
    55  		return 1
    56  	case len(c.values) < len(oc.values):
    57  		return -1
    58  	}
    59  
    60  	for name := range c.values {
    61  		cEl, cExists := c.Get(values.NewString(name))
    62  
    63  		if !cExists {
    64  			return -1
    65  		}
    66  
    67  		ocEl, ocExists := oc.Get(values.NewString(name))
    68  
    69  		if !ocExists {
    70  			return 1
    71  		}
    72  
    73  		c := cEl.Compare(ocEl)
    74  
    75  		if c != 0 {
    76  			return c
    77  		}
    78  	}
    79  
    80  	return 0
    81  }
    82  
    83  func (c *HTTPCookies) Unwrap() interface{} {
    84  	return c.values
    85  }
    86  
    87  func (c *HTTPCookies) Hash() uint64 {
    88  	hash := fnv.New64a()
    89  
    90  	hash.Write([]byte(c.Type().String()))
    91  	hash.Write([]byte(":"))
    92  	hash.Write([]byte("{"))
    93  
    94  	keys := make([]string, 0, len(c.values))
    95  
    96  	for key := range c.values {
    97  		keys = append(keys, key)
    98  	}
    99  
   100  	// order does not really matter
   101  	// but it will give us a consistent hash sum
   102  	sort.Strings(keys)
   103  	endIndex := len(keys) - 1
   104  
   105  	for idx, key := range keys {
   106  		hash.Write([]byte(key))
   107  		hash.Write([]byte(":"))
   108  
   109  		el := c.values[key]
   110  
   111  		bytes := make([]byte, 8)
   112  		binary.LittleEndian.PutUint64(bytes, el.Hash())
   113  
   114  		hash.Write(bytes)
   115  
   116  		if idx != endIndex {
   117  			hash.Write([]byte(","))
   118  		}
   119  	}
   120  
   121  	hash.Write([]byte("}"))
   122  
   123  	return hash.Sum64()
   124  }
   125  
   126  func (c *HTTPCookies) Copy() core.Value {
   127  	return NewHTTPCookiesWith(c.values)
   128  }
   129  
   130  func (c *HTTPCookies) Clone() core.Cloneable {
   131  	clone := make(map[string]HTTPCookie)
   132  
   133  	for _, cookie := range c.values {
   134  		clone[cookie.Name] = cookie
   135  	}
   136  
   137  	return NewHTTPCookiesWith(clone)
   138  }
   139  
   140  func (c *HTTPCookies) Length() values.Int {
   141  	return values.NewInt(len(c.values))
   142  }
   143  
   144  func (c *HTTPCookies) Keys() []values.String {
   145  	result := make([]values.String, 0, len(c.values))
   146  
   147  	for k := range c.values {
   148  		result = append(result, values.NewString(k))
   149  	}
   150  
   151  	return result
   152  }
   153  
   154  func (c *HTTPCookies) Values() []HTTPCookie {
   155  	result := make([]HTTPCookie, 0, len(c.values))
   156  
   157  	for _, v := range c.values {
   158  		result = append(result, v)
   159  	}
   160  
   161  	return result
   162  }
   163  
   164  func (c *HTTPCookies) Get(key values.String) (HTTPCookie, values.Boolean) {
   165  	value, found := c.values[key.String()]
   166  
   167  	if found {
   168  		return value, values.True
   169  	}
   170  
   171  	return HTTPCookie{}, values.False
   172  }
   173  
   174  func (c *HTTPCookies) Set(cookie HTTPCookie) {
   175  	c.values[cookie.Name] = cookie
   176  }
   177  
   178  func (c *HTTPCookies) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
   179  	if len(path) == 0 {
   180  		return values.None, nil
   181  	}
   182  
   183  	segmentIdx := 0
   184  	segment := path[segmentIdx]
   185  
   186  	err := core.ValidateType(segment, types.String)
   187  
   188  	if err != nil {
   189  		return values.None, core.NewPathError(err, segmentIdx)
   190  	}
   191  
   192  	cookie, found := c.values[segment.String()]
   193  
   194  	if found {
   195  		if len(path) == 1 {
   196  			return cookie, nil
   197  		}
   198  
   199  		return values.GetIn(ctx, cookie, path[segmentIdx+1:])
   200  	}
   201  
   202  	return values.None, nil
   203  }
   204  
   205  func (c *HTTPCookies) ForEach(predicate func(value HTTPCookie, key values.String) bool) {
   206  	for key, val := range c.values {
   207  		if !predicate(val, values.NewString(key)) {
   208  			break
   209  		}
   210  	}
   211  }