refactor: replace Value() from Option[T] with Get() & ValueOrZeroValue() (#11218)

`Option[T]` currently exposes a method `Value()` which is permitted to be called on an option that has a value, and an option that doesn't have a value.  This API is awkward because the behaviour if the option doesn't have a value isn't clear to the caller, and, because almost all accesses end up being `.Has()?` then `OK, use .Value()`.

`Get() (bool, T)` is added as a better replacement, which both returns whether the option has a value, and the value if present.  Most call-sites are rewritten to this form.

`ValueOrZeroValue()` is a direct replacement that has the same behaviour that `Value()` had, but describes the behaviour if the value is missing.

In addition to the current API being awkward, the core reason for this change is that `Value()` conflicts with the `Value()` function from the `driver.Valuer` interface.  If this interface was implemented, it would allow `Option[T]` to be used to represent a nullable field in an xorm bean struct (requires: https://code.forgejo.org/xorm/xorm/pulls/66).

_Note:_ changes are extensive in this PR, but are almost all changes are easy, mechanical transitions from `.Has()` to `.Get()`.  All of this work was performed by hand.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [ ] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
- [x] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

*The decision if the pull request will be shown in the release notes is up to the mergers / release team.*

The content of the `release-notes/<pull request number>.md` file will serve as the basis for the release notes. If the file does not exist, the title of the pull request will be used instead.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11218
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
This commit is contained in:
Mathieu Fenniak 2026-02-10 16:41:21 +01:00 committed by Mathieu Fenniak
parent a53371b103
commit 51d0188533
47 changed files with 362 additions and 384 deletions

View file

@ -78,10 +78,10 @@ func (opts FindRunJobOptions) ToConds() builder.Cond {
if opts.UpdatedBefore > 0 {
cond = cond.And(builder.Lt{"updated": opts.UpdatedBefore})
}
if opts.RunNeedsApproval.Has() {
if has, value := opts.RunNeedsApproval.Get(); has {
cond = cond.And(builder.Exists(builder.Select("id").From("action_run", "outer_run").
Where(builder.Eq{
"outer_run.need_approval": opts.RunNeedsApproval.Value(),
"outer_run.need_approval": value,
"outer_run.id": builder.Expr("run_id"),
})))
}

View file

@ -212,8 +212,8 @@ func (opts FindRunnerOptions) ToConds() builder.Cond {
cond = cond.And(builder.Like{"name", opts.Filter})
}
if opts.IsOnline.Has() {
if opts.IsOnline.Value() {
if has, value := opts.IsOnline.Get(); has {
if value {
cond = cond.And(builder.Gt{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})
} else {
cond = cond.And(builder.Lte{"last_online": time.Now().Add(-RunnerOfflineTime).Unix()})

View file

@ -82,11 +82,11 @@ func (opts FindTaskOptions) ToConds() builder.Cond {
if opts.RunnerID > 0 {
cond = cond.And(builder.Eq{"runner_id": opts.RunnerID})
}
if opts.LogExpired.Has() {
cond = cond.And(builder.Eq{"log_expired": opts.LogExpired.Value()})
if has, value := opts.LogExpired.Get(); has {
cond = cond.And(builder.Eq{"log_expired": value})
}
if opts.LogInStorage.Has() {
cond = cond.And(builder.Eq{"log_in_storage": opts.LogInStorage.Value()})
if has, value := opts.LogInStorage.Get(); has {
cond = cond.And(builder.Eq{"log_in_storage": value})
}
return cond
}

View file

@ -250,8 +250,8 @@ type FindSourcesOptions struct {
func (opts FindSourcesOptions) ToConds() builder.Cond {
conds := builder.NewCond()
if opts.IsActive.Has() {
conds = conds.And(builder.Eq{"is_active": opts.IsActive.Value()})
if has, value := opts.IsActive.Get(); has {
conds = conds.And(builder.Eq{"is_active": value})
}
if opts.LoginType != NoType {
conds = conds.And(builder.Eq{"`type`": opts.LoginType})

View file

@ -77,8 +77,8 @@ func (opts FindBranchOptions) ToConds() builder.Cond {
if len(opts.ExcludeBranchNames) > 0 {
cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames))
}
if opts.IsDeletedBranch.Has() {
cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.Value()})
if has, value := opts.IsDeletedBranch.Get(); has {
cond = cond.And(builder.Eq{"is_deleted": value})
}
if opts.Keyword != "" {
cond = cond.And(builder.Like{"name", opts.Keyword})

View file

@ -1102,11 +1102,11 @@ func (opts FindCommentsOptions) ToConds() builder.Cond {
if len(opts.TreePath) > 0 {
cond = cond.And(builder.Eq{"comment.tree_path": opts.TreePath})
}
if opts.Invalidated.Has() {
cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.Value()})
if has, value := opts.Invalidated.Get(); has {
cond = cond.And(builder.Eq{"comment.invalidated": value})
}
if opts.IsPull.Has() {
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.Value()})
if has, value := opts.IsPull.Get(); has {
cond = cond.And(builder.Eq{"issue.is_pull": value})
}
return cond
}

View file

@ -226,8 +226,8 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
applyRepoConditions(sess, opts)
if opts.IsClosed.Has() {
sess.And("issue.is_closed=?", opts.IsClosed.Value())
if has, value := opts.IsClosed.Get(); has {
sess.And("issue.is_closed=?", value)
}
if opts.AssigneeID > 0 {
@ -269,18 +269,18 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) {
applyProjectColumnCondition(sess, opts)
if opts.IsPull.Has() {
sess.And("issue.is_pull=?", opts.IsPull.Value())
if has, value := opts.IsPull.Get(); has {
sess.And("issue.is_pull=?", value)
}
if opts.IsArchived.Has() {
sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.Value()})
if has, value := opts.IsArchived.Get(); has {
sess.And(builder.Eq{"repository.is_archived": value})
}
applyLabelsCondition(sess, opts)
if opts.User != nil {
cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value())
cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.ValueOrZeroValue())
// If AllPublic was set, then also consider all issues in public
// repositories in addition to the private repositories the user has access
// to.

View file

@ -180,8 +180,8 @@ func applyIssuesOptions(sess *xorm.Session, opts *IssuesOptions, issueIDs []int6
applyReviewedCondition(sess, opts.ReviewedID)
}
if opts.IsPull.Has() {
sess.And("issue.is_pull=?", opts.IsPull.Value())
if has, value := opts.IsPull.Get(); has {
sess.And("issue.is_pull=?", value)
}
return sess

View file

@ -378,8 +378,8 @@ func doRecalcMilestone(ctx context.Context, cond builder.Cond, updateTimestamp o
}),
).
Where(cond)
if updateTimestamp.Has() {
sess.SetExpr("updated_unix", updateTimestamp.Value()).NoAutoTime()
if has, value := updateTimestamp.Get(); has {
sess.SetExpr("updated_unix", value).NoAutoTime()
}
_, err := sess.Update(&Milestone{})
if err != nil {

View file

@ -40,8 +40,8 @@ func (opts FindMilestoneOptions) ToConds() builder.Cond {
if opts.RepoID != 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
if opts.IsClosed.Has() {
cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()})
if has, value := opts.IsClosed.Get(); has {
cond = cond.And(builder.Eq{"is_closed": value})
}
if opts.RepoCond != nil && opts.RepoCond.IsValid() {
cond = cond.And(builder.In("repo_id", builder.Select("id").From("repository").Where(opts.RepoCond)))

View file

@ -113,8 +113,8 @@ func (opts *FindReviewOptions) toCond() builder.Cond {
if opts.OfficialOnly {
cond = cond.And(builder.Eq{"official": true})
}
if opts.Dismissed.Has() {
cond = cond.And(builder.Eq{"dismissed": opts.Dismissed.Value()})
if has, value := opts.Dismissed.Get(); has {
cond = cond.And(builder.Eq{"dismissed": value})
}
return cond
}

View file

@ -379,8 +379,8 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC
}
session := sumSession(opts, issueIDs)
if isClosed.Has() {
session = session.And("issue.is_closed = ?", isClosed.Value())
if has, value := isClosed.Get(); has {
session = session.And("issue.is_closed = ?", value)
}
return session.SumInt(new(trackedTime), "tracked_time.time")
}

View file

@ -55,7 +55,7 @@ func CountPackages(ctx context.Context, opts *packages_model.PackageSearchOption
func toConds(opts *packages_model.PackageSearchOptions) builder.Cond {
var cond builder.Cond = builder.Eq{
"package.is_internal": opts.IsInternal.Value(),
"package.is_internal": opts.IsInternal.ValueOrZeroValue(),
"package.owner_id": opts.OwnerID,
"package.type": packages_model.TypeNuGet,
}

View file

@ -192,9 +192,9 @@ type PackageSearchOptions struct {
func (opts *PackageSearchOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.IsInternal.Has() {
if has, value := opts.IsInternal.Get(); has {
cond = builder.Eq{
"package_version.is_internal": opts.IsInternal.Value(),
"package_version.is_internal": value,
}
}
@ -254,10 +254,10 @@ func (opts *PackageSearchOptions) ToConds() builder.Cond {
cond = cond.And(builder.Exists(builder.Select("package_file.id").From("package_file").Where(fileCond)))
}
if opts.HasFiles.Has() {
if has, value := opts.HasFiles.Get(); has {
filesCond := builder.Exists(builder.Select("package_file.id").From("package_file").Where(builder.Expr("package_file.version_id = package_version.id")))
if !opts.HasFiles.Value() {
if !value {
filesCond = builder.Not{filesCond}
}

View file

@ -217,8 +217,8 @@ func (opts SearchOptions) ToConds() builder.Cond {
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
if opts.IsClosed.Has() {
cond = cond.And(builder.Eq{"is_closed": opts.IsClosed.Value()})
if has, value := opts.IsClosed.Get(); has {
cond = cond.And(builder.Eq{"is_closed": value})
}
if opts.Type > 0 {

View file

@ -312,14 +312,14 @@ func (opts FindReleasesOptions) ToConds() builder.Cond {
if len(opts.TagNames) > 0 {
cond = cond.And(builder.In("tag_name", opts.TagNames))
}
if opts.IsPreRelease.Has() {
cond = cond.And(builder.Eq{"is_prerelease": opts.IsPreRelease.Value()})
if has, value := opts.IsPreRelease.Get(); has {
cond = cond.And(builder.Eq{"is_prerelease": value})
}
if opts.IsDraft.Has() {
cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.Value()})
if has, value := opts.IsDraft.Get(); has {
cond = cond.And(builder.Eq{"is_draft": value})
}
if opts.HasSha1.Has() {
if opts.HasSha1.Value() {
if has, value := opts.HasSha1.Get(); has {
if value {
cond = cond.And(builder.Neq{"sha1": ""})
} else {
cond = cond.And(builder.Eq{"sha1": ""})

View file

@ -895,8 +895,8 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64,
if opts.OwnerID > 0 {
sess.And("owner_id = ?", opts.OwnerID)
}
if opts.Private.Has() {
sess.And("is_private=?", opts.Private.Value())
if has, value := opts.Private.Get(); has {
sess.And("is_private=?", value)
}
count, err := sess.Count(new(Repository))

View file

@ -354,12 +354,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
)))
}
if opts.IsPrivate.Has() {
cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.Value()})
if has, value := opts.IsPrivate.Get(); has {
cond = cond.And(builder.Eq{"is_private": value})
}
if opts.Template.Has() {
cond = cond.And(builder.Eq{"is_template": opts.Template.Value()})
if has, value := opts.Template.Get(); has {
cond = cond.And(builder.Eq{"is_template": value})
}
// Restrict to starred repositories
@ -375,7 +375,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
// Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
if opts.OwnerID > 0 {
accessCond := builder.NewCond()
if !opts.Collaborate.Value() {
if !opts.Collaborate.ValueOrZeroValue() {
accessCond = builder.Eq{"owner_id": opts.OwnerID}
}
@ -467,28 +467,28 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
}
if opts.Fork.Has() || opts.OnlyShowRelevant {
if opts.OnlyShowRelevant && !opts.Fork.Has() {
if has, value := opts.Fork.Get(); has || opts.OnlyShowRelevant {
if opts.OnlyShowRelevant && !has {
cond = cond.And(builder.Eq{"is_fork": false})
} else {
cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()})
cond = cond.And(builder.Eq{"is_fork": value})
}
}
if opts.Mirror.Has() {
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror.Value()})
if has, value := opts.Mirror.Get(); has {
cond = cond.And(builder.Eq{"is_mirror": value})
}
if opts.Actor != nil && opts.Actor.IsRestricted {
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
}
if opts.Archived.Has() {
cond = cond.And(builder.Eq{"is_archived": opts.Archived.Value()})
if has, value := opts.Archived.Get(); has {
cond = cond.And(builder.Eq{"is_archived": value})
}
if opts.HasMilestones.Has() {
if opts.HasMilestones.Value() {
if has, value := opts.HasMilestones.Get(); has {
if value {
cond = cond.And(builder.Gt{"num_milestones": 0})
} else {
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))

View file

@ -331,21 +331,21 @@ func TestSearchRepository(t *testing.T) {
assert.False(t, repo.IsPrivate)
}
if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() {
if testCase.opts.Fork.ValueOrZeroValue() && testCase.opts.Mirror.ValueOrZeroValue() {
assert.True(t, repo.IsFork && repo.IsMirror)
} else {
if testCase.opts.Fork.Has() {
assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork)
if has, value := testCase.opts.Fork.Get(); has {
assert.Equal(t, value, repo.IsFork)
}
if testCase.opts.Mirror.Has() {
assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror)
if has, value := testCase.opts.Mirror.Get(); has {
assert.Equal(t, value, repo.IsMirror)
}
}
if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
if testCase.opts.Collaborate.Has() {
if testCase.opts.Collaborate.Value() {
if has, value := testCase.opts.Collaborate.Get(); has {
if value {
assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
} else {
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)

View file

@ -323,12 +323,12 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
))
}
if opts.IsPrimary.Has() {
cond = cond.And(builder.Eq{"email_address.is_primary": opts.IsPrimary.Value()})
if has, value := opts.IsPrimary.Get(); has {
cond = cond.And(builder.Eq{"email_address.is_primary": value})
}
if opts.IsActivated.Has() {
cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()})
if has, value := opts.IsActivated.Get(); has {
cond = cond.And(builder.Eq{"email_address.is_activated": value})
}
count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.id = email_address.uid").

View file

@ -101,40 +101,41 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
cond = cond.And(builder.Eq{"id": opts.UID})
}
if opts.SourceID.Has() {
cond = cond.And(builder.Eq{"login_source": opts.SourceID.Value()})
if has, value := opts.SourceID.Get(); has {
cond = cond.And(builder.Eq{"login_source": value})
}
if opts.LoginName != "" {
cond = cond.And(builder.Eq{"login_name": opts.LoginName})
}
if opts.IsActive.Has() {
cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
if has, value := opts.IsActive.Get(); has {
cond = cond.And(builder.Eq{"is_active": value})
}
if opts.IsAdmin.Has() {
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
if has, value := opts.IsAdmin.Get(); has {
cond = cond.And(builder.Eq{"is_admin": value})
}
if opts.IsRestricted.Has() {
cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.Value()})
if has, value := opts.IsRestricted.Get(); has {
cond = cond.And(builder.Eq{"is_restricted": value})
}
if opts.IsProhibitLogin.Has() {
cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()})
if has, value := opts.IsProhibitLogin.Get(); has {
cond = cond.And(builder.Eq{"prohibit_login": value})
}
if opts.AccountType.Has() {
cond = cond.And(builder.Eq{"type": opts.AccountType.Value()})
if has, value := opts.AccountType.Get(); has {
cond = cond.And(builder.Eq{"type": value})
}
e := db.GetEngine(ctx)
if !opts.IsTwoFactorEnabled.Has() {
hasTwoFactor, isTwoFactorEnabled := opts.IsTwoFactorEnabled.Get()
if !hasTwoFactor {
return e.Where(cond)
}
// Check if the user has two factor enabled, which is TOTP or Webauthn.
if opts.IsTwoFactorEnabled.Value() {
if isTwoFactorEnabled {
cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL OR webauthn_credential.user_id IS NOT NULL"))
} else {
cond = cond.And(builder.Expr("two_factor.uid IS NULL AND webauthn_credential.user_id IS NULL"))

View file

@ -779,14 +779,14 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa
// overwrite defaults if set
if overwriteDefaultPresent {
overwrite := overwriteDefault[0]
if overwrite.KeepEmailPrivate.Has() {
u.KeepEmailPrivate = overwrite.KeepEmailPrivate.Value()
if has, value := overwrite.KeepEmailPrivate.Get(); has {
u.KeepEmailPrivate = value
}
if overwrite.Visibility != nil {
u.Visibility = *overwrite.Visibility
}
if overwrite.AllowCreateOrganization.Has() {
u.AllowCreateOrganization = overwrite.AllowCreateOrganization.Value()
if has, value := overwrite.AllowCreateOrganization.Get(); has {
u.AllowCreateOrganization = value
}
if overwrite.EmailNotificationsPreference != nil {
u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference
@ -797,11 +797,11 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa
if overwrite.Theme != nil {
u.Theme = *overwrite.Theme
}
if overwrite.IsRestricted.Has() {
u.IsRestricted = overwrite.IsRestricted.Value()
if has, value := overwrite.IsRestricted.Get(); has {
u.IsRestricted = value
}
if overwrite.IsActive.Has() {
u.IsActive = overwrite.IsActive.Value()
if has, value := overwrite.IsActive.Get(); has {
u.IsActive = value
}
}
@ -917,8 +917,8 @@ func countUsers(ctx context.Context, opts *CountUserFilter) int64 {
cond = cond.And(builder.Gte{"last_login_unix": *opts.LastLoginSince})
}
if opts.IsAdmin.Has() {
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
if has, value := opts.IsAdmin.Get(); has {
cond = cond.And(builder.Eq{"is_admin": value})
}
}

View file

@ -487,8 +487,8 @@ func (opts ListWebhookOptions) ToConds() builder.Cond {
if opts.OwnerID != 0 {
cond = cond.And(builder.Eq{"webhook.owner_id": opts.OwnerID})
}
if opts.IsActive.Has() {
cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.Value()})
if has, value := opts.IsActive.Get(); has {
cond = cond.And(builder.Eq{"webhook.is_active": value})
}
return cond
}

View file

@ -48,15 +48,15 @@ func BoolFieldQuery(value bool, field string) *query.BoolFieldQuery {
func NumericRangeInclusiveQuery(min, max optional.Option[int64], field string) *query.NumericRangeQuery {
var minF, maxF *float64
var minI, maxI *bool
if min.Has() {
if has, value := min.Get(); has {
minF = new(float64)
*minF = float64(min.Value())
*minF = float64(value)
minI = new(bool)
*minI = true
}
if max.Has() {
if has, value := max.Get(); has {
maxF = new(float64)
*maxF = float64(max.Value())
*maxF = float64(value)
maxI = new(bool)
*maxI = true
}

View file

@ -195,19 +195,19 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
filters = append(filters, bleve.NewDisjunctionQuery(repoQueries...))
}
if options.PriorityRepoID.Has() {
eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id")
if has, value := options.PriorityRepoID.Get(); has {
eq := inner_bleve.NumericEqualityQuery(value, "repo_id")
eq.SetBoost(10.0)
meh := bleve.NewMatchAllQuery()
meh.SetBoost(0)
q.AddShould(bleve.NewDisjunctionQuery(eq, meh))
}
if options.IsPull.Has() {
filters = append(filters, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull"))
if has, value := options.IsPull.Get(); has {
filters = append(filters, inner_bleve.BoolFieldQuery(value, "is_pull"))
}
if options.IsClosed.Has() {
filters = append(filters, inner_bleve.BoolFieldQuery(options.IsClosed.Value(), "is_closed"))
if has, value := options.IsClosed.Get(); has {
filters = append(filters, inner_bleve.BoolFieldQuery(value, "is_closed"))
}
if options.NoLabelOnly {
@ -251,8 +251,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
"review_requested_ids": options.ReviewRequestedID,
"subscriber_ids": options.SubscriberID,
} {
if val.Has() {
filters = append(filters, inner_bleve.NumericEqualityQuery(val.Value(), key))
if has, value := val.Get(); has {
filters = append(filters, inner_bleve.NumericEqualityQuery(value, key))
}
}

View file

@ -39,10 +39,10 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_
// See the comment of issues_model.SearchOptions for the reason why we need to convert
convertID := func(id optional.Option[int64]) int64 {
if !id.Has() {
has, value := id.Get()
if !has {
return 0
}
value := id.Value()
if value == 0 {
return db.NoConditionID
}
@ -69,8 +69,8 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_
IncludeMilestones: nil,
SortType: sortType,
IssueIDs: nil,
UpdatedAfterUnix: options.UpdatedAfterUnix.Value(),
UpdatedBeforeUnix: options.UpdatedBeforeUnix.Value(),
UpdatedAfterUnix: options.UpdatedAfterUnix.ValueOrZeroValue(),
UpdatedBeforeUnix: options.UpdatedBeforeUnix.ValueOrZeroValue(),
PriorityRepoID: 0,
IsArchived: optional.None[bool](),
Org: nil,
@ -78,9 +78,9 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issues_
User: nil,
}
if options.PriorityRepoID.Has() {
if has, value := options.PriorityRepoID.Get(); has {
opts.SortType = "priorityrepo"
opts.PriorityRepoID = options.PriorityRepoID.Value()
opts.PriorityRepoID = value
}
if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 {

View file

@ -184,16 +184,16 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
}
query.Must(q)
}
if options.PriorityRepoID.Has() {
q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10)
if has, value := options.PriorityRepoID.Get(); has {
q := elastic.NewTermQuery("repo_id", value).Boost(10)
query.Should(q)
}
if options.IsPull.Has() {
query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value()))
if has, value := options.IsPull.Get(); has {
query.Must(elastic.NewTermQuery("is_pull", value))
}
if options.IsClosed.Has() {
query.Must(elastic.NewTermQuery("is_closed", options.IsClosed.Value()))
if has, value := options.IsClosed.Get(); has {
query.Must(elastic.NewTermQuery("is_closed", value))
}
if options.NoLabelOnly {
@ -221,43 +221,43 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...))
}
if options.ProjectID.Has() {
query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value()))
if has, value := options.ProjectID.Get(); has {
query.Must(elastic.NewTermQuery("project_id", value))
}
if options.ProjectColumnID.Has() {
query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value()))
if has, value := options.ProjectColumnID.Get(); has {
query.Must(elastic.NewTermQuery("project_board_id", value))
}
if options.PosterID.Has() {
query.Must(elastic.NewTermQuery("poster_id", options.PosterID.Value()))
if has, value := options.PosterID.Get(); has {
query.Must(elastic.NewTermQuery("poster_id", value))
}
if options.AssigneeID.Has() {
query.Must(elastic.NewTermQuery("assignee_ids", options.AssigneeID.Value()))
if has, value := options.AssigneeID.Get(); has {
query.Must(elastic.NewTermQuery("assignee_ids", value))
}
if options.MentionID.Has() {
query.Must(elastic.NewTermQuery("mention_ids", options.MentionID.Value()))
if has, value := options.MentionID.Get(); has {
query.Must(elastic.NewTermQuery("mention_ids", value))
}
if options.ReviewedID.Has() {
query.Must(elastic.NewTermQuery("reviewed_ids", options.ReviewedID.Value()))
if has, value := options.ReviewedID.Get(); has {
query.Must(elastic.NewTermQuery("reviewed_ids", value))
}
if options.ReviewRequestedID.Has() {
query.Must(elastic.NewTermQuery("review_requested_ids", options.ReviewRequestedID.Value()))
if has, value := options.ReviewRequestedID.Get(); has {
query.Must(elastic.NewTermQuery("review_requested_ids", value))
}
if options.SubscriberID.Has() {
query.Must(elastic.NewTermQuery("subscriber_ids", options.SubscriberID.Value()))
if has, value := options.SubscriberID.Get(); has {
query.Must(elastic.NewTermQuery("subscriber_ids", value))
}
if options.UpdatedAfterUnix.Has() || options.UpdatedBeforeUnix.Has() {
q := elastic.NewRangeQuery("updated_unix")
if options.UpdatedAfterUnix.Has() {
q.Gte(options.UpdatedAfterUnix.Value())
if has, value := options.UpdatedAfterUnix.Get(); has {
q.Gte(value)
}
if options.UpdatedBeforeUnix.Has() {
q.Lte(options.UpdatedBeforeUnix.Value())
if has, value := options.UpdatedBeforeUnix.Get(); has {
q.Lte(value)
}
query.Must(q)
}

View file

@ -139,11 +139,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.And(q)
}
if options.IsPull.Has() {
query.And(inner_meilisearch.NewFilterEq("is_pull", options.IsPull.Value()))
if has, value := options.IsPull.Get(); has {
query.And(inner_meilisearch.NewFilterEq("is_pull", value))
}
if options.IsClosed.Has() {
query.And(inner_meilisearch.NewFilterEq("is_closed", options.IsClosed.Value()))
if has, value := options.IsClosed.Get(); has {
query.And(inner_meilisearch.NewFilterEq("is_closed", value))
}
if options.NoLabelOnly {
@ -171,41 +171,41 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...))
}
if options.ProjectID.Has() {
query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value()))
if has, value := options.ProjectID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("project_id", value))
}
if options.ProjectColumnID.Has() {
query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value()))
if has, value := options.ProjectColumnID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("project_board_id", value))
}
if options.PosterID.Has() {
query.And(inner_meilisearch.NewFilterEq("poster_id", options.PosterID.Value()))
if has, value := options.PosterID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("poster_id", value))
}
if options.AssigneeID.Has() {
query.And(inner_meilisearch.NewFilterEq("assignee_ids", options.AssigneeID.Value()))
if has, value := options.AssigneeID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("assignee_ids", value))
}
if options.MentionID.Has() {
query.And(inner_meilisearch.NewFilterEq("mention_ids", options.MentionID.Value()))
if has, value := options.MentionID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("mention_ids", value))
}
if options.ReviewedID.Has() {
query.And(inner_meilisearch.NewFilterEq("reviewed_ids", options.ReviewedID.Value()))
if has, value := options.ReviewedID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("reviewed_ids", value))
}
if options.ReviewRequestedID.Has() {
query.And(inner_meilisearch.NewFilterEq("review_requested_ids", options.ReviewRequestedID.Value()))
if has, value := options.ReviewRequestedID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("review_requested_ids", value))
}
if options.SubscriberID.Has() {
query.And(inner_meilisearch.NewFilterEq("subscriber_ids", options.SubscriberID.Value()))
if has, value := options.SubscriberID.Get(); has {
query.And(inner_meilisearch.NewFilterEq("subscriber_ids", value))
}
if options.UpdatedAfterUnix.Has() {
query.And(inner_meilisearch.NewFilterGte("updated_unix", options.UpdatedAfterUnix.Value()))
if has, value := options.UpdatedAfterUnix.Get(); has {
query.And(inner_meilisearch.NewFilterGte("updated_unix", value))
}
if options.UpdatedBeforeUnix.Has() {
query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value()))
if has, value := options.UpdatedBeforeUnix.Get(); has {
query.And(inner_meilisearch.NewFilterLte("updated_unix", value))
}
var sortBy []string

View file

@ -34,9 +34,17 @@ func (o Option[T]) Has() bool {
return o != nil
}
func (o Option[T]) Value() T {
var zero T
return o.ValueOrDefault(zero)
func (o Option[T]) Get() (has bool, value T) {
if o != nil {
has = true
value = o[0]
}
return has, value
}
func (o Option[T]) ValueOrZeroValue() T {
var zeroValue T
return o.ValueOrDefault(zeroValue)
}
func (o Option[T]) ValueOrDefault(v T) T {

View file

@ -14,27 +14,27 @@ import (
func TestOption(t *testing.T) {
var uninitialized optional.Option[int]
assert.False(t, uninitialized.Has())
assert.Equal(t, int(0), uninitialized.Value())
assert.Equal(t, int(0), uninitialized.ValueOrZeroValue())
assert.Equal(t, int(1), uninitialized.ValueOrDefault(1))
none := optional.None[int]()
assert.False(t, none.Has())
assert.Equal(t, int(0), none.Value())
assert.Equal(t, int(0), none.ValueOrZeroValue())
assert.Equal(t, int(1), none.ValueOrDefault(1))
some := optional.Some(1)
assert.True(t, some.Has())
assert.Equal(t, int(1), some.Value())
assert.Equal(t, int(1), some.ValueOrZeroValue())
assert.Equal(t, int(1), some.ValueOrDefault(2))
noneBool := optional.None[bool]()
assert.False(t, noneBool.Has())
assert.False(t, noneBool.Value())
assert.False(t, noneBool.ValueOrZeroValue())
assert.True(t, noneBool.ValueOrDefault(true))
someBool := optional.Some(true)
assert.True(t, someBool.Has())
assert.True(t, someBool.Value())
assert.True(t, someBool.ValueOrZeroValue())
assert.True(t, someBool.ValueOrDefault(false))
var ptr *int
@ -43,19 +43,22 @@ func TestOption(t *testing.T) {
int1 := 1
opt1 := optional.FromPtr(&int1)
assert.True(t, opt1.Has())
assert.Equal(t, int(1), opt1.Value())
_, v := opt1.Get()
assert.Equal(t, int(1), v)
assert.False(t, optional.FromNonDefault("").Has())
opt2 := optional.FromNonDefault("test")
assert.True(t, opt2.Has())
assert.Equal(t, "test", opt2.Value())
_, vStr := opt2.Get()
assert.Equal(t, "test", vStr)
assert.False(t, optional.FromNonDefault(0).Has())
opt3 := optional.FromNonDefault(1)
assert.True(t, opt3.Has())
assert.Equal(t, int(1), opt3.Value())
_, v = opt3.Get()
assert.Equal(t, int(1), v)
}
func Test_ParseBool(t *testing.T) {

View file

@ -19,11 +19,11 @@ func (o *Option[T]) UnmarshalJSON(data []byte) error {
}
func (o Option[T]) MarshalJSON() ([]byte, error) {
if !o.Has() {
has, v := o.Get()
if !has {
return []byte("null"), nil
}
return json.Marshal(o.Value())
return json.Marshal(v)
}
func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error {
@ -36,11 +36,12 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error {
}
func (o Option[T]) MarshalYAML() (any, error) {
if !o.Has() {
has, v := o.Get()
if !has {
return nil, nil
}
value := new(yaml.Node)
err := value.Encode(o.Value())
err := value.Encode(v)
return value, err
}

View file

@ -167,7 +167,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) {
// The following is a purposefully undocumented option. Please do not run Forgejo as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")
unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value()
unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || optional.ParseBool(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).ValueOrDefault(false)
RunMode = os.Getenv("GITEA_RUN_MODE")
if RunMode == "" {
RunMode = rootSec.Key("RUN_MODE").MustString("prod")

View file

@ -497,7 +497,7 @@ func ListIssues(ctx *context.APIContext) {
isPull = optional.Some(false)
}
if isPull.Has() && !ctx.Repo.CanReadIssuesOrPulls(isPull.Value()) {
if has, value := isPull.Get(); has && !ctx.Repo.CanReadIssuesOrPulls(value) {
ctx.NotFound()
return
}

View file

@ -143,20 +143,20 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
pager.AddParam(ctx, "topic", "TopicOnly")
pager.AddParam(ctx, "language", "Language")
pager.AddParamString(relevantReposOnlyParam, fmt.Sprint(opts.OnlyShowRelevant))
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
if has, value := archived.Get(); has {
pager.AddParamString("archived", fmt.Sprint(value))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
if has, value := fork.Get(); has {
pager.AddParamString("fork", fmt.Sprint(value))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
if has, value := mirror.Get(); has {
pager.AddParamString("mirror", fmt.Sprint(value))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
if has, value := template.Get(); has {
pager.AddParamString("template", fmt.Sprint(value))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
if has, value := private.Get(); has {
pager.AddParamString("private", fmt.Sprint(value))
}
ctx.Data["Page"] = pager

View file

@ -148,20 +148,20 @@ func Home(ctx *context.Context) {
pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParamString("language", language)
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
if has, value := archived.Get(); has {
pager.AddParamString("archived", fmt.Sprint(value))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
if has, value := fork.Get(); has {
pager.AddParamString("fork", fmt.Sprint(value))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
if has, value := mirror.Get(); has {
pager.AddParamString("mirror", fmt.Sprint(value))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
if has, value := template.Get(); has {
pager.AddParamString("template", fmt.Sprint(value))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
if has, value := private.Get(); has {
pager.AddParamString("private", fmt.Sprint(value))
}
ctx.Data["Page"] = pager

View file

@ -274,7 +274,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
var total int
switch {
case isShowClosed.Value():
case isShowClosed.ValueOrZeroValue():
total = int(issueStats.ClosedCount)
case !isShowClosed.Has():
total = int(issueStats.OpenCount + issueStats.ClosedCount)
@ -321,8 +321,8 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
// depending on the query syntax
isShowClosed = opts.IsClosed
sortType = opts.SortBy.ToIssueSort()
posterID = opts.PosterID.Value()
assigneeID = opts.AssigneeID.Value()
posterID = opts.PosterID.ValueOrZeroValue()
assigneeID = opts.AssigneeID.ValueOrZeroValue()
}
approvalCounts, err := issues.GetApprovalCounts(ctx)
@ -440,7 +440,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
return
}
pinned, err := issues_model.GetPinnedIssues(ctx, repo.ID, isPullOption.Value())
pinned, err := issues_model.GetPinnedIssues(ctx, repo.ID, isPullOption.ValueOrZeroValue())
if err != nil {
ctx.ServerError("GetPinnedIssues", err)
return
@ -475,7 +475,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
ctx.Data["Keyword"] = keyword
ctx.Data["IsShowClosed"] = isShowClosed
switch {
case isShowClosed.Value():
case isShowClosed.ValueOrZeroValue():
ctx.Data["State"] = "closed"
case !isShowClosed.Has():
ctx.Data["State"] = "all"

View file

@ -190,7 +190,7 @@ func lockablesGitAttributes(gitRepo *git.Repository, lfsLocks []*git_model.LFSLo
if err != nil {
return nil, fmt.Errorf("could not CheckPath(%s): %w", lock.Path, err)
}
lockables[i] = attrs["lockable"].Bool().Value()
lockables[i] = attrs["lockable"].Bool().ValueOrZeroValue()
}
return lockables, nil
}

View file

@ -679,8 +679,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
if err != nil {
log.Error("GitAttributes(%s, %s) failed: %v", ctx.Repo.CommitID, ctx.Repo.TreePath, err)
} else {
ctx.Data["IsVendored"] = attrs["linguist-vendored"].Bool().Value()
ctx.Data["IsGenerated"] = attrs["linguist-generated"].Bool().Value()
ctx.Data["IsVendored"] = attrs["linguist-vendored"].Bool().ValueOrZeroValue()
ctx.Data["IsGenerated"] = attrs["linguist-generated"].Bool().ValueOrZeroValue()
}
}

View file

@ -680,8 +680,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
ctx.Data["State"] = state
ctx.SetFormString("state", state)
if searchOpts.AssigneeID.Has() {
id := strconv.FormatInt(searchOpts.AssigneeID.Value(), 10)
if has, value := searchOpts.AssigneeID.Get(); has {
id := strconv.FormatInt(value, 10)
ctx.SetFormString("assignee", id)
}

View file

@ -439,20 +439,20 @@ func NotificationWatching(ctx *context.Context) {
// redirect to last page if request page is more than total pages
pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx)
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
if has, value := archived.Get(); has {
pager.AddParamString("archived", fmt.Sprint(value))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
if has, value := fork.Get(); has {
pager.AddParamString("fork", fmt.Sprint(value))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
if has, value := mirror.Get(); has {
pager.AddParamString("mirror", fmt.Sprint(value))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
if has, value := template.Get(); has {
pager.AddParamString("template", fmt.Sprint(value))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
if has, value := private.Get(); has {
pager.AddParamString("private", fmt.Sprint(value))
}
ctx.Data["Page"] = pager

View file

@ -345,20 +345,20 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
if tab == "activity" {
pager.AddParam(ctx, "date", "Date")
}
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
if has, value := archived.Get(); has {
pager.AddParamString("archived", fmt.Sprint(value))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
if has, value := fork.Get(); has {
pager.AddParamString("fork", fmt.Sprint(value))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
if has, value := mirror.Get(); has {
pager.AddParamString("mirror", fmt.Sprint(value))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
if has, value := template.Get(); has {
pager.AddParamString("template", fmt.Sprint(value))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
if has, value := private.Get(); has {
pager.AddParamString("private", fmt.Sprint(value))
}
ctx.Data["Page"] = pager
}

View file

@ -1240,11 +1240,11 @@ func GetDiffFull(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions
log.Error("checker.CheckPath(%s) failed: %v", diffFile.Name, err)
} else {
vendored := attrs["linguist-vendored"].Bool()
diffFile.IsVendored = vendored.Value()
diffFile.IsVendored = vendored.ValueOrZeroValue()
gotVendor = vendored.Has()
generated := attrs["linguist-generated"].Bool()
diffFile.IsGenerated = generated.Value()
diffFile.IsGenerated = generated.ValueOrZeroValue()
gotGenerated = generated.Has()
diffFile.Language = cmp.Or(

View file

@ -46,135 +46,102 @@ type UpdateOptions struct {
func UpdateUser(ctx context.Context, u *user_model.User, opts *UpdateOptions) error {
cols := make([]string, 0, 20)
if opts.KeepEmailPrivate.Has() {
u.KeepEmailPrivate = opts.KeepEmailPrivate.Value()
if has, value := opts.KeepEmailPrivate.Get(); has {
u.KeepEmailPrivate = value
cols = append(cols, "keep_email_private")
}
if opts.FullName.Has() {
u.FullName = opts.FullName.Value()
if has, value := opts.FullName.Get(); has {
u.FullName = value
cols = append(cols, "full_name")
}
if opts.Pronouns.Has() {
u.Pronouns = opts.Pronouns.Value()
if has, value := opts.Pronouns.Get(); has {
u.Pronouns = value
cols = append(cols, "pronouns")
}
if opts.Website.Has() {
u.Website = opts.Website.Value()
if has, value := opts.Website.Get(); has {
u.Website = value
cols = append(cols, "website")
}
if opts.Location.Has() {
u.Location = opts.Location.Value()
if has, value := opts.Location.Get(); has {
u.Location = value
cols = append(cols, "location")
}
if opts.Description.Has() {
u.Description = opts.Description.Value()
if has, value := opts.Description.Get(); has {
u.Description = value
cols = append(cols, "description")
}
if opts.Language.Has() {
u.Language = opts.Language.Value()
if has, value := opts.Language.Get(); has {
u.Language = value
cols = append(cols, "language")
}
if opts.Theme.Has() {
u.Theme = opts.Theme.Value()
if has, value := opts.Theme.Get(); has {
u.Theme = value
cols = append(cols, "theme")
}
if opts.DiffViewStyle.Has() {
u.DiffViewStyle = opts.DiffViewStyle.Value()
if has, value := opts.DiffViewStyle.Get(); has {
u.DiffViewStyle = value
cols = append(cols, "diff_view_style")
}
if opts.EnableRepoUnitHints.Has() {
u.EnableRepoUnitHints = opts.EnableRepoUnitHints.Value()
if has, value := opts.EnableRepoUnitHints.Get(); has {
u.EnableRepoUnitHints = value
cols = append(cols, "enable_repo_unit_hints")
}
if opts.KeepPronounsPrivate.Has() {
u.KeepPronounsPrivate = opts.KeepPronounsPrivate.Value()
if has, value := opts.KeepPronounsPrivate.Get(); has {
u.KeepPronounsPrivate = value
cols = append(cols, "keep_pronouns_private")
}
if opts.AllowGitHook.Has() {
u.AllowGitHook = opts.AllowGitHook.Value()
if has, value := opts.AllowGitHook.Get(); has {
u.AllowGitHook = value
cols = append(cols, "allow_git_hook")
}
if opts.AllowImportLocal.Has() {
u.AllowImportLocal = opts.AllowImportLocal.Value()
if has, value := opts.AllowImportLocal.Get(); has {
u.AllowImportLocal = value
cols = append(cols, "allow_import_local")
}
if opts.MaxRepoCreation.Has() {
u.MaxRepoCreation = opts.MaxRepoCreation.Value()
if has, value := opts.MaxRepoCreation.Get(); has {
u.MaxRepoCreation = value
cols = append(cols, "max_repo_creation")
}
if opts.IsActive.Has() {
u.IsActive = opts.IsActive.Value()
if has, value := opts.IsActive.Get(); has {
u.IsActive = value
cols = append(cols, "is_active")
}
if opts.IsRestricted.Has() {
u.IsRestricted = opts.IsRestricted.Value()
if has, value := opts.IsRestricted.Get(); has {
u.IsRestricted = value
cols = append(cols, "is_restricted")
}
if opts.IsAdmin.Has() {
if !opts.IsAdmin.Value() && user_model.IsLastAdminUser(ctx, u) {
if has, value := opts.IsAdmin.Get(); has {
if !value && user_model.IsLastAdminUser(ctx, u) {
return models.ErrDeleteLastAdminUser{UID: u.ID}
}
u.IsAdmin = opts.IsAdmin.Value()
u.IsAdmin = value
cols = append(cols, "is_admin")
}
if opts.Visibility.Has() {
if !u.IsOrganization() && !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(opts.Visibility.Value()) {
return fmt.Errorf("visibility mode not allowed: %s", opts.Visibility.Value().String())
if has, value := opts.Visibility.Get(); has {
if !u.IsOrganization() && !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(value) {
return fmt.Errorf("visibility mode not allowed: %s", value.String())
}
u.Visibility = opts.Visibility.Value()
u.Visibility = value
cols = append(cols, "visibility")
}
if opts.KeepActivityPrivate.Has() {
u.KeepActivityPrivate = opts.KeepActivityPrivate.Value()
if has, value := opts.KeepActivityPrivate.Get(); has {
u.KeepActivityPrivate = value
cols = append(cols, "keep_activity_private")
}
if opts.AllowCreateOrganization.Has() {
u.AllowCreateOrganization = opts.AllowCreateOrganization.Value()
if has, value := opts.AllowCreateOrganization.Get(); has {
u.AllowCreateOrganization = value
cols = append(cols, "allow_create_organization")
}
if opts.RepoAdminChangeTeamAccess.Has() {
u.RepoAdminChangeTeamAccess = opts.RepoAdminChangeTeamAccess.Value()
if has, value := opts.RepoAdminChangeTeamAccess.Get(); has {
u.RepoAdminChangeTeamAccess = value
cols = append(cols, "repo_admin_change_team_access")
}
if opts.EmailNotificationsPreference.Has() {
u.EmailNotificationsPreference = opts.EmailNotificationsPreference.Value()
if has, value := opts.EmailNotificationsPreference.Get(); has {
u.EmailNotificationsPreference = value
cols = append(cols, "email_notifications_preference")
}
if opts.SetLastLogin {
u.SetLastLogin()
cols = append(cols, "last_login_unix")
}
@ -190,8 +157,8 @@ type UpdateAuthOptions struct {
}
func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions) error {
if opts.LoginSource.Has() {
source, err := auth_model.GetSourceByID(ctx, opts.LoginSource.Value())
if has, value := opts.LoginSource.Get(); has {
source, err := auth_model.GetSourceByID(ctx, value)
if err != nil {
return err
}
@ -199,12 +166,12 @@ func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions
u.LoginType = source.Type
u.LoginSource = source.ID
}
if opts.LoginName.Has() {
u.LoginName = opts.LoginName.Value()
if has, value := opts.LoginName.Get(); has {
u.LoginName = value
}
if opts.Password.Has() && (u.IsLocal() || u.IsOAuth2()) {
password := opts.Password.Value()
if has, value := opts.Password.Get(); has && (u.IsLocal() || u.IsOAuth2()) {
password := value
if len(password) < setting.MinPasswordLength {
return password_module.ErrMinLength
@ -221,11 +188,11 @@ func UpdateAuth(ctx context.Context, u *user_model.User, opts *UpdateAuthOptions
}
}
if opts.MustChangePassword.Has() {
u.MustChangePassword = opts.MustChangePassword.Value()
if has, value := opts.MustChangePassword.Get(); has {
u.MustChangePassword = value
}
if opts.ProhibitLogin.Has() {
u.ProhibitLogin = opts.ProhibitLogin.Value()
if has, value := opts.ProhibitLogin.Get(); has {
u.ProhibitLogin = value
}
if err := user_model.UpdateUserCols(ctx, u, "login_type", "login_source", "login_name", "passwd", "passwd_hash_algo", "salt", "must_change_password", "prohibit_login"); err != nil {

View file

@ -51,44 +51,44 @@ func TestUpdateUser(t *testing.T) {
}
require.NoError(t, UpdateUser(db.DefaultContext, user, opts))
assert.Equal(t, opts.KeepEmailPrivate.Value(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.Value(), user.FullName)
assert.Equal(t, opts.Website.Value(), user.Website)
assert.Equal(t, opts.Location.Value(), user.Location)
assert.Equal(t, opts.Description.Value(), user.Description)
assert.Equal(t, opts.AllowGitHook.Value(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.Value(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.Value(), user.Language)
assert.Equal(t, opts.Theme.Value(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.Value(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.Value(), user.EmailNotificationsPreference)
assert.Equal(t, opts.KeepEmailPrivate.ValueOrZeroValue(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.ValueOrZeroValue(), user.FullName)
assert.Equal(t, opts.Website.ValueOrZeroValue(), user.Website)
assert.Equal(t, opts.Location.ValueOrZeroValue(), user.Location)
assert.Equal(t, opts.Description.ValueOrZeroValue(), user.Description)
assert.Equal(t, opts.AllowGitHook.ValueOrZeroValue(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.ValueOrZeroValue(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.ValueOrZeroValue(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.ValueOrZeroValue(), user.IsRestricted)
assert.Equal(t, opts.IsActive.ValueOrZeroValue(), user.IsActive)
assert.Equal(t, opts.IsAdmin.ValueOrZeroValue(), user.IsAdmin)
assert.Equal(t, opts.Visibility.ValueOrZeroValue(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.ValueOrZeroValue(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.ValueOrZeroValue(), user.Language)
assert.Equal(t, opts.Theme.ValueOrZeroValue(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.ValueOrZeroValue(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.ValueOrZeroValue(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.ValueOrZeroValue(), user.EmailNotificationsPreference)
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 28})
assert.Equal(t, opts.KeepEmailPrivate.Value(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.Value(), user.FullName)
assert.Equal(t, opts.Website.Value(), user.Website)
assert.Equal(t, opts.Location.Value(), user.Location)
assert.Equal(t, opts.Description.Value(), user.Description)
assert.Equal(t, opts.AllowGitHook.Value(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.Value(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.Value(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.Value(), user.IsRestricted)
assert.Equal(t, opts.IsActive.Value(), user.IsActive)
assert.Equal(t, opts.IsAdmin.Value(), user.IsAdmin)
assert.Equal(t, opts.Visibility.Value(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.Value(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.Value(), user.Language)
assert.Equal(t, opts.Theme.Value(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.Value(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.Value(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.Value(), user.EmailNotificationsPreference)
assert.Equal(t, opts.KeepEmailPrivate.ValueOrZeroValue(), user.KeepEmailPrivate)
assert.Equal(t, opts.FullName.ValueOrZeroValue(), user.FullName)
assert.Equal(t, opts.Website.ValueOrZeroValue(), user.Website)
assert.Equal(t, opts.Location.ValueOrZeroValue(), user.Location)
assert.Equal(t, opts.Description.ValueOrZeroValue(), user.Description)
assert.Equal(t, opts.AllowGitHook.ValueOrZeroValue(), user.AllowGitHook)
assert.Equal(t, opts.AllowImportLocal.ValueOrZeroValue(), user.AllowImportLocal)
assert.Equal(t, opts.MaxRepoCreation.ValueOrZeroValue(), user.MaxRepoCreation)
assert.Equal(t, opts.IsRestricted.ValueOrZeroValue(), user.IsRestricted)
assert.Equal(t, opts.IsActive.ValueOrZeroValue(), user.IsActive)
assert.Equal(t, opts.IsAdmin.ValueOrZeroValue(), user.IsAdmin)
assert.Equal(t, opts.Visibility.ValueOrZeroValue(), user.Visibility)
assert.Equal(t, opts.KeepActivityPrivate.ValueOrZeroValue(), user.KeepActivityPrivate)
assert.Equal(t, opts.Language.ValueOrZeroValue(), user.Language)
assert.Equal(t, opts.Theme.ValueOrZeroValue(), user.Theme)
assert.Equal(t, opts.DiffViewStyle.ValueOrZeroValue(), user.DiffViewStyle)
assert.Equal(t, opts.AllowCreateOrganization.ValueOrZeroValue(), user.AllowCreateOrganization)
assert.Equal(t, opts.EmailNotificationsPreference.ValueOrZeroValue(), user.EmailNotificationsPreference)
}
func TestUpdateAuth(t *testing.T) {

View file

@ -1,9 +1,9 @@
<div class="ui secondary filter menu">
{{if not .Repository.IsArchived}}
<!-- Action Button -->
{{if and .IsShowClosed.Has .IsShowClosed.Value}}
{{if and .IsShowClosed.Has .IsShowClosed.ValueOrZeroValue}}
<button class="ui primary basic button issue-action" data-action="open" data-url="{{$.RepoLink}}/issues/status">{{ctx.Locale.Tr "repo.issues.action_open"}}</button>
{{else if and .IsShowClosed.Has (not .IsShowClosed.Value)}}
{{else if and .IsShowClosed.Has (not .IsShowClosed.ValueOrZeroValue)}}
<button class="ui red basic button issue-action" data-action="close" data-url="{{$.RepoLink}}/issues/status">{{ctx.Locale.Tr "repo.issues.action_close"}}</button>
{{end}}
{{if $.IsRepoAdmin}}

View file

@ -20,19 +20,19 @@
<div class="menu">
<label class="item"><input type="radio" name="clear-filter"> {{ctx.Locale.Tr "filter.clear"}}</label>
<div class="divider"></div>
<label class="item"><input type="radio" name="archived" {{if .IsArchived.Value}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_archived"}}</label>
<label class="item"><input type="radio" name="archived" {{if .IsArchived.ValueOrZeroValue}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_archived"}}</label>
<label class="item"><input type="radio" name="archived" {{if (not (.IsArchived.ValueOrDefault true))}}checked{{end}} value="0"> {{ctx.Locale.Tr "filter.not_archived"}}</label>
<div class="divider"></div>
<label class="item"><input type="radio" name="fork" {{if .IsFork.Value}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_fork"}}</label>
<label class="item"><input type="radio" name="fork" {{if .IsFork.ValueOrZeroValue}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_fork"}}</label>
<label class="item"><input type="radio" name="fork" {{if (not (.IsFork.ValueOrDefault true))}}checked{{end}} value="0"> {{ctx.Locale.Tr "filter.not_fork"}}</label>
<div class="divider"></div>
<label class="item"><input type="radio" name="mirror" {{if .IsMirror.Value}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_mirror"}}</label>
<label class="item"><input type="radio" name="mirror" {{if .IsMirror.ValueOrZeroValue}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_mirror"}}</label>
<label class="item"><input type="radio" name="mirror" {{if (not (.IsMirror.ValueOrDefault true))}}checked{{end}} value="0"> {{ctx.Locale.Tr "filter.not_mirror"}}</label>
<div class="divider"></div>
<label class="item"><input type="radio" name="template" {{if .IsTemplate.Value}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_template"}}</label>
<label class="item"><input type="radio" name="template" {{if .IsTemplate.ValueOrZeroValue}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.is_template"}}</label>
<label class="item"><input type="radio" name="template" {{if (not (.IsTemplate.ValueOrDefault true))}}checked{{end}} value="0"> {{ctx.Locale.Tr "filter.not_template"}}</label>
<div class="divider"></div>
<label class="item"><input type="radio" name="private" {{if .IsPrivate.Value}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.private"}}</label>
<label class="item"><input type="radio" name="private" {{if .IsPrivate.ValueOrZeroValue}}checked{{end}} value="1"> {{ctx.Locale.Tr "filter.private"}}</label>
<label class="item"><input type="radio" name="private" {{if (not (.IsPrivate.ValueOrDefault true))}}checked{{end}} value="0"> {{ctx.Locale.Tr "filter.public"}}</label>
</div>
</div>

View file

@ -404,15 +404,15 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts
// Not using opts.Name.ValueOrDefault() here to avoid unnecessarily
// generating an UUID when a name is specified.
var repoName string
if opts.Name.Has() {
repoName = opts.Name.Value()
if has, value := opts.Name.Get(); has {
repoName = value
} else {
repoName = uuid.NewString()
}
var autoInit bool
if opts.AutoInit.Has() {
autoInit = opts.AutoInit.Value()
if has, value := opts.AutoInit.Get(); has {
autoInit = value
} else {
autoInit = true
}
@ -426,22 +426,21 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts
License: "WTFPL",
Readme: "Default",
DefaultBranch: "main",
IsTemplate: opts.IsTemplate.Value(),
ObjectFormatName: opts.ObjectFormat.Value(),
IsPrivate: opts.IsPrivate.Value(),
IsTemplate: opts.IsTemplate.ValueOrZeroValue(),
ObjectFormatName: opts.ObjectFormat.ValueOrZeroValue(),
IsPrivate: opts.IsPrivate.ValueOrZeroValue(),
})
require.NoError(t, err)
assert.NotEmpty(t, repo)
// Populate `enabledUnits` if we have any enabled.
var enabledUnits []repo_model.RepoUnit
if opts.EnabledUnits.Has() {
units := opts.EnabledUnits.Value()
if has, units := opts.EnabledUnits.Get(); has {
enabledUnits = make([]repo_model.RepoUnit, len(units))
for i, unitType := range units {
var config convert.Conversion
if cfg, ok := opts.UnitConfig.Value()[unitType]; ok {
if cfg, ok := opts.UnitConfig.ValueOrZeroValue()[unitType]; ok {
config = cfg
}
enabledUnits[i] = repo_model.RepoUnit{
@ -460,9 +459,8 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts
// Add files, if any.
var sha string
if opts.Files.Has() {
if has, files := opts.Files.Get(); has {
assert.True(t, autoInit, "Files cannot be specified if AutoInit is disabled")
files := opts.Files.Value()
commitID, err := gitrepo.GetBranchCommitID(git.DefaultContext, repo, "main")
require.NoError(t, err)
@ -493,9 +491,9 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts
}
// If there's a Wiki branch specified, create a wiki, and a default wiki page.
if opts.WikiBranch.Has() {
if has, value := opts.WikiBranch.Get(); has {
// Set the wiki branch in the database first
repo.WikiBranch = opts.WikiBranch.Value()
repo.WikiBranch = value
err := repo_model.UpdateRepositoryCols(db.DefaultContext, repo, "wiki_branch")
require.NoError(t, err)