|
1 |
| -// Copyright © 2016,2018 Pennock Tech, LLC. |
| 1 | +// Copyright © 2016,2018,2025 Pennock Tech, LLC. |
2 | 2 | // All rights reserved, except as granted under license.
|
3 | 3 | // Licensed per file LICENSE.txt
|
4 | 4 |
|
5 | 5 | package tabular // import "go.pennock.tech/tabular"
|
6 | 6 |
|
7 | 7 | import (
|
8 | 8 | "fmt"
|
| 9 | + "reflect" |
9 | 10 | "strings"
|
10 | 11 |
|
11 | 12 | "go.pennock.tech/tabular/length"
|
@@ -69,6 +70,12 @@ func (c *Cell) Update() {
|
69 | 70 | c.height = o.height
|
70 | 71 | c.empty = o.empty
|
71 | 72 | return
|
| 73 | + case *Cell: |
| 74 | + c.str = o.str |
| 75 | + c.width = o.width |
| 76 | + c.height = o.height |
| 77 | + c.empty = o.empty |
| 78 | + return |
72 | 79 |
|
73 | 80 | // After this point, MUST set .str
|
74 | 81 | case string:
|
@@ -189,3 +196,101 @@ func (c *Cell) Empty() bool {
|
189 | 196 | }
|
190 | 197 | return c.empty
|
191 | 198 | }
|
| 199 | + |
| 200 | +// LessThan returns true if the value of this cell is less than the value of |
| 201 | +// the other cell. The determination of "less" is euphemistically heuristic. |
| 202 | +func (c *Cell) LessThan(d *Cell) bool { |
| 203 | + var ( |
| 204 | + g, h, t *Cell |
| 205 | + u Cell |
| 206 | + ok bool |
| 207 | + cv, dv reflect.Value |
| 208 | + tt, sortIntType reflect.Type |
| 209 | + as string |
| 210 | + af float64 |
| 211 | + aOkay bool |
| 212 | + ) |
| 213 | + |
| 214 | + g, ok = c, true |
| 215 | + for ok { |
| 216 | + if t, ok = g.raw.(*Cell); ok { |
| 217 | + g = t |
| 218 | + } else if u, ok = g.raw.(Cell); ok { |
| 219 | + g = &u |
| 220 | + } |
| 221 | + } |
| 222 | + cv = reflect.ValueOf(g.raw) |
| 223 | + |
| 224 | + h, ok = d, true |
| 225 | + for ok { |
| 226 | + if t, ok = h.raw.(*Cell); ok { |
| 227 | + h = t |
| 228 | + } else if u, ok = h.raw.(Cell); ok { |
| 229 | + h = &u |
| 230 | + } |
| 231 | + } |
| 232 | + dv = reflect.ValueOf(h.raw) |
| 233 | + |
| 234 | + // Do not try to convert to uint, because positive floats convert and lose precision. |
| 235 | + // Similarly for int. |
| 236 | + // Leave _conversions_ for the float. But "can" is the underlying type. |
| 237 | + // We want to use SortInter as our _first_ choice, including when defined on types for which the underlying type is an int |
| 238 | + |
| 239 | + sortIntType = reflect.TypeOf((*SortInter)(nil)).Elem() |
| 240 | + |
| 241 | + if cv.Type().Implements(sortIntType) { |
| 242 | + if dv.Type().Implements(sortIntType) { |
| 243 | + return cv.Interface().(SortInter).SortInt64() < dv.Interface().(SortInter).SortInt64() |
| 244 | + } else if dv.CanInt() { |
| 245 | + return cv.Interface().(SortInter).SortInt64() < dv.Int() |
| 246 | + } |
| 247 | + } else if dv.Type().Implements(sortIntType) { |
| 248 | + if cv.CanInt() { |
| 249 | + return cv.Int() < dv.Interface().(SortInter).SortInt64() |
| 250 | + } |
| 251 | + } |
| 252 | + |
| 253 | + if cv.CanFloat() && dv.CanFloat() { |
| 254 | + return cv.Float() < dv.Float() |
| 255 | + } |
| 256 | + if cv.CanUint() && dv.CanUint() { |
| 257 | + return cv.Uint() < dv.Uint() |
| 258 | + } |
| 259 | + if cv.CanInt() { |
| 260 | + if dv.CanFloat() { |
| 261 | + return float64(cv.Int()) < dv.Float() |
| 262 | + } else if dv.CanInt() { |
| 263 | + return cv.Int() < dv.Int() |
| 264 | + } |
| 265 | + } else if cv.CanFloat() && dv.CanInt() { |
| 266 | + return cv.Float() < float64(dv.Int()) |
| 267 | + } |
| 268 | + |
| 269 | + tt = reflect.TypeOf(af) |
| 270 | + if cv.CanConvert(tt) && dv.CanConvert(tt) { |
| 271 | + return cv.Convert(tt).Float() < dv.Convert(tt).Float() |
| 272 | + } |
| 273 | + |
| 274 | + aOkay = false |
| 275 | + if x, ok := g.raw.(string); ok { |
| 276 | + as = x |
| 277 | + aOkay = true |
| 278 | + } else if x, ok := g.raw.(Stringer); ok { |
| 279 | + as = x.String() |
| 280 | + aOkay = true |
| 281 | + } else if x, ok := g.raw.(GoStringer); ok { |
| 282 | + as = x.GoString() |
| 283 | + aOkay = true |
| 284 | + } |
| 285 | + if aOkay { |
| 286 | + if x, ok := h.raw.(string); ok { |
| 287 | + return as < x |
| 288 | + } else if x, ok := h.raw.(Stringer); ok { |
| 289 | + return as < x.String() |
| 290 | + } else if x, ok := h.raw.(GoStringer); ok { |
| 291 | + return as < x.GoString() |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + return false |
| 296 | +} |
0 commit comments