ライブラリを番組一覧・番組ページ・再生の3段にする(ステップ12)

This commit is contained in:
2026-10-10 12:18:58 +09:00
parent 511365f0d4
commit 6586d7edd2
12 changed files with 766 additions and 28 deletions
+101
View File
@@ -0,0 +1,101 @@
package web
import (
"sort"
"strings"
"gitea.kazu634.com/kazu634/tver/internal/store"
)
func sortJobsNaturally(jobs []store.Job) {
sort.SliceStable(jobs, func(i, j int) bool {
if comparison := compareNaturalTitle(jobs[i].Title, jobs[j].Title); comparison != 0 {
return comparison < 0
}
left, right := jobs[i].FinishedAt, jobs[j].FinishedAt
if left == nil || right == nil {
if left != right {
return left == nil
}
} else if !left.Equal(*right) {
return left.Before(*right)
}
return jobs[i].ID < jobs[j].ID
})
}
func compareNaturalTitle(left, right string) int {
a, b := []rune(left), []rune(right)
ai, bi := 0, 0
for ai < len(a) && bi < len(b) {
_, aIsDigit := decimalDigit(a[ai])
_, bIsDigit := decimalDigit(b[bi])
if aIsDigit && bIsDigit {
var an, bn strings.Builder
for ai < len(a) {
digit, ok := decimalDigit(a[ai])
if !ok {
break
}
an.WriteByte(byte('0' + digit))
ai++
}
for bi < len(b) {
digit, ok := decimalDigit(b[bi])
if !ok {
break
}
bn.WriteByte(byte('0' + digit))
bi++
}
if comparison := compareDecimal(an.String(), bn.String()); comparison != 0 {
return comparison
}
continue
}
if a[ai] < b[bi] {
return -1
}
if a[ai] > b[bi] {
return 1
}
ai++
bi++
}
if ai == len(a) && bi < len(b) {
return -1
}
if bi == len(b) && ai < len(a) {
return 1
}
return 0
}
func decimalDigit(r rune) (int, bool) {
switch {
case '0' <= r && r <= '9':
return int(r - '0'), true
case '0' <= r && r <= '9':
return int(r - '0'), true
default:
return 0, false
}
}
func compareDecimal(left, right string) int {
left = strings.TrimLeft(left, "0")
right = strings.TrimLeft(right, "0")
if left == "" {
left = "0"
}
if right == "" {
right = "0"
}
if len(left) < len(right) {
return -1
}
if len(left) > len(right) {
return 1
}
return strings.Compare(left, right)
}