github.com/mitranim/gg@v0.1.17/misc.go (about)

     1  package gg
     2  
     3  import (
     4  	"context"
     5  	"database/sql/driver"
     6  )
     7  
     8  var (
     9  	Indent  = `    `
    10  	Space   = ` `
    11  	Newline = "\n"
    12  )
    13  
    14  // Does nothing.
    15  func Nop() {}
    16  
    17  // Does nothing.
    18  func Nop1[A any](A) {}
    19  
    20  // Does nothing.
    21  func Nop2[A, B any](A, B) {}
    22  
    23  // Does nothing.
    24  func Nop3[A, B, C any](A, B, C) {}
    25  
    26  // Identity function. Returns input as-is.
    27  func Id1[A any](val A) A { return val }
    28  
    29  // Identity function. Returns input as-is.
    30  func Id2[A, B any](val0 A, val1 B) (A, B) { return val0, val1 }
    31  
    32  // Identity function. Returns input as-is.
    33  func Id3[A, B, C any](val0 A, val1 B, val2 C) (A, B, C) { return val0, val1, val2 }
    34  
    35  // Returns a zero value of the given type.
    36  func Zero[A any]() (_ A) { return }
    37  
    38  /*
    39  Same as Go's `+` operator, expressed as a generic function. Input type may be
    40  numeric or ~string. When the input type is numeric, this is unchecked and may
    41  overflow. For integers, prefer `Add` whenever possible, which has overflow
    42  checks.
    43  */
    44  func Plus2[A Plusable](one, two A) A { return one + two }
    45  
    46  /*
    47  Variadic version of Go's `+` operator. Input type may be numeric or ~string.
    48  If the input is empty, returns a zero value. Use caution: this has no overflow
    49  checks for numbers. Prefer `Add` for integers.
    50  */
    51  func Plus[A Plusable](val ...A) A { return Foldz(val, Plus2[A]) }
    52  
    53  /*
    54  Shortcut for implementing `driver.Valuer` on `Nullable` types that wrap other
    55  types, such as `Opt`. Mostly for internal use.
    56  */
    57  func ValueNull[A any, B NullableValGetter[A]](src B) (driver.Value, error) {
    58  	if src.IsNull() {
    59  		return nil, nil
    60  	}
    61  
    62  	val := src.Get()
    63  
    64  	impl, _ := AnyNoEscUnsafe(val).(driver.Valuer)
    65  	if impl != nil {
    66  		return impl.Value()
    67  	}
    68  
    69  	return val, nil
    70  }
    71  
    72  /*
    73  Returns true if the given `any` can be usefully converted into a value of the
    74  given type. If the result is true, `src.(A)` doesn't panic. If the output is
    75  false, `src.(A)` panics.
    76  */
    77  func AnyIs[A any](src any) bool {
    78  	_, ok := AnyNoEscUnsafe(src).(A)
    79  	return ok
    80  }
    81  
    82  /*
    83  Non-asserting interface conversion. Converts the given `any` into the given
    84  type, returning zero value on failure.
    85  */
    86  func AnyAs[A any](src any) A {
    87  	val, _ := AnyNoEscUnsafe(src).(A)
    88  	return val
    89  }
    90  
    91  /*
    92  Converts the argument to `any` and returns it. Sometimes useful in higher-order
    93  functions.
    94  */
    95  func ToAny[A any](val A) any { return val }
    96  
    97  /*
    98  Uses `context.WithValue` to create a context with the given value, using the
    99  type's nil pointer "(*A)(nil)" as the key.
   100  */
   101  func CtxSet[A any](ctx context.Context, val A) context.Context {
   102  	return context.WithValue(ctx, (*A)(nil), val)
   103  }
   104  
   105  /*
   106  Uses `ctx.Value` to get the value of the given type, using the type's nil pointer
   107  "(*A)(nil)" as the key. If the context is nil or doesn't contain the value,
   108  returns zero value and false.
   109  */
   110  func CtxGot[A any](ctx context.Context) (A, bool) {
   111  	if ctx == nil {
   112  		return Zero[A](), false
   113  	}
   114  	val, ok := ctx.Value((*A)(nil)).(A)
   115  	return val, ok
   116  }
   117  
   118  // Same as `CtxGot` but returns only the boolean.
   119  func CtxHas[A any](ctx context.Context) bool {
   120  	_, ok := CtxGot[A](ctx)
   121  	return ok
   122  }
   123  
   124  /*
   125  Same as `CtxGot` but returns only the resulting value. If value was not found,
   126  output is zero.
   127  */
   128  func CtxGet[A any](ctx context.Context) A {
   129  	val, _ := CtxGot[A](ctx)
   130  	return val
   131  }
   132  
   133  /*
   134  Short for "iterator". Returns a slice of the given length that can be iterated
   135  by using a `range` loop. Usage:
   136  
   137  	for range Iter(size) { ... }
   138  	for ind := range Iter(size) { ... }
   139  
   140  Because `struct{}` is zero-sized, `[]struct{}` is backed by "zerobase" (see Go
   141  source → "runtime/malloc.go") and does not allocate. Loops using this should
   142  compile to approximately the same instructions as "normal" counted loops.
   143  */
   144  func Iter(size int) []struct{} { return make([]struct{}, size) }
   145  
   146  /*
   147  Returns a slice of numbers from `min` to `max`. The range is inclusive at the
   148  start but exclusive at the end: `[min,max)`. If `!(max > min)`, returns nil.
   149  Values must be within the range of the Go type `int`.
   150  */
   151  func Range[A Int](min, max A) []A {
   152  	// We must check this before calling `max-1` to avoid underflow.
   153  	if !(max > min) {
   154  		return nil
   155  	}
   156  	return RangeIncl(min, max-1)
   157  }
   158  
   159  /*
   160  Returns a slice of numbers from `min` to `max`. The range is inclusive at the
   161  start and at the end: `[min,max]`. If `!(max >= min)`, returns nil. Values must
   162  be within the range of the Go type `int`.
   163  
   164  While the exclusive range `[min,max)` implemented by `Range` is more
   165  traditional, this function allows to create a range that includes the maximum
   166  representable value of any given integer type, such as 255 for `uint8`, which
   167  cannot be done with `Range`.
   168  */
   169  func RangeIncl[A Int](min, max A) []A {
   170  	if !(max >= min) {
   171  		return nil
   172  	}
   173  
   174  	minInt := NumConv[int](min)
   175  	maxInt := NumConv[int](max)
   176  	buf := make([]A, (maxInt-minInt)+1)
   177  	for ind := range buf {
   178  		buf[ind] = A(ind + minInt)
   179  	}
   180  	return buf
   181  }
   182  
   183  // Shortcut for creating range `[0,N)`, exclusive at the end.
   184  func Span[A Int](val A) []A { return Range(0, val) }
   185  
   186  /*
   187  Takes a pointer and a fallback value which must be non-zero. If the pointer
   188  destination is zero, sets the fallback and returns true. Otherwise returns
   189  false.
   190  */
   191  func Fellback[A any](tar *A, fallback A) bool {
   192  	if IsZero(fallback) {
   193  		panic(Errf(`invalid non-zero fallback %#v`, fallback))
   194  	}
   195  
   196  	if tar == nil {
   197  		return false
   198  	}
   199  
   200  	if IsZero(*tar) {
   201  		*tar = fallback
   202  		return true
   203  	}
   204  	return false
   205  }
   206  
   207  /*
   208  Snapshots the current value at the given pointer and returns a snapshot
   209  that can restore this value. Usage:
   210  
   211  	defer Snap(&somePtr).Done()
   212  	somePtr.SomeField = someValue
   213  */
   214  func Snap[A any](ptr *A) Snapshot[A] { return Snapshot[A]{ptr, *ptr} }
   215  
   216  /*
   217  Snapshots the previous value, sets the next value, and returns a snapshot
   218  that can restore the previous value. Usage:
   219  
   220  	defer SnapSwap(&somePtr, someVal).Done()
   221  */
   222  func SnapSwap[A any](ptr *A, next A) Snapshot[A] {
   223  	prev := *ptr
   224  	*ptr = next
   225  	return Snapshot[A]{ptr, prev}
   226  }
   227  
   228  // Short for "snapshot". Used by `SnapSwap`.
   229  type Snapshot[A any] struct {
   230  	Ptr *A
   231  	Val A
   232  }
   233  
   234  // If the pointer is non-nil, writes the value to it. See `SnapSwap`.
   235  func (self Snapshot[_]) Done() {
   236  	if self.Ptr != nil {
   237  		*self.Ptr = self.Val
   238  	}
   239  }
   240  
   241  /*
   242  Snapshots the length of the given slice and returns a snapshot that can restore
   243  the previous length. Usage:
   244  
   245  	defer SnapSlice(&somePtr).Done()
   246  */
   247  func SnapSlice[Slice ~[]Elem, Elem any](ptr *Slice) SliceSnapshot[Slice, Elem] {
   248  	return SliceSnapshot[Slice, Elem]{ptr, PtrLen(ptr)}
   249  }
   250  
   251  /*
   252  Analogous to `Snapshot`, but instead of storing a value, stores a length.
   253  When done, reverts the referenced slice to the given length.
   254  */
   255  type SliceSnapshot[Slice ~[]Elem, Elem any] struct {
   256  	Ptr *Slice
   257  	Len int
   258  }
   259  
   260  /*
   261  Analogous to `Snapshot.Done`. Reverts the referenced slice to `self.Len` while
   262  keeping the capacity.
   263  */
   264  func (self SliceSnapshot[_, _]) Done() {
   265  	if self.Ptr != nil {
   266  		*self.Ptr = (*self.Ptr)[:self.Len]
   267  	}
   268  }
   269  
   270  // Shortcut for making a pseudo-tuple with two elements.
   271  func Tuple2[A, B any](valA A, valB B) Tup2[A, B] {
   272  	return Tup2[A, B]{valA, valB}
   273  }
   274  
   275  // Represents a pseudo-tuple with two elements.
   276  type Tup2[A, B any] struct {
   277  	A A
   278  	B B
   279  }
   280  
   281  // Converts the pseudo-tuple to a proper Go tuple.
   282  func (self Tup2[A, B]) Get() (A, B) { return self.A, self.B }
   283  
   284  // Shortcut for making a pseudo-tuple with three elements.
   285  func Tuple3[A, B, C any](valA A, valB B, valC C) Tup3[A, B, C] {
   286  	return Tup3[A, B, C]{valA, valB, valC}
   287  }
   288  
   289  // Represents a pseudo-tuple with three elements.
   290  type Tup3[A, B, C any] struct {
   291  	A A
   292  	B B
   293  	C C
   294  }
   295  
   296  // Converts the pseudo-tuple to a proper Go tuple.
   297  func (self Tup3[A, B, C]) Get() (A, B, C) { return self.A, self.B, self.C }
   298  
   299  /*
   300  Makes a zero value of the given type, passes it to the given mutator functions
   301  by pointer, and returns the modified value. Nil functions are ignored.
   302  */
   303  func With[A any](funs ...func(*A)) (out A) {
   304  	for _, fun := range funs {
   305  		if fun != nil {
   306  			fun(&out)
   307  		}
   308  	}
   309  	return
   310  }