From 85169c799b4a0d4ba7d39347c07690dc0b49a1d1 Mon Sep 17 00:00:00 2001
From: Luke Shumaker <lukeshu@lukeshu.com>
Date: Wed, 7 Dec 2022 22:08:51 -0700
Subject: containers.Set: Don't use constraints.Ordered

---
 lib/containers/ordered.go |  4 +++-
 lib/containers/set.go     | 57 +++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 53 insertions(+), 8 deletions(-)

(limited to 'lib/containers')

diff --git a/lib/containers/ordered.go b/lib/containers/ordered.go
index 038edf8..d918149 100644
--- a/lib/containers/ordered.go
+++ b/lib/containers/ordered.go
@@ -8,10 +8,12 @@ import (
 	"golang.org/x/exp/constraints"
 )
 
-type Ordered[T interface{ Cmp(T) int }] interface {
+type _Ordered[T any] interface {
 	Cmp(T) int
 }
 
+type Ordered[T _Ordered[T]] _Ordered[T]
+
 type NativeOrdered[T constraints.Ordered] struct {
 	Val T
 }
diff --git a/lib/containers/set.go b/lib/containers/set.go
index 1c525ca..d3f8ca7 100644
--- a/lib/containers/set.go
+++ b/lib/containers/set.go
@@ -5,27 +5,70 @@
 package containers
 
 import (
+	"fmt"
 	"io"
+	"sort"
 
 	"git.lukeshu.com/go/lowmemjson"
-	"golang.org/x/exp/constraints"
 
 	"git.lukeshu.com/btrfs-progs-ng/lib/maps"
 )
 
 // Set[T] is an unordered set of T.
-//
-// Despite Set[T] being unordered, T is required to be an ordered type
-// in order that a Set[T] have a deterministic JSON representation.
-type Set[T constraints.Ordered] map[T]struct{}
+type Set[T comparable] map[T]struct{}
 
 var (
 	_ lowmemjson.Encodable = Set[int]{}
 	_ lowmemjson.Decodable = (*Set[int])(nil)
 )
 
+func cast[T any](x any) T { return x.(T) }
+
 func (o Set[T]) EncodeJSON(w io.Writer) error {
-	return lowmemjson.Encode(w, maps.SortedKeys(o))
+	var less func(a, b T) bool
+	var zero T
+	switch (any(zero)).(type) {
+	case _Ordered[T]:
+		less = func(a, b T) bool { return cast[_Ordered[T]](a).Cmp(b) < 0 }
+	// This is the constraints.Ordered list
+	case string:
+		less = func(a, b T) bool { return cast[string](a) < cast[string](b) }
+	case int:
+		less = func(a, b T) bool { return cast[int](a) < cast[int](b) }
+	case int8:
+		less = func(a, b T) bool { return cast[int8](a) < cast[int8](b) }
+	case int16:
+		less = func(a, b T) bool { return cast[int16](a) < cast[int16](b) }
+	case int32:
+		less = func(a, b T) bool { return cast[int32](a) < cast[int32](b) }
+	case int64:
+		less = func(a, b T) bool { return cast[int64](a) < cast[int64](b) }
+	case uint:
+		less = func(a, b T) bool { return cast[uint](a) < cast[uint](b) }
+	case uint8:
+		less = func(a, b T) bool { return cast[uint8](a) < cast[uint8](b) }
+	case uint16:
+		less = func(a, b T) bool { return cast[uint16](a) < cast[uint16](b) }
+	case uint32:
+		less = func(a, b T) bool { return cast[uint32](a) < cast[uint32](b) }
+	case uint64:
+		less = func(a, b T) bool { return cast[uint64](a) < cast[uint64](b) }
+	case uintptr:
+		less = func(a, b T) bool { return cast[uintptr](a) < cast[uintptr](b) }
+	case float32:
+		less = func(a, b T) bool { return cast[float32](a) < cast[float32](b) }
+	case float64:
+		less = func(a, b T) bool { return cast[float64](a) < cast[float64](b) }
+	default:
+		less = func(a, b T) bool { return fmt.Sprint(a) < fmt.Sprint(b) }
+	}
+
+	keys := maps.Keys(o)
+	sort.Slice(keys, func(i, j int) bool {
+		return less(keys[i], keys[j])
+	})
+
+	return lowmemjson.Encode(w, keys)
 }
 
 func (o *Set[T]) DecodeJSON(r io.RuneScanner) error {
@@ -49,7 +92,7 @@ func (o *Set[T]) DecodeJSON(r io.RuneScanner) error {
 	})
 }
 
-func NewSet[T constraints.Ordered](values ...T) Set[T] {
+func NewSet[T comparable](values ...T) Set[T] {
 	ret := make(Set[T], len(values))
 	for _, value := range values {
 		ret.Insert(value)
-- 
cgit v1.2.3-2-g168b