mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
refactor(lyrics): share pointer helpers via gg.Clone and gg.If
Add a nil-safe generic gg.Clone[T](*T) *T to utils/gg alongside the existing V and If helpers, and route the lyrics package's bespoke pointer-copy helpers through it: ptrOrNil (model/lyrics.go) and cloneTTMLCue (model/lyrics_ttml.go) are removed in favor of gg.Clone. Also replaces the local positiveOrDefault helper with gg.If at its three TTML timing-param call sites. This removes duplicated single-purpose plumbing in favor of shared, generic utilities. Behavior is unchanged. Adds tests for gg.Clone covering copy, non-aliasing, and nil input.
This commit is contained in:
parent
60377c9485
commit
c66d92c724
@ -10,6 +10,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/utils/gg"
|
||||
"github.com/navidrome/navidrome/utils/str"
|
||||
)
|
||||
|
||||
@ -542,7 +543,7 @@ func NormalizeCueEnds(cues []Cue, fallbackEnd *int64) []Cue {
|
||||
if end != nil && out[i].Start != nil && *end < *out[i].Start {
|
||||
end = out[i].Start
|
||||
}
|
||||
out[i].End = ptrOrNil(end)
|
||||
out[i].End = gg.Clone(end)
|
||||
}
|
||||
|
||||
for i := range out {
|
||||
@ -555,13 +556,3 @@ func NormalizeCueEnds(cues []Cue, fallbackEnd *int64) []Cue {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ptrOrNil returns a fresh copy of *v so callers never alias another cue's
|
||||
// Start/End pointer into the result.
|
||||
func ptrOrNil(v *int64) *int64 {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
c := *v
|
||||
return &c
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
"github.com/navidrome/navidrome/utils/gg"
|
||||
"github.com/navidrome/navidrome/utils/str"
|
||||
)
|
||||
|
||||
@ -487,7 +488,7 @@ func splitTTMLPiecesByNewline(pieces []ttmlPiece) [][]ttmlPiece {
|
||||
if start < i {
|
||||
lines[len(lines)-1] = append(lines[len(lines)-1], ttmlPiece{
|
||||
raw: raw[start:i],
|
||||
cue: cloneTTMLCue(piece.cue),
|
||||
cue: gg.Clone(piece.cue),
|
||||
})
|
||||
}
|
||||
lines = append(lines, []ttmlPiece{})
|
||||
@ -496,7 +497,7 @@ func splitTTMLPiecesByNewline(pieces []ttmlPiece) [][]ttmlPiece {
|
||||
if start < len(raw) {
|
||||
lines[len(lines)-1] = append(lines[len(lines)-1], ttmlPiece{
|
||||
raw: raw[start:],
|
||||
cue: cloneTTMLCue(piece.cue),
|
||||
cue: gg.Clone(piece.cue),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -562,15 +563,6 @@ func ttmlPiecesContainCue(pieces []ttmlPiece) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func cloneTTMLCue(cue *Cue) *Cue {
|
||||
if cue == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := *cue
|
||||
return &cloned
|
||||
}
|
||||
|
||||
func (p *ttmlParser) toLyricList() LyricList {
|
||||
res := make(LyricList, 0, len(p.mainLangOrder)+len(p.translationLangOrder)+len(p.pronunciationLangOrder))
|
||||
for _, lang := range p.mainLangOrder {
|
||||
@ -979,9 +971,9 @@ func (p *ttmlParser) updateTimingParams(attrs []xml.Attr) {
|
||||
}
|
||||
}
|
||||
|
||||
p.params.frameRate = positiveOrDefault(frameRate, defaultTTMLFrameRate)
|
||||
p.params.subFrameRate = positiveOrDefault(subFrameRate, defaultTTMLSubFrameRate)
|
||||
p.params.tickRate = positiveOrDefault(tickRate, defaultTTMLTickRate)
|
||||
p.params.frameRate = gg.If(frameRate > 0, frameRate, defaultTTMLFrameRate)
|
||||
p.params.subFrameRate = gg.If(subFrameRate > 0, subFrameRate, defaultTTMLSubFrameRate)
|
||||
p.params.tickRate = gg.If(tickRate > 0, tickRate, defaultTTMLTickRate)
|
||||
}
|
||||
|
||||
func parseTTMLDurationExpression(expr string, params ttmlTimingParams) (int64, bool) {
|
||||
@ -1262,10 +1254,3 @@ func linesAreSynced(lines []Line) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func positiveOrDefault(v float64, fallback float64) float64 {
|
||||
if v <= 0 {
|
||||
return fallback
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
@ -16,3 +16,13 @@ func If[T any](cond bool, v1, v2 T) T {
|
||||
}
|
||||
return v2
|
||||
}
|
||||
|
||||
// Clone returns a pointer to a fresh copy of *p, or nil if p is nil. Use it to
|
||||
// avoid aliasing the pointed-to value when a separate *T is needed.
|
||||
func Clone[T any](p *T) *T {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
v := *p
|
||||
return &v
|
||||
}
|
||||
|
||||
@ -46,4 +46,25 @@ var _ = Describe("GG", func() {
|
||||
Expect(gg.If(false, 1.1, 2.2)).To(Equal(2.2))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Clone", func() {
|
||||
It("returns a pointer to a copy of the value", func() {
|
||||
original := 123
|
||||
cloned := gg.Clone(&original)
|
||||
Expect(cloned).To(HaveValue(Equal(123)))
|
||||
Expect(cloned).NotTo(BeIdenticalTo(&original))
|
||||
})
|
||||
|
||||
It("does not alias the original value", func() {
|
||||
original := 123
|
||||
cloned := gg.Clone(&original)
|
||||
original = 456
|
||||
Expect(*cloned).To(Equal(123))
|
||||
})
|
||||
|
||||
It("returns nil when the input is nil", func() {
|
||||
var v *int
|
||||
Expect(gg.Clone(v)).To(BeNil())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user