1
1
mirror of https://github.com/go-gitea/gitea synced 2024-12-22 16:44:26 +00:00

migrate some more "OptionalBool" to "Option[bool]" (#29479)

just some refactoring bits towards replacing **util.OptionalBool** with
**optional.Option[bool]**

---------

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
This commit is contained in:
6543 2024-02-29 19:52:49 +01:00 committed by GitHub
parent c7dcb58b1d
commit f6656181e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 183 additions and 174 deletions

View File

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -125,11 +126,11 @@ type SearchRepoOptions struct {
// None -> include public and private // None -> include public and private
// True -> include just private // True -> include just private
// False -> include just public // False -> include just public
IsPrivate util.OptionalBool IsPrivate optional.Option[bool]
// None -> include collaborative AND non-collaborative // None -> include collaborative AND non-collaborative
// True -> include just collaborative // True -> include just collaborative
// False -> include just non-collaborative // False -> include just non-collaborative
Collaborate util.OptionalBool Collaborate optional.Option[bool]
// What type of unit the user can be collaborative in, // What type of unit the user can be collaborative in,
// it is ignored if Collaborate is False. // it is ignored if Collaborate is False.
// TypeInvalid means any unit type. // TypeInvalid means any unit type.
@ -137,19 +138,19 @@ type SearchRepoOptions struct {
// None -> include forks AND non-forks // None -> include forks AND non-forks
// True -> include just forks // True -> include just forks
// False -> include just non-forks // False -> include just non-forks
Fork util.OptionalBool Fork optional.Option[bool]
// None -> include templates AND non-templates // None -> include templates AND non-templates
// True -> include just templates // True -> include just templates
// False -> include just non-templates // False -> include just non-templates
Template util.OptionalBool Template optional.Option[bool]
// None -> include mirrors AND non-mirrors // None -> include mirrors AND non-mirrors
// True -> include just mirrors // True -> include just mirrors
// False -> include just non-mirrors // False -> include just non-mirrors
Mirror util.OptionalBool Mirror optional.Option[bool]
// None -> include archived AND non-archived // None -> include archived AND non-archived
// True -> include just archived // True -> include just archived
// False -> include just non-archived // False -> include just non-archived
Archived util.OptionalBool Archived optional.Option[bool]
// only search topic name // only search topic name
TopicOnly bool TopicOnly bool
// only search repositories with specified primary language // only search repositories with specified primary language
@ -159,7 +160,7 @@ type SearchRepoOptions struct {
// None -> include has milestones AND has no milestone // None -> include has milestones AND has no milestone
// True -> include just has milestones // True -> include just has milestones
// False -> include just has no milestone // False -> include just has no milestone
HasMilestones util.OptionalBool HasMilestones optional.Option[bool]
// LowerNames represents valid lower names to restrict to // LowerNames represents valid lower names to restrict to
LowerNames []string LowerNames []string
// When specified true, apply some filters over the conditions: // When specified true, apply some filters over the conditions:
@ -359,12 +360,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
))) )))
} }
if opts.IsPrivate != util.OptionalBoolNone { if opts.IsPrivate.Has() {
cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()}) cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.Value()})
} }
if opts.Template != util.OptionalBoolNone { if opts.Template.Has() {
cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue}) cond = cond.And(builder.Eq{"is_template": opts.Template.Value()})
} }
// Restrict to starred repositories // Restrict to starred repositories
@ -380,11 +381,11 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
// Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
if opts.OwnerID > 0 { if opts.OwnerID > 0 {
accessCond := builder.NewCond() accessCond := builder.NewCond()
if opts.Collaborate != util.OptionalBoolTrue { if !opts.Collaborate.Value() {
accessCond = builder.Eq{"owner_id": opts.OwnerID} accessCond = builder.Eq{"owner_id": opts.OwnerID}
} }
if opts.Collaborate != util.OptionalBoolFalse { if opts.Collaborate.ValueOrDefault(true) {
// A Collaboration is: // A Collaboration is:
collaborateCond := builder.NewCond() collaborateCond := builder.NewCond()
@ -472,32 +473,33 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true}))) Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
} }
if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant { if opts.Fork.Has() || opts.OnlyShowRelevant {
if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone { if opts.OnlyShowRelevant && !opts.Fork.Has() {
cond = cond.And(builder.Eq{"is_fork": false}) cond = cond.And(builder.Eq{"is_fork": false})
} else { } else {
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue}) cond = cond.And(builder.Eq{"is_fork": opts.Fork.Value()})
} }
} }
if opts.Mirror != util.OptionalBoolNone { if opts.Mirror.Has() {
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue}) cond = cond.And(builder.Eq{"is_mirror": opts.Mirror.Value()})
} }
if opts.Actor != nil && opts.Actor.IsRestricted { if opts.Actor != nil && opts.Actor.IsRestricted {
cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid)) cond = cond.And(AccessibleRepositoryCondition(opts.Actor, unit.TypeInvalid))
} }
if opts.Archived != util.OptionalBoolNone { if opts.Archived.Has() {
cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue}) cond = cond.And(builder.Eq{"is_archived": opts.Archived.Value()})
} }
switch opts.HasMilestones { if opts.HasMilestones.Has() {
case util.OptionalBoolTrue: if opts.HasMilestones.Value() {
cond = cond.And(builder.Gt{"num_milestones": 0}) cond = cond.And(builder.Gt{"num_milestones": 0})
case util.OptionalBoolFalse: } else {
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"})) cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
} }
}
if opts.OnlyShowRelevant { if opts.OnlyShowRelevant {
// Only show a repo that has at least a topic, an icon, or a description // Only show a repo that has at least a topic, an icon, or a description

View File

@ -10,7 +10,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/optional"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -27,62 +27,62 @@ func getTestCases() []struct {
}{ }{
{ {
name: "PublicRepositoriesByName", name: "PublicRepositoriesByName",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, Collaborate: optional.Some(false)},
count: 7, count: 7,
}, },
{ {
name: "PublicAndPrivateRepositoriesByName", name: "PublicAndPrivateRepositoriesByName",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage", name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage", name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage", name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage", name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "PublicRepositoriesOfUser", name: "PublicRepositoriesOfUser",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: optional.Some(false)},
count: 2, count: 2,
}, },
{ {
name: "PublicRepositoriesOfUser2", name: "PublicRepositoriesOfUser2",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: optional.Some(false)},
count: 0, count: 0,
}, },
{ {
name: "PublicRepositoriesOfOrg3", name: "PublicRepositoriesOfOrg3",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: optional.Some(false)},
count: 2, count: 2,
}, },
{ {
name: "PublicAndPrivateRepositoriesOfUser", name: "PublicAndPrivateRepositoriesOfUser",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: optional.Some(false)},
count: 4, count: 4,
}, },
{ {
name: "PublicAndPrivateRepositoriesOfUser2", name: "PublicAndPrivateRepositoriesOfUser2",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: optional.Some(false)},
count: 0, count: 0,
}, },
{ {
name: "PublicAndPrivateRepositoriesOfOrg3", name: "PublicAndPrivateRepositoriesOfOrg3",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: optional.Some(false)},
count: 4, count: 4,
}, },
{ {
@ -117,32 +117,32 @@ func getTestCases() []struct {
}, },
{ {
name: "PublicRepositoriesOfOrganization", name: "PublicRepositoriesOfOrganization",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: optional.Some(false)},
count: 1, count: 1,
}, },
{ {
name: "PublicAndPrivateRepositoriesOfOrganization", name: "PublicAndPrivateRepositoriesOfOrganization",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: optional.Some(false)},
count: 2, count: 2,
}, },
{ {
name: "AllPublic/PublicRepositoriesByName", name: "AllPublic/PublicRepositoriesByName",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{PageSize: 10}, AllPublic: true, Collaborate: optional.Some(false)},
count: 7, count: 7,
}, },
{ {
name: "AllPublic/PublicAndPrivateRepositoriesByName", name: "AllPublic/PublicAndPrivateRepositoriesByName",
opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{Keyword: "big_test_", ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: optional.Some(false)},
count: 14, count: 14,
}, },
{ {
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: optional.Some(false)},
count: 33, count: 33,
}, },
{ {
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: optional.Some(false)},
count: 38, count: 38,
}, },
{ {
@ -157,12 +157,12 @@ func getTestCases() []struct {
}, },
{ {
name: "AllPublic/PublicRepositoriesOfOrganization", name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: optional.Some(false), Template: optional.Some(false)},
count: 33, count: 33,
}, },
{ {
name: "AllTemplates", name: "AllTemplates",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, Template: optional.Some(true)},
count: 2, count: 2,
}, },
{ {
@ -190,7 +190,7 @@ func TestSearchRepository(t *testing.T) {
PageSize: 10, PageSize: 10,
}, },
Keyword: "repo_12", Keyword: "repo_12",
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -205,7 +205,7 @@ func TestSearchRepository(t *testing.T) {
PageSize: 10, PageSize: 10,
}, },
Keyword: "test_repo", Keyword: "test_repo",
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -220,7 +220,7 @@ func TestSearchRepository(t *testing.T) {
}, },
Keyword: "repo_13", Keyword: "repo_13",
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -236,7 +236,7 @@ func TestSearchRepository(t *testing.T) {
}, },
Keyword: "test_repo", Keyword: "test_repo",
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
assert.NoError(t, err) assert.NoError(t, err)
@ -257,7 +257,7 @@ func TestSearchRepository(t *testing.T) {
PageSize: 10, PageSize: 10,
}, },
Keyword: "description_14", Keyword: "description_14",
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
IncludeDescription: true, IncludeDescription: true,
}) })
@ -274,7 +274,7 @@ func TestSearchRepository(t *testing.T) {
PageSize: 10, PageSize: 10,
}, },
Keyword: "description_14", Keyword: "description_14",
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
IncludeDescription: false, IncludeDescription: false,
}) })
@ -327,30 +327,25 @@ func TestSearchRepository(t *testing.T) {
assert.False(t, repo.IsPrivate) assert.False(t, repo.IsPrivate)
} }
if testCase.opts.Fork == util.OptionalBoolTrue && testCase.opts.Mirror == util.OptionalBoolTrue { if testCase.opts.Fork.Value() && testCase.opts.Mirror.Value() {
assert.True(t, repo.IsFork || repo.IsMirror) assert.True(t, repo.IsFork && repo.IsMirror)
} else { } else {
switch testCase.opts.Fork { if testCase.opts.Fork.Has() {
case util.OptionalBoolFalse: assert.Equal(t, testCase.opts.Fork.Value(), repo.IsFork)
assert.False(t, repo.IsFork)
case util.OptionalBoolTrue:
assert.True(t, repo.IsFork)
} }
switch testCase.opts.Mirror { if testCase.opts.Mirror.Has() {
case util.OptionalBoolFalse: assert.Equal(t, testCase.opts.Mirror.Value(), repo.IsMirror)
assert.False(t, repo.IsMirror)
case util.OptionalBoolTrue:
assert.True(t, repo.IsMirror)
} }
} }
if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic { if testCase.opts.OwnerID > 0 && !testCase.opts.AllPublic {
switch testCase.opts.Collaborate { if testCase.opts.Collaborate.Has() {
case util.OptionalBoolFalse: if testCase.opts.Collaborate.Value() {
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
case util.OptionalBoolTrue:
assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID) assert.NotEqual(t, testCase.opts.OwnerID, repo.Owner.ID)
} else {
assert.Equal(t, testCase.opts.OwnerID, repo.Owner.ID)
}
} }
} }
} }

View File

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
@ -416,8 +417,8 @@ type SearchEmailOptions struct {
db.ListOptions db.ListOptions
Keyword string Keyword string
SortType SearchEmailOrderBy SortType SearchEmailOrderBy
IsPrimary util.OptionalBool IsPrimary optional.Option[bool]
IsActivated util.OptionalBool IsActivated optional.Option[bool]
} }
// SearchEmailResult is an e-mail address found in the user or email_address table // SearchEmailResult is an e-mail address found in the user or email_address table
@ -444,18 +445,12 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
)) ))
} }
switch { if opts.IsPrimary.Has() {
case opts.IsPrimary.IsTrue(): cond = cond.And(builder.Eq{"email_address.is_primary": opts.IsPrimary.Value()})
cond = cond.And(builder.Eq{"email_address.is_primary": true})
case opts.IsPrimary.IsFalse():
cond = cond.And(builder.Eq{"email_address.is_primary": false})
} }
switch { if opts.IsActivated.Has() {
case opts.IsActivated.IsTrue(): cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()})
cond = cond.And(builder.Eq{"email_address.is_activated": true})
case opts.IsActivated.IsFalse():
cond = cond.And(builder.Eq{"email_address.is_activated": false})
} }
count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid"). count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid").

View File

@ -9,7 +9,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/optional"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -128,14 +128,14 @@ func TestListEmails(t *testing.T) {
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 27 })) assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.UID == 27 }))
// Must find only primary addresses (i.e. from the `user` table) // Must find only primary addresses (i.e. from the `user` table)
opts = &user_model.SearchEmailOptions{IsPrimary: util.OptionalBoolTrue} opts = &user_model.SearchEmailOptions{IsPrimary: optional.Some(true)}
emails, _, err = user_model.SearchEmails(db.DefaultContext, opts) emails, _, err = user_model.SearchEmails(db.DefaultContext, opts)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsPrimary })) assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return s.IsPrimary }))
assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsPrimary })) assert.False(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsPrimary }))
// Must find only inactive addresses (i.e. not validated) // Must find only inactive addresses (i.e. not validated)
opts = &user_model.SearchEmailOptions{IsActivated: util.OptionalBoolFalse} opts = &user_model.SearchEmailOptions{IsActivated: optional.Some(false)}
emails, _, err = user_model.SearchEmails(db.DefaultContext, opts) emails, _, err = user_model.SearchEmails(db.DefaultContext, opts)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsActivated })) assert.True(t, contains(func(s *user_model.SearchEmailResult) bool { return !s.IsActivated }))

View File

@ -10,8 +10,8 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm" "xorm.io/xorm"
@ -33,11 +33,11 @@ type SearchUserOptions struct {
SupportedSortOrders container.Set[string] // if not nil, only allow to use the sort orders in this set SupportedSortOrders container.Set[string] // if not nil, only allow to use the sort orders in this set
IsActive util.OptionalBool IsActive optional.Option[bool]
IsAdmin util.OptionalBool IsAdmin optional.Option[bool]
IsRestricted util.OptionalBool IsRestricted optional.Option[bool]
IsTwoFactorEnabled util.OptionalBool IsTwoFactorEnabled optional.Option[bool]
IsProhibitLogin util.OptionalBool IsProhibitLogin optional.Option[bool]
IncludeReserved bool IncludeReserved bool
ExtraParamStrings map[string]string ExtraParamStrings map[string]string
@ -89,24 +89,24 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
cond = cond.And(builder.Eq{"login_name": opts.LoginName}) cond = cond.And(builder.Eq{"login_name": opts.LoginName})
} }
if !opts.IsActive.IsNone() { if opts.IsActive.Has() {
cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) cond = cond.And(builder.Eq{"is_active": opts.IsActive.Value()})
} }
if !opts.IsAdmin.IsNone() { if opts.IsAdmin.Has() {
cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()}) cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.Value()})
} }
if !opts.IsRestricted.IsNone() { if opts.IsRestricted.Has() {
cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.IsTrue()}) cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.Value()})
} }
if !opts.IsProhibitLogin.IsNone() { if opts.IsProhibitLogin.Has() {
cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.IsTrue()}) cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.Value()})
} }
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
if opts.IsTwoFactorEnabled.IsNone() { if !opts.IsTwoFactorEnabled.Has() {
return e.Where(cond) return e.Where(cond)
} }
@ -114,7 +114,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
// While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed. // While using LEFT JOIN, sometimes the performance might not be good, but it won't be a problem now, such SQL is seldom executed.
// There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now): // There are some possible methods to refactor this SQL in future when we really need to optimize the performance (but not now):
// (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch) // (1) add a column in user table (2) add a setting value in user_setting table (3) use search engines (bleve/elasticsearch)
if opts.IsTwoFactorEnabled.IsTrue() { if opts.IsTwoFactorEnabled.Value() {
cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL")) cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL"))
} else { } else {
cond = cond.And(builder.Expr("two_factor.uid IS NULL")) cond = cond.And(builder.Expr("two_factor.uid IS NULL"))
@ -131,7 +131,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
defer sessCount.Close() defer sessCount.Close()
count, err := sessCount.Count(new(User)) count, err := sessCount.Count(new(User))
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("Count: %w", err) return nil, 0, fmt.Errorf("count: %w", err)
} }
if len(opts.OrderBy) == 0 { if len(opts.OrderBy) == 0 {

View File

@ -16,10 +16,10 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -103,29 +103,29 @@ func TestSearchUsers(t *testing.T) {
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse}, testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
[]int64{9}) []int64{9})
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
// order by name asc default // order by name asc default
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
[]int64{1}) []int64{1})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
[]int64{29}) []int64{29})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
[]int64{37}) []int64{37})
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: util.OptionalBoolTrue}, testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
[]int64{24}) []int64{24})
} }

View File

@ -20,10 +20,10 @@ import (
"code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/indexer/issues/internal"
"code.gitea.io/gitea/modules/indexer/issues/meilisearch" "code.gitea.io/gitea/modules/indexer/issues/meilisearch"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
) )
// IndexerMetadata is used to send data to the queue, so it contains only the ids. // IndexerMetadata is used to send data to the queue, so it contains only the ids.
@ -220,7 +220,7 @@ func PopulateIssueIndexer(ctx context.Context) error {
ListOptions: db_model.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize}, ListOptions: db_model.ListOptions{Page: page, PageSize: repo_model.RepositoryListDefaultPageSize},
OrderBy: db_model.SearchOrderByID, OrderBy: db_model.SearchOrderByID,
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
if err != nil { if err != nil {
log.Error("SearchRepositoryByName: %v", err) log.Error("SearchRepositoryByName: %v", err)

View File

@ -27,6 +27,16 @@ func TestOption(t *testing.T) {
assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.Value())
assert.Equal(t, int(1), some.ValueOrDefault(2)) assert.Equal(t, int(1), some.ValueOrDefault(2))
noneBool := optional.None[bool]()
assert.False(t, noneBool.Has())
assert.False(t, noneBool.Value())
assert.True(t, noneBool.ValueOrDefault(true))
someBool := optional.Some(true)
assert.True(t, someBool.Has())
assert.True(t, someBool.Value())
assert.True(t, someBool.ValueOrDefault(false))
var ptr *int var ptr *int
assert.False(t, optional.FromPtr(ptr).Has()) assert.False(t, optional.FromPtr(ptr).Has())

View File

@ -68,13 +68,13 @@ func OptionalBoolOf(b bool) OptionalBool {
return OptionalBoolFalse return OptionalBoolFalse
} }
// OptionalBoolParse get the corresponding OptionalBool of a string using strconv.ParseBool // OptionalBoolParse get the corresponding optional.Option[bool] of a string using strconv.ParseBool
func OptionalBoolParse(s string) OptionalBool { func OptionalBoolParse(s string) optional.Option[bool] {
b, e := strconv.ParseBool(s) v, e := strconv.ParseBool(s)
if e != nil { if e != nil {
return OptionalBoolNone return optional.None[bool]()
} }
return OptionalBoolOf(b) return optional.Some(v)
} }
// IsEmptyString checks if the provided string is empty // IsEmptyString checks if the provided string is empty

View File

@ -8,6 +8,8 @@ import (
"strings" "strings"
"testing" "testing"
"code.gitea.io/gitea/modules/optional"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -173,17 +175,17 @@ func Test_RandomBytes(t *testing.T) {
assert.NotEqual(t, bytes3, bytes4) assert.NotEqual(t, bytes3, bytes4)
} }
func Test_OptionalBool(t *testing.T) { func TestOptionalBoolParse(t *testing.T) {
assert.Equal(t, OptionalBoolNone, OptionalBoolParse("")) assert.Equal(t, optional.None[bool](), OptionalBoolParse(""))
assert.Equal(t, OptionalBoolNone, OptionalBoolParse("x")) assert.Equal(t, optional.None[bool](), OptionalBoolParse("x"))
assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("0")) assert.Equal(t, optional.Some(false), OptionalBoolParse("0"))
assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("f")) assert.Equal(t, optional.Some(false), OptionalBoolParse("f"))
assert.Equal(t, OptionalBoolFalse, OptionalBoolParse("False")) assert.Equal(t, optional.Some(false), OptionalBoolParse("False"))
assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("1")) assert.Equal(t, optional.Some(true), OptionalBoolParse("1"))
assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("t")) assert.Equal(t, optional.Some(true), OptionalBoolParse("t"))
assert.Equal(t, OptionalBoolTrue, OptionalBoolParse("True")) assert.Equal(t, optional.Some(true), OptionalBoolParse("True"))
} }
// Test case for any function which accepts and returns a single string. // Test case for any function which accepts and returns a single string.

View File

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues" issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
@ -142,7 +143,7 @@ func SearchIssues(ctx *context.APIContext) {
Private: false, Private: false,
AllPublic: true, AllPublic: true,
TopicOnly: false, TopicOnly: false,
Collaborate: util.OptionalBoolNone, Collaborate: optional.None[bool](),
// This needs to be a column that is not nil in fixtures or // This needs to be a column that is not nil in fixtures or
// MySQL will return different results when sorting by null in some cases // MySQL will return different results when sorting by null in some cases
OrderBy: db.SearchOrderByAlphabetically, OrderBy: db.SearchOrderByAlphabetically,
@ -165,7 +166,7 @@ func SearchIssues(ctx *context.APIContext) {
opts.OwnerID = owner.ID opts.OwnerID = owner.ID
opts.AllLimited = false opts.AllLimited = false
opts.AllPublic = false opts.AllPublic = false
opts.Collaborate = util.OptionalBoolFalse opts.Collaborate = optional.Some(false)
} }
if ctx.FormString("team") != "" { if ctx.FormString("team") != "" {
if ctx.FormString("owner") == "" { if ctx.FormString("owner") == "" {

View File

@ -24,10 +24,10 @@ import (
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/api/v1/utils"
@ -135,33 +135,33 @@ func Search(ctx *context.APIContext) {
PriorityOwnerID: ctx.FormInt64("priority_owner_id"), PriorityOwnerID: ctx.FormInt64("priority_owner_id"),
TeamID: ctx.FormInt64("team_id"), TeamID: ctx.FormInt64("team_id"),
TopicOnly: ctx.FormBool("topic"), TopicOnly: ctx.FormBool("topic"),
Collaborate: util.OptionalBoolNone, Collaborate: optional.None[bool](),
Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")),
Template: util.OptionalBoolNone, Template: optional.None[bool](),
StarredByID: ctx.FormInt64("starredBy"), StarredByID: ctx.FormInt64("starredBy"),
IncludeDescription: ctx.FormBool("includeDesc"), IncludeDescription: ctx.FormBool("includeDesc"),
} }
if ctx.FormString("template") != "" { if ctx.FormString("template") != "" {
opts.Template = util.OptionalBoolOf(ctx.FormBool("template")) opts.Template = optional.Some(ctx.FormBool("template"))
} }
if ctx.FormBool("exclusive") { if ctx.FormBool("exclusive") {
opts.Collaborate = util.OptionalBoolFalse opts.Collaborate = optional.Some(false)
} }
mode := ctx.FormString("mode") mode := ctx.FormString("mode")
switch mode { switch mode {
case "source": case "source":
opts.Fork = util.OptionalBoolFalse opts.Fork = optional.Some(false)
opts.Mirror = util.OptionalBoolFalse opts.Mirror = optional.Some(false)
case "fork": case "fork":
opts.Fork = util.OptionalBoolTrue opts.Fork = optional.Some(true)
case "mirror": case "mirror":
opts.Mirror = util.OptionalBoolTrue opts.Mirror = optional.Some(true)
case "collaborative": case "collaborative":
opts.Mirror = util.OptionalBoolFalse opts.Mirror = optional.Some(false)
opts.Collaborate = util.OptionalBoolTrue opts.Collaborate = optional.Some(true)
case "": case "":
default: default:
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode)) ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode))
@ -169,11 +169,11 @@ func Search(ctx *context.APIContext) {
} }
if ctx.FormString("archived") != "" { if ctx.FormString("archived") != "" {
opts.Archived = util.OptionalBoolOf(ctx.FormBool("archived")) opts.Archived = optional.Some(ctx.FormBool("archived"))
} }
if ctx.FormString("is_private") != "" { if ctx.FormString("is_private") != "" {
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) opts.IsPrivate = optional.Some(ctx.FormBool("is_private"))
} }
sortMode := ctx.FormString("sort") sortMode := ctx.FormString("sort")

View File

@ -12,8 +12,8 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
@ -68,10 +68,10 @@ func Emails(ctx *context.Context) {
opts.Keyword = ctx.FormTrim("q") opts.Keyword = ctx.FormTrim("q")
opts.SortType = orderBy opts.SortType = orderBy
if len(ctx.FormString("is_activated")) != 0 { if len(ctx.FormString("is_activated")) != 0 {
opts.IsActivated = util.OptionalBoolOf(ctx.FormBool("activated")) opts.IsActivated = optional.Some(ctx.FormBool("activated"))
} }
if len(ctx.FormString("is_primary")) != 0 { if len(ctx.FormString("is_primary")) != 0 {
opts.IsPrimary = util.OptionalBoolOf(ctx.FormBool("primary")) opts.IsPrimary = optional.Some(ctx.FormBool("primary"))
} }
if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {

View File

@ -276,7 +276,7 @@ func ViewUser(ctx *context.Context) {
OwnerID: u.ID, OwnerID: u.ID,
OrderBy: db.SearchOrderByAlphabetically, OrderBy: db.SearchOrderByAlphabetically,
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
}) })
if err != nil { if err != nil {
ctx.ServerError("SearchRepository", err) ctx.ServerError("SearchRepository", err)

View File

@ -12,10 +12,10 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/sitemap"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
@ -155,7 +155,7 @@ func Users(ctx *context.Context) {
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum}, ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
IsActive: util.OptionalBoolTrue, IsActive: optional.Some(true),
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
SupportedSortOrders: supportedSortOrders, SupportedSortOrders: supportedSortOrders,

View File

@ -13,10 +13,10 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap" "code.gitea.io/gitea/modules/sitemap"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers/web/auth" "code.gitea.io/gitea/routers/web/auth"
"code.gitea.io/gitea/routers/web/user" "code.gitea.io/gitea/routers/web/user"
@ -71,7 +71,7 @@ func HomeSitemap(ctx *context.Context) {
_, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ _, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
ListOptions: db.ListOptions{PageSize: 1}, ListOptions: db.ListOptions{PageSize: 1},
IsActive: util.OptionalBoolTrue, IsActive: optional.Some(true),
Visible: []structs.VisibleType{structs.VisibleTypePublic}, Visible: []structs.VisibleType{structs.VisibleTypePublic},
}) })
if err != nil { if err != nil {

View File

@ -38,6 +38,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
@ -2519,7 +2520,7 @@ func SearchIssues(ctx *context.Context) {
Private: false, Private: false,
AllPublic: true, AllPublic: true,
TopicOnly: false, TopicOnly: false,
Collaborate: util.OptionalBoolNone, Collaborate: optional.None[bool](),
// This needs to be a column that is not nil in fixtures or // This needs to be a column that is not nil in fixtures or
// MySQL will return different results when sorting by null in some cases // MySQL will return different results when sorting by null in some cases
OrderBy: db.SearchOrderByAlphabetically, OrderBy: db.SearchOrderByAlphabetically,
@ -2542,7 +2543,7 @@ func SearchIssues(ctx *context.Context) {
opts.OwnerID = owner.ID opts.OwnerID = owner.ID
opts.AllLimited = false opts.AllLimited = false
opts.AllPublic = false opts.AllPublic = false
opts.Collaborate = util.OptionalBoolFalse opts.Collaborate = optional.Some(false)
} }
if ctx.FormString("team") != "" { if ctx.FormString("team") != "" {
if ctx.FormString("owner") == "" { if ctx.FormString("owner") == "" {

View File

@ -553,33 +553,33 @@ func SearchRepo(ctx *context.Context) {
PriorityOwnerID: ctx.FormInt64("priority_owner_id"), PriorityOwnerID: ctx.FormInt64("priority_owner_id"),
TeamID: ctx.FormInt64("team_id"), TeamID: ctx.FormInt64("team_id"),
TopicOnly: ctx.FormBool("topic"), TopicOnly: ctx.FormBool("topic"),
Collaborate: util.OptionalBoolNone, Collaborate: optional.None[bool](),
Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")),
Template: util.OptionalBoolNone, Template: optional.None[bool](),
StarredByID: ctx.FormInt64("starredBy"), StarredByID: ctx.FormInt64("starredBy"),
IncludeDescription: ctx.FormBool("includeDesc"), IncludeDescription: ctx.FormBool("includeDesc"),
} }
if ctx.FormString("template") != "" { if ctx.FormString("template") != "" {
opts.Template = util.OptionalBoolOf(ctx.FormBool("template")) opts.Template = optional.Some(ctx.FormBool("template"))
} }
if ctx.FormBool("exclusive") { if ctx.FormBool("exclusive") {
opts.Collaborate = util.OptionalBoolFalse opts.Collaborate = optional.Some(false)
} }
mode := ctx.FormString("mode") mode := ctx.FormString("mode")
switch mode { switch mode {
case "source": case "source":
opts.Fork = util.OptionalBoolFalse opts.Fork = optional.Some(false)
opts.Mirror = util.OptionalBoolFalse opts.Mirror = optional.Some(false)
case "fork": case "fork":
opts.Fork = util.OptionalBoolTrue opts.Fork = optional.Some(true)
case "mirror": case "mirror":
opts.Mirror = util.OptionalBoolTrue opts.Mirror = optional.Some(true)
case "collaborative": case "collaborative":
opts.Mirror = util.OptionalBoolFalse opts.Mirror = optional.Some(false)
opts.Collaborate = util.OptionalBoolTrue opts.Collaborate = optional.Some(true)
case "": case "":
default: default:
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode)) ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Invalid search mode: \"%s\"", mode))
@ -587,11 +587,11 @@ func SearchRepo(ctx *context.Context) {
} }
if ctx.FormString("archived") != "" { if ctx.FormString("archived") != "" {
opts.Archived = util.OptionalBoolOf(ctx.FormBool("archived")) opts.Archived = optional.Some(ctx.FormBool("archived"))
} }
if ctx.FormString("is_private") != "" { if ctx.FormString("is_private") != "" {
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) opts.IsPrivate = optional.Some(ctx.FormBool("is_private"))
} }
sortMode := ctx.FormString("sort") sortMode := ctx.FormString("sort")

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
@ -114,7 +115,7 @@ func LoadHeaderCount(ctx *context.Context) error {
Actor: ctx.Doer, Actor: ctx.Doer,
OwnerID: ctx.ContextUser.ID, OwnerID: ctx.ContextUser.ID,
Private: ctx.IsSigned, Private: ctx.IsSigned,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
}) })
if err != nil { if err != nil {

View File

@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/routers/web/feed"
@ -161,8 +162,8 @@ func Milestones(ctx *context.Context) {
Private: true, Private: true,
AllPublic: false, // Include also all public repositories of users and public organisations AllPublic: false, // Include also all public repositories of users and public organisations
AllLimited: false, // Include also all public repositories of limited organisations AllLimited: false, // Include also all public repositories of limited organisations
Archived: util.OptionalBoolFalse, Archived: optional.Some(false),
HasMilestones: util.OptionalBoolTrue, // Just needs display repos has milestones HasMilestones: optional.Some(true), // Just needs display repos has milestones
} }
if ctxUser.IsOrganization() && ctx.Org.Team != nil { if ctxUser.IsOrganization() && ctx.Org.Team != nil {
@ -465,9 +466,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
Private: true, Private: true,
AllPublic: false, AllPublic: false,
AllLimited: false, AllLimited: false,
Collaborate: util.OptionalBoolNone, Collaborate: optional.None[bool](),
UnitType: unitType, UnitType: unitType,
Archived: util.OptionalBoolFalse, Archived: optional.Some(false),
} }
if team != nil { if team != nil {
repoOpts.TeamID = team.ID repoOpts.TeamID = team.ID

View File

@ -17,6 +17,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -399,7 +400,7 @@ func NotificationWatching(ctx *context.Context) {
OrderBy: orderBy, OrderBy: orderBy,
Private: ctx.IsSigned, Private: ctx.IsSigned,
WatchedByID: ctx.Doer.ID, WatchedByID: ctx.Doer.ID,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
TopicOnly: ctx.FormBool("topic"), TopicOnly: ctx.FormBool("topic"),
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
}) })

View File

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/routers/web/feed"
@ -203,7 +204,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
OrderBy: orderBy, OrderBy: orderBy,
Private: ctx.IsSigned, Private: ctx.IsSigned,
StarredByID: ctx.ContextUser.ID, StarredByID: ctx.ContextUser.ID,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
TopicOnly: topicOnly, TopicOnly: topicOnly,
Language: language, Language: language,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
@ -225,7 +226,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
OrderBy: orderBy, OrderBy: orderBy,
Private: ctx.IsSigned, Private: ctx.IsSigned,
WatchedByID: ctx.ContextUser.ID, WatchedByID: ctx.ContextUser.ID,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
TopicOnly: topicOnly, TopicOnly: topicOnly,
Language: language, Language: language,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
@ -270,7 +271,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
OwnerID: ctx.ContextUser.ID, OwnerID: ctx.ContextUser.ID,
OrderBy: orderBy, OrderBy: orderBy,
Private: ctx.IsSigned, Private: ctx.IsSigned,
Collaborate: util.OptionalBoolFalse, Collaborate: optional.Some(false),
TopicOnly: topicOnly, TopicOnly: topicOnly,
Language: language, Language: language,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,

View File

@ -8,7 +8,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/convert"
) )
@ -25,7 +24,7 @@ func Search(ctx *context.Context) {
Keyword: ctx.FormTrim("q"), Keyword: ctx.FormTrim("q"),
UID: ctx.FormInt64("uid"), UID: ctx.FormInt64("uid"),
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
IsActive: util.OptionalBoolFromGeneric(ctx.FormOptionalBool("active")), IsActive: ctx.FormOptionalBool("active"),
ListOptions: listOptions, ListOptions: listOptions,
}) })
if err != nil { if err != nil {