Files
bookhoard/internal/utils/mediaurl.go
T
John O'Keefe 188ae076ab refactor(utils): one PgTextString for the four pgtype.Text unwrappers
handlers textToString, router textToString, router getText, and utils
normText were four copies of the same NULL-to-empty unwrap (99 call
sites). One exported utils.PgTextString replaces them all; the private
normText (comparison normalizer) stays but is now the odd one out.

Harness fix found on the way: setupTestServer built a full wiring
literal for route registration and then RETURNED a second, sparser
literal that never copied AnnotationService — setup.AnnotationService
was always nil on the struct every test receives. Nobody noticed until
cross_library_move_test became the first caller (it previously used
the now-retired plain CreateMediaHighlight query); it now exercises
the real SaveHighlight path through the wired service.
2026-10-17 16:36:58 -04:00

46 lines
1.2 KiB
Go

// Package utils provides helper functions for URL resolution, ISBN handling, and tag management.
package utils
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
)
// ResolveMediaURL resolves a relative path (cover or file) to a full URL
func ResolveMediaURL(libraryID pgtype.UUID, relativePath pgtype.Text) string {
if !relativePath.Valid || relativePath.String == "" {
return ""
}
if strings.HasPrefix(relativePath.String, "/uploads/") {
return relativePath.String
}
if filepath.IsAbs(relativePath.String) {
return relativePath.String
}
libraryIDStr := uuid.UUID(libraryID.Bytes).String()
segments := strings.Split(relativePath.String, "/")
for i, seg := range segments {
segments[i] = url.PathEscape(seg)
}
encodedPath := strings.Join(segments, "/")
return fmt.Sprintf("/uploads/library-%s/%s", libraryIDStr, encodedPath)
}
// PgTextString unwraps a nullable text column to a plain string (""
// when NULL). The former textToString/getText/normText copies in
// handlers, router, and utils all did exactly this.
func PgTextString(t pgtype.Text) string {
if t.Valid {
return t.String
}
return ""
}