package adapter import ( "context" "database/sql" "errors" "fmt" "gitea.dwysokinski.me/twhelp/core/internal/bun/bunmodel" "gitea.dwysokinski.me/twhelp/core/internal/domain" "github.com/uptrace/bun" ) type VersionBunRepository struct { db bun.IDB } func NewVersionBunRepository(db bun.IDB) *VersionBunRepository { return &VersionBunRepository{db: db} } func (repo *VersionBunRepository) List( ctx context.Context, params domain.ListVersionsParams, ) (domain.ListVersionsResult, error) { var versions bunmodel.Versions if err := repo.db.NewSelect(). Model(&versions). Apply(listVersionsParamsApplier{params: params}.apply). Scan(ctx); err != nil && !errors.Is(err, sql.ErrNoRows) { return domain.ListVersionsResult{}, fmt.Errorf("couldn't select versions from the database: %w", err) } converted, err := versions.ToDomain() if err != nil { return domain.ListVersionsResult{}, err } return domain.NewListVersionsResult(separateListResultAndNext(converted, params.Limit())) } type listVersionsParamsApplier struct { params domain.ListVersionsParams } func (a listVersionsParamsApplier) apply(q *bun.SelectQuery) *bun.SelectQuery { if codes := a.params.Codes(); len(codes) > 0 { q = q.Where("version.code IN (?)", bun.In(codes)) } for _, s := range a.params.Sort() { column, dir, err := a.sortToColumnAndDirection(s) if err != nil { return q.Err(err) } q.OrderExpr("? ?", column, dir.Bun()) } return q.Limit(a.params.Limit() + 1).Apply(a.applyCursor) } func (a listVersionsParamsApplier) applyCursor(q *bun.SelectQuery) *bun.SelectQuery { cursor := a.params.Cursor() if cursor.IsZero() { return q } sort := a.params.Sort() cursorApplier := cursorPaginationApplier{ data: make([]cursorPaginationApplierDataElement, 0, len(sort)), } for _, s := range sort { var err error var el cursorPaginationApplierDataElement el.column, el.direction, err = a.sortToColumnAndDirection(s) if err != nil { return q.Err(err) } switch s { case domain.VersionSortCodeASC, domain.VersionSortCodeDESC: el.value = cursor.Code() el.unique = true default: return q.Err(fmt.Errorf("%s: %w", s.String(), errInvalidSortValue)) } cursorApplier.data = append(cursorApplier.data, el) } return q.Apply(cursorApplier.apply) } func (a listVersionsParamsApplier) sortToColumnAndDirection( s domain.VersionSort, ) (bun.Safe, sortDirection, error) { switch s { case domain.VersionSortCodeASC: return "version.code", sortDirectionASC, nil case domain.VersionSortCodeDESC: return "version.code", sortDirectionDESC, nil default: return "", 0, fmt.Errorf("%s: %w", s.String(), errInvalidSortValue) } }