clean up scope production, cat comparators (#630)
Scope production was still using service-type specific factories instead of a generic factory set. This has been centralized so that all service instances share the same scope production concerns. Additionally, category comparator funcs now use generic comparators as well, which allows for the removal of the isType() and includesType() comparator funcs.
This commit is contained in:
parent
e7b863c444
commit
8d2a437f1d
@ -140,32 +140,12 @@ func (s *exchange) Include(scopes ...[]ExchangeScope) {
|
|||||||
|
|
||||||
// Scopes retrieves the list of exchangeScopes in the selector.
|
// Scopes retrieves the list of exchangeScopes in the selector.
|
||||||
func (s *exchange) Scopes() []ExchangeScope {
|
func (s *exchange) Scopes() []ExchangeScope {
|
||||||
scopes := s.scopes()
|
return scopes[ExchangeScope](s.Selector)
|
||||||
es := make([]ExchangeScope, len(scopes))
|
|
||||||
for i := range scopes {
|
|
||||||
es[i] = ExchangeScope(scopes[i])
|
|
||||||
}
|
|
||||||
return es
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Scope Factories
|
// Scope Factories
|
||||||
|
|
||||||
func makeExchangeScope(granularity string, cat exchangeCategory, vs []string) ExchangeScope {
|
|
||||||
return ExchangeScope{
|
|
||||||
scopeKeyGranularity: granularity,
|
|
||||||
scopeKeyCategory: cat.String(),
|
|
||||||
cat.String(): join(vs...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeExchangeUserScope(user, granularity string, cat exchangeCategory, vs []string) ExchangeScope {
|
|
||||||
es := makeExchangeScope(granularity, cat, vs).set(ExchangeUser, user)
|
|
||||||
es[scopeKeyResource] = user
|
|
||||||
es[scopeKeyDataType] = cat.leafType().String()
|
|
||||||
return es
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces one or more exchange contact scopes.
|
// Produces one or more exchange contact scopes.
|
||||||
// One scope is created per combination of users,folders,contacts.
|
// One scope is created per combination of users,folders,contacts.
|
||||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||||
@ -180,7 +160,7 @@ func (s *exchange) Contacts(users, folders, contacts []string) []ExchangeScope {
|
|||||||
for _, f := range folders {
|
for _, f := range folders {
|
||||||
scopes = append(
|
scopes = append(
|
||||||
scopes,
|
scopes,
|
||||||
makeExchangeUserScope(u, Item, ExchangeContact, contacts).set(ExchangeContactFolder, f),
|
makeScope[ExchangeScope](u, Item, ExchangeContact, contacts).set(ExchangeContactFolder, f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,7 +179,7 @@ func (s *exchange) ContactFolders(users, folders []string) []ExchangeScope {
|
|||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
scopes = append(
|
scopes = append(
|
||||||
scopes,
|
scopes,
|
||||||
makeExchangeUserScope(u, Group, ExchangeContactFolder, folders),
|
makeScope[ExchangeScope](u, Group, ExchangeContactFolder, folders),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
@ -217,7 +197,7 @@ func (s *exchange) Events(users, events []string) []ExchangeScope {
|
|||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
scopes = append(
|
scopes = append(
|
||||||
scopes,
|
scopes,
|
||||||
makeExchangeUserScope(u, Item, ExchangeEvent, events),
|
makeScope[ExchangeScope](u, Item, ExchangeEvent, events),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
@ -237,7 +217,7 @@ func (s *exchange) Mails(users, folders, mails []string) []ExchangeScope {
|
|||||||
for _, f := range folders {
|
for _, f := range folders {
|
||||||
scopes = append(
|
scopes = append(
|
||||||
scopes,
|
scopes,
|
||||||
makeExchangeUserScope(u, Item, ExchangeMail, mails).set(ExchangeMailFolder, f),
|
makeScope[ExchangeScope](u, Item, ExchangeMail, mails).set(ExchangeMailFolder, f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,7 +236,7 @@ func (s *exchange) MailFolders(users, folders []string) []ExchangeScope {
|
|||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
scopes = append(
|
scopes = append(
|
||||||
scopes,
|
scopes,
|
||||||
makeExchangeUserScope(u, Group, ExchangeMailFolder, folders),
|
makeScope[ExchangeScope](u, Group, ExchangeMailFolder, folders),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
@ -271,23 +251,15 @@ func (s *exchange) Users(users []string) []ExchangeScope {
|
|||||||
users = normalize(users)
|
users = normalize(users)
|
||||||
scopes := []ExchangeScope{}
|
scopes := []ExchangeScope{}
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
scopes = append(scopes, makeExchangeUserScope(u, Group, ExchangeContactFolder, Any()))
|
scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeContactFolder, Any()))
|
||||||
scopes = append(scopes, makeExchangeUserScope(u, Item, ExchangeEvent, Any()))
|
scopes = append(scopes, makeScope[ExchangeScope](u, Item, ExchangeEvent, Any()))
|
||||||
scopes = append(scopes, makeExchangeUserScope(u, Group, ExchangeMailFolder, Any()))
|
scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeMailFolder, Any()))
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeExchangeFilterScope(cat, filterCat exchangeCategory, vs []string) ExchangeScope {
|
// -------------------
|
||||||
return ExchangeScope{
|
// Filter Factories
|
||||||
scopeKeyGranularity: Filter,
|
|
||||||
scopeKeyCategory: cat.String(),
|
|
||||||
scopeKeyInfoFilter: filterCat.String(),
|
|
||||||
scopeKeyResource: Filter,
|
|
||||||
scopeKeyDataType: cat.leafType().String(),
|
|
||||||
filterCat.String(): join(vs...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces an exchange mail received-after filter scope.
|
// Produces an exchange mail received-after filter scope.
|
||||||
// Matches any mail which was received after the timestring.
|
// Matches any mail which was received after the timestring.
|
||||||
@ -295,7 +267,7 @@ func makeExchangeFilterScope(cat, filterCat exchangeCategory, vs []string) Excha
|
|||||||
// If the input is empty or selectors.None, the scope will always fail comparisons.
|
// If the input is empty or selectors.None, the scope will always fail comparisons.
|
||||||
func (sr *ExchangeRestore) MailReceivedAfter(timeStrings string) []ExchangeScope {
|
func (sr *ExchangeRestore) MailReceivedAfter(timeStrings string) []ExchangeScope {
|
||||||
return []ExchangeScope{
|
return []ExchangeScope{
|
||||||
makeExchangeFilterScope(ExchangeMail, ExchangeInfoMailReceivedAfter, []string{timeStrings}),
|
makeFilterScope[ExchangeScope](ExchangeMail, ExchangeInfoMailReceivedAfter, []string{timeStrings}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +277,7 @@ func (sr *ExchangeRestore) MailReceivedAfter(timeStrings string) []ExchangeScope
|
|||||||
// If the input is empty or selectors.None, the scope will always fail comparisons.
|
// If the input is empty or selectors.None, the scope will always fail comparisons.
|
||||||
func (sr *ExchangeRestore) MailReceivedBefore(timeStrings string) []ExchangeScope {
|
func (sr *ExchangeRestore) MailReceivedBefore(timeStrings string) []ExchangeScope {
|
||||||
return []ExchangeScope{
|
return []ExchangeScope{
|
||||||
makeExchangeFilterScope(ExchangeMail, ExchangeInfoMailReceivedBefore, []string{timeStrings}),
|
makeFilterScope[ExchangeScope](ExchangeMail, ExchangeInfoMailReceivedBefore, []string{timeStrings}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +288,7 @@ func (sr *ExchangeRestore) MailReceivedBefore(timeStrings string) []ExchangeScop
|
|||||||
// If any slice is empty, it defaults to [selectors.None]
|
// If any slice is empty, it defaults to [selectors.None]
|
||||||
func (sr *ExchangeRestore) MailSender(senderIDs []string) []ExchangeScope {
|
func (sr *ExchangeRestore) MailSender(senderIDs []string) []ExchangeScope {
|
||||||
return []ExchangeScope{
|
return []ExchangeScope{
|
||||||
makeExchangeFilterScope(ExchangeMail, ExchangeInfoMailSender, senderIDs),
|
makeFilterScope[ExchangeScope](ExchangeMail, ExchangeInfoMailSender, senderIDs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +299,7 @@ func (sr *ExchangeRestore) MailSender(senderIDs []string) []ExchangeScope {
|
|||||||
// If any slice is empty, it defaults to [selectors.None]
|
// If any slice is empty, it defaults to [selectors.None]
|
||||||
func (sr *ExchangeRestore) MailSubject(subjectSubstrings []string) []ExchangeScope {
|
func (sr *ExchangeRestore) MailSubject(subjectSubstrings []string) []ExchangeScope {
|
||||||
return []ExchangeScope{
|
return []ExchangeScope{
|
||||||
makeExchangeFilterScope(ExchangeMail, ExchangeInfoMailSubject, subjectSubstrings),
|
makeFilterScope[ExchangeScope](ExchangeMail, ExchangeInfoMailSubject, subjectSubstrings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,13 +404,13 @@ var exchangePathSet = map[categorizer][]categorizer{
|
|||||||
ExchangeUser: {ExchangeUser}, // the root category must be represented
|
ExchangeUser: {ExchangeUser}, // the root category must be represented
|
||||||
}
|
}
|
||||||
|
|
||||||
// leafType returns the leaf category of the receiver.
|
// leafCat returns the leaf category of the receiver.
|
||||||
// If the receiver category has multiple leaves (ex: User) or no leaves,
|
// If the receiver category has multiple leaves (ex: User) or no leaves,
|
||||||
// (ex: Unknown), the receiver itself is returned.
|
// (ex: Unknown), the receiver itself is returned.
|
||||||
// Ex: ExchangeContactFolder.leafType() => ExchangeContact
|
// Ex: ExchangeContactFolder.leafCat() => ExchangeContact
|
||||||
// Ex: ExchangeEvent.leafType() => ExchangeEvent
|
// Ex: ExchangeEvent.leafCat() => ExchangeEvent
|
||||||
// Ex: ExchangeUser.leafType() => ExchangeUser
|
// Ex: ExchangeUser.leafCat() => ExchangeUser
|
||||||
func (ec exchangeCategory) leafType() exchangeCategory {
|
func (ec exchangeCategory) leafCat() categorizer {
|
||||||
switch ec {
|
switch ec {
|
||||||
case ExchangeContact, ExchangeContactFolder:
|
case ExchangeContact, ExchangeContactFolder:
|
||||||
return ExchangeContact
|
return ExchangeContact
|
||||||
@ -448,28 +420,14 @@ func (ec exchangeCategory) leafType() exchangeCategory {
|
|||||||
return ec
|
return ec
|
||||||
}
|
}
|
||||||
|
|
||||||
// isType checks if either the receiver is a supertype of the parameter,
|
// rootCat returns the root category type.
|
||||||
// or if the parameter is a supertype of the receiver.
|
func (ec exchangeCategory) rootCat() categorizer {
|
||||||
// if either value is an unknown types, the comparison is always false.
|
return ExchangeUser
|
||||||
// if either value is the root type (user), the comparison is always true.
|
|
||||||
func (ec exchangeCategory) isType(cat exchangeCategory) bool {
|
|
||||||
if cat == ExchangeCategoryUnknown || ec == ExchangeCategoryUnknown {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if cat == ExchangeUser || ec == ExchangeUser {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return ec.leafType() == cat.leafType()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// includesType returns true if it matches the isType check for
|
// unknownCat returns the unknown category type.
|
||||||
// the receiver's service category.
|
func (ec exchangeCategory) unknownCat() categorizer {
|
||||||
func (ec exchangeCategory) includesType(cat categorizer) bool {
|
return ExchangeCategoryUnknown
|
||||||
c, ok := cat.(exchangeCategory)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return ec.isType(c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transforms a path to a map of identified properties.
|
// transforms a path to a map of identified properties.
|
||||||
@ -519,7 +477,7 @@ func (ec exchangeCategory) pathValues(path []string) map[categorizer]string {
|
|||||||
|
|
||||||
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
||||||
func (ec exchangeCategory) pathKeys() []categorizer {
|
func (ec exchangeCategory) pathKeys() []categorizer {
|
||||||
return exchangePathSet[ec.leafType()]
|
return exchangePathSet[ec.leafCat()]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -566,7 +524,7 @@ func (s ExchangeScope) Granularity() string {
|
|||||||
// Ex: to check if the scope includes mail data:
|
// Ex: to check if the scope includes mail data:
|
||||||
// s.IncludesCategory(selector.ExchangeMail)
|
// s.IncludesCategory(selector.ExchangeMail)
|
||||||
func (s ExchangeScope) IncludesCategory(cat exchangeCategory) bool {
|
func (s ExchangeScope) IncludesCategory(cat exchangeCategory) bool {
|
||||||
return s.Category().isType(cat)
|
return categoryMatches(s.Category(), cat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the category is included in the scope's data type,
|
// returns true if the category is included in the scope's data type,
|
||||||
@ -584,8 +542,7 @@ func (s ExchangeScope) Get(cat exchangeCategory) []string {
|
|||||||
|
|
||||||
// sets a value by category to the scope. Only intended for internal use.
|
// sets a value by category to the scope. Only intended for internal use.
|
||||||
func (s ExchangeScope) set(cat exchangeCategory, v string) ExchangeScope {
|
func (s ExchangeScope) set(cat exchangeCategory, v string) ExchangeScope {
|
||||||
s[cat.String()] = v
|
return set(s, cat, v)
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setDefaults ensures that contact folder, mail folder, and user category
|
// setDefaults ensures that contact folder, mail folder, and user category
|
||||||
@ -630,7 +587,7 @@ func (s ExchangeScope) matchesEntry(
|
|||||||
entry details.DetailsEntry,
|
entry details.DetailsEntry,
|
||||||
) bool {
|
) bool {
|
||||||
// matchesPathValues can be handled generically, thanks to SCIENCE.
|
// matchesPathValues can be handled generically, thanks to SCIENCE.
|
||||||
return matchesPathValues(s, cat, pathValues) || s.matchesInfo(entry.Exchange)
|
return matchesPathValues(s, cat.(exchangeCategory), pathValues) || s.matchesInfo(entry.Exchange)
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchesInfo handles the standard behavior when comparing a scope and an exchangeInfo
|
// matchesInfo handles the standard behavior when comparing a scope and an exchangeInfo
|
||||||
|
|||||||
@ -956,7 +956,7 @@ func (suite *ExchangeSourceSuite) TestIsAny() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *ExchangeSourceSuite) TestExchangeCategory_LeafType() {
|
func (suite *ExchangeSourceSuite) TestExchangeCategory_leafCat() {
|
||||||
table := []struct {
|
table := []struct {
|
||||||
cat exchangeCategory
|
cat exchangeCategory
|
||||||
expect exchangeCategory
|
expect exchangeCategory
|
||||||
@ -971,69 +971,7 @@ func (suite *ExchangeSourceSuite) TestExchangeCategory_LeafType() {
|
|||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.T().Run(test.cat.String(), func(t *testing.T) {
|
suite.T().Run(test.cat.String(), func(t *testing.T) {
|
||||||
assert.Equal(t, test.expect, test.cat.leafType(), test.cat.String())
|
assert.Equal(t, test.expect, test.cat.leafCat(), test.cat.String())
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ExchangeSourceSuite) TestExchangeCategory_IsType() {
|
|
||||||
table := []struct {
|
|
||||||
cat exchangeCategory
|
|
||||||
input exchangeCategory
|
|
||||||
expect assert.BoolAssertionFunc
|
|
||||||
}{
|
|
||||||
// technically this should be false, but we're not reducing fabricated
|
|
||||||
// exchange category values to unknown. Since the type is unexported,
|
|
||||||
// that transformation would be unnecessary.
|
|
||||||
{exchangeCategory(-1), exchangeCategory(-1), assert.True},
|
|
||||||
{ExchangeCategoryUnknown, ExchangeCategoryUnknown, assert.False},
|
|
||||||
{ExchangeUser, ExchangeCategoryUnknown, assert.False},
|
|
||||||
{ExchangeCategoryUnknown, ExchangeUser, assert.False},
|
|
||||||
{ExchangeUser, ExchangeUser, assert.True},
|
|
||||||
{ExchangeMailFolder, ExchangeMail, assert.True},
|
|
||||||
{ExchangeMailFolder, ExchangeContact, assert.False},
|
|
||||||
{ExchangeContactFolder, ExchangeMail, assert.False},
|
|
||||||
{ExchangeMail, ExchangeMail, assert.True},
|
|
||||||
{ExchangeContactFolder, ExchangeContact, assert.True},
|
|
||||||
{ExchangeContactFolder, ExchangeEvent, assert.False},
|
|
||||||
{ExchangeEvent, ExchangeContact, assert.False},
|
|
||||||
{ExchangeEvent, ExchangeEvent, assert.True},
|
|
||||||
}
|
|
||||||
for _, test := range table {
|
|
||||||
suite.T().Run(test.cat.String(), func(t *testing.T) {
|
|
||||||
test.expect(t, test.cat.isType(test.input))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (suite *ExchangeSourceSuite) TestExchangeCategory_IncludesType() {
|
|
||||||
table := []struct {
|
|
||||||
cat categorizer
|
|
||||||
input categorizer
|
|
||||||
expect assert.BoolAssertionFunc
|
|
||||||
}{
|
|
||||||
// technically this should be false, but we're not reducing fabricated
|
|
||||||
// exchange category values to unknown. Since the type is unexported,
|
|
||||||
// that transformation would be unnecessary.
|
|
||||||
{exchangeCategory(-1), exchangeCategory(-1), assert.True},
|
|
||||||
{ExchangeCategoryUnknown, ExchangeCategoryUnknown, assert.False},
|
|
||||||
{ExchangeUser, ExchangeCategoryUnknown, assert.False},
|
|
||||||
{ExchangeCategoryUnknown, ExchangeUser, assert.False},
|
|
||||||
{ExchangeUser, ExchangeUser, assert.True},
|
|
||||||
{ExchangeMailFolder, ExchangeMail, assert.True},
|
|
||||||
{ExchangeMailFolder, ExchangeContact, assert.False},
|
|
||||||
{ExchangeContactFolder, ExchangeMail, assert.False},
|
|
||||||
{ExchangeMail, ExchangeMail, assert.True},
|
|
||||||
{ExchangeContactFolder, ExchangeContact, assert.True},
|
|
||||||
{ExchangeContactFolder, ExchangeEvent, assert.False},
|
|
||||||
{ExchangeEvent, ExchangeContact, assert.False},
|
|
||||||
{ExchangeEvent, ExchangeEvent, assert.True},
|
|
||||||
{ExchangeUser, rootCatStub, assert.False},
|
|
||||||
{rootCatStub, ExchangeUser, assert.False},
|
|
||||||
}
|
|
||||||
for _, test := range table {
|
|
||||||
suite.T().Run(test.cat.String(), func(t *testing.T) {
|
|
||||||
test.expect(t, test.cat.includesType(test.input))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,8 @@ const (
|
|||||||
|
|
||||||
var _ categorizer = unknownCatStub
|
var _ categorizer = unknownCatStub
|
||||||
|
|
||||||
func (sc mockCategorizer) String() string {
|
func (mc mockCategorizer) String() string {
|
||||||
switch sc {
|
switch mc {
|
||||||
case leafCatStub:
|
case leafCatStub:
|
||||||
return "leaf"
|
return "leaf"
|
||||||
case rootCatStub:
|
case rootCatStub:
|
||||||
@ -27,21 +27,23 @@ func (sc mockCategorizer) String() string {
|
|||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc mockCategorizer) includesType(cat categorizer) bool {
|
func (mc mockCategorizer) leafCat() categorizer {
|
||||||
switch sc {
|
return mc
|
||||||
case rootCatStub:
|
|
||||||
return cat == rootCatStub
|
|
||||||
case leafCatStub:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc mockCategorizer) pathValues(path []string) map[categorizer]string {
|
func (mc mockCategorizer) rootCat() categorizer {
|
||||||
|
return rootCatStub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc mockCategorizer) unknownCat() categorizer {
|
||||||
|
return unknownCatStub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc mockCategorizer) pathValues(path []string) map[categorizer]string {
|
||||||
return map[categorizer]string{rootCatStub: "stub"}
|
return map[categorizer]string{rootCatStub: "stub"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc mockCategorizer) pathKeys() []categorizer {
|
func (mc mockCategorizer) pathKeys() []categorizer {
|
||||||
return []categorizer{rootCatStub, leafCatStub}
|
return []categorizer{rootCatStub, leafCatStub}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -102,21 +102,6 @@ func (s *onedrive) Filter(scopes ...[]OneDriveScope) {
|
|||||||
s.Filters = appendScopes(s.Filters, scopes...)
|
s.Filters = appendScopes(s.Filters, scopes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeOnedriveScope(granularity string, cat onedriveCategory, vs []string) OneDriveScope {
|
|
||||||
return OneDriveScope{
|
|
||||||
scopeKeyGranularity: granularity,
|
|
||||||
scopeKeyCategory: cat.String(),
|
|
||||||
cat.String(): join(vs...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeOnedriveUserScope(user, granularity string, cat onedriveCategory, vs []string) OneDriveScope {
|
|
||||||
s := makeOnedriveScope(granularity, cat, vs).set(OneDriveUser, user)
|
|
||||||
s[scopeKeyResource] = user
|
|
||||||
s[scopeKeyDataType] = cat.String() // TODO: use leafType() instead of base cat.
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces one or more onedrive user scopes.
|
// Produces one or more onedrive user scopes.
|
||||||
// One scope is created per user entry.
|
// One scope is created per user entry.
|
||||||
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
|
||||||
@ -126,19 +111,14 @@ func (s *onedrive) Users(users []string) []OneDriveScope {
|
|||||||
users = normalize(users)
|
users = normalize(users)
|
||||||
scopes := []OneDriveScope{}
|
scopes := []OneDriveScope{}
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
scopes = append(scopes, makeOnedriveUserScope(u, Group, OneDriveUser, users))
|
scopes = append(scopes, makeScope[OneDriveScope](u, Group, OneDriveUser, users))
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scopes retrieves the list of onedriveScopes in the selector.
|
// Scopes retrieves the list of onedriveScopes in the selector.
|
||||||
func (s *onedrive) Scopes() []OneDriveScope {
|
func (s *onedrive) Scopes() []OneDriveScope {
|
||||||
scopes := s.scopes()
|
return scopes[OneDriveScope](s.Selector)
|
||||||
ss := make([]OneDriveScope, len(scopes))
|
|
||||||
for i := range scopes {
|
|
||||||
ss[i] = OneDriveScope(scopes[i])
|
|
||||||
}
|
|
||||||
return ss
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -150,7 +130,7 @@ func (s *onedrive) Scopes() []OneDriveScope {
|
|||||||
type onedriveCategory int
|
type onedriveCategory int
|
||||||
|
|
||||||
// interface compliance checks
|
// interface compliance checks
|
||||||
// var _ categorizer = OneDriveCategoryUnknown
|
var _ categorizer = OneDriveCategoryUnknown
|
||||||
|
|
||||||
//go:generate go run golang.org/x/tools/cmd/stringer -type=onedriveCategory
|
//go:generate go run golang.org/x/tools/cmd/stringer -type=onedriveCategory
|
||||||
const (
|
const (
|
||||||
@ -177,37 +157,23 @@ var oneDrivePathSet = map[categorizer][]categorizer{
|
|||||||
OneDriveUser: {OneDriveUser}, // the root category must be represented
|
OneDriveUser: {OneDriveUser}, // the root category must be represented
|
||||||
}
|
}
|
||||||
|
|
||||||
// leafType returns the leaf category of the receiver.
|
// leafCat returns the leaf category of the receiver.
|
||||||
// If the receiver category has multiple leaves (ex: User) or no leaves,
|
// If the receiver category has multiple leaves (ex: User) or no leaves,
|
||||||
// (ex: Unknown), the receiver itself is returned.
|
// (ex: Unknown), the receiver itself is returned.
|
||||||
// Ex: ServiceTypeFolder.leafType() => ServiceTypeItem
|
// Ex: ServiceTypeFolder.leafCat() => ServiceTypeItem
|
||||||
// Ex: ServiceUser.leafType() => ServiceUser
|
// Ex: ServiceUser.leafCat() => ServiceUser
|
||||||
func (c onedriveCategory) leafType() onedriveCategory {
|
func (c onedriveCategory) leafCat() categorizer {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// isType checks if either the receiver is a supertype of the parameter,
|
// rootCat returns the root category type.
|
||||||
// or if the parameter is a supertype of the receiver.
|
func (c onedriveCategory) rootCat() categorizer {
|
||||||
// if either value is an unknown types, the comparison is always false.
|
return OneDriveUser
|
||||||
// if either value is the root type (user), the comparison is always true.
|
|
||||||
func (c onedriveCategory) isType(cat onedriveCategory) bool {
|
|
||||||
if cat == OneDriveCategoryUnknown || c == OneDriveCategoryUnknown {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if cat == OneDriveUser || c == OneDriveUser {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return c.leafType() == cat.leafType()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// includesType returns true if it matches the isType check for
|
// unknownCat returns the unknown category type.
|
||||||
// the receiver's service category.
|
func (c onedriveCategory) unknownCat() categorizer {
|
||||||
func (c onedriveCategory) includesType(cat categorizer) bool {
|
return OneDriveCategoryUnknown
|
||||||
cc, ok := cat.(onedriveCategory)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return c.isType(cc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathValues transforms a path to a map of identified properties.
|
// pathValues transforms a path to a map of identified properties.
|
||||||
@ -239,7 +205,7 @@ func (c onedriveCategory) pathValues(path []string) map[categorizer]string {
|
|||||||
|
|
||||||
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
// pathKeys returns the path keys recognized by the receiver's leaf type.
|
||||||
func (c onedriveCategory) pathKeys() []categorizer {
|
func (c onedriveCategory) pathKeys() []categorizer {
|
||||||
return oneDrivePathSet[c.leafType()]
|
return oneDrivePathSet[c.leafCat()]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -281,7 +247,7 @@ func (s OneDriveScope) Granularity() string {
|
|||||||
// Ex: to check if the scope includes file data:
|
// Ex: to check if the scope includes file data:
|
||||||
// s.IncludesCategory(selector.OneDriveFile)
|
// s.IncludesCategory(selector.OneDriveFile)
|
||||||
func (s OneDriveScope) IncludesCategory(cat onedriveCategory) bool {
|
func (s OneDriveScope) IncludesCategory(cat onedriveCategory) bool {
|
||||||
return s.Category().isType(cat)
|
return categoryMatches(s.Category(), cat)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains returns true if the category is included in the scope's
|
// Contains returns true if the category is included in the scope's
|
||||||
@ -321,7 +287,7 @@ func (s OneDriveScope) matchesEntry(
|
|||||||
entry details.DetailsEntry,
|
entry details.DetailsEntry,
|
||||||
) bool {
|
) bool {
|
||||||
// matchesPathValues can be handled generically, thanks to SCIENCE.
|
// matchesPathValues can be handled generically, thanks to SCIENCE.
|
||||||
return matchesPathValues(s, cat, pathValues) || s.matchesInfo(entry.Onedrive)
|
return matchesPathValues(s, cat.(onedriveCategory), pathValues) || s.matchesInfo(entry.Onedrive)
|
||||||
}
|
}
|
||||||
|
|
||||||
// matchesInfo handles the standard behavior when comparing a scope and an onedriveInfo
|
// matchesInfo handles the standard behavior when comparing a scope and an onedriveInfo
|
||||||
|
|||||||
@ -17,10 +17,18 @@ type (
|
|||||||
// String should return the human readable name of the category.
|
// String should return the human readable name of the category.
|
||||||
String() string
|
String() string
|
||||||
|
|
||||||
// includesType should return true if the parameterized category is, contextually
|
// leafCat should return the lowest level type matching the category. If the type
|
||||||
// within the service, a subset of the receiver category. Ex: a Mail category
|
// has multiple leaf types (ex: the root category) or no leaves (ex: unknown values),
|
||||||
// is a subset of a MailFolder category.
|
// the same value is returned. Otherwise, if the receiver is an intermediary type,
|
||||||
includesType(categorizer) bool
|
// such as a folder, then the child value should be returned.
|
||||||
|
// Ex: fooFolder.leafCat() => foo.
|
||||||
|
leafCat() categorizer
|
||||||
|
|
||||||
|
// rootCat returns the root category for the categorizer
|
||||||
|
rootCat() categorizer
|
||||||
|
|
||||||
|
// unknownType returns the unknown category value
|
||||||
|
unknownCat() categorizer
|
||||||
|
|
||||||
// pathValues should produce a map of category:string pairs populated by extracting
|
// pathValues should produce a map of category:string pairs populated by extracting
|
||||||
// values out of the path that match the given categorizer.
|
// values out of the path that match the given categorizer.
|
||||||
@ -106,14 +114,47 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// makeScope produces a well formatted, typed scope that ensures all base values are populated.
|
||||||
|
func makeScope[T scopeT](
|
||||||
|
resource, granularity string,
|
||||||
|
cat categorizer,
|
||||||
|
vs []string,
|
||||||
|
) T {
|
||||||
|
s := T{
|
||||||
|
scopeKeyCategory: cat.String(),
|
||||||
|
scopeKeyDataType: cat.leafCat().String(),
|
||||||
|
scopeKeyGranularity: granularity,
|
||||||
|
scopeKeyResource: resource,
|
||||||
|
cat.String(): join(vs...),
|
||||||
|
cat.rootCat().String(): resource,
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeFilterScope produces a well formatted, typed scope, with properties specifically oriented
|
||||||
|
// towards identifying filter-type scopes, that ensures all base values are populated.
|
||||||
|
func makeFilterScope[T scopeT](
|
||||||
|
cat, filterCat categorizer,
|
||||||
|
vs []string,
|
||||||
|
) T {
|
||||||
|
return T{
|
||||||
|
scopeKeyCategory: cat.String(),
|
||||||
|
scopeKeyDataType: cat.leafCat().String(),
|
||||||
|
scopeKeyGranularity: Filter,
|
||||||
|
scopeKeyInfoFilter: filterCat.String(),
|
||||||
|
scopeKeyResource: Filter,
|
||||||
|
filterCat.String(): join(vs...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// funcs
|
// scope funcs
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// contains returns true if the category is included in the scope's
|
// contains returns true if the category is included in the scope's
|
||||||
// data type, and the target string is included in the scope.
|
// data type, and the target string is included in the scope.
|
||||||
func contains[T scopeT](s T, cat categorizer, target string) bool {
|
func contains[T scopeT, C categoryT](s T, cat C, target string) bool {
|
||||||
if !s.categorizer().includesType(cat) {
|
if !typeAndCategoryMatches(cat, s.categorizer()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(target) == 0 {
|
if len(target) == 0 {
|
||||||
@ -143,6 +184,13 @@ func getCatValue[T scopeT](s T, cat categorizer) []string {
|
|||||||
return split(v)
|
return split(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set sets a value by category to the scope. Only intended for internal
|
||||||
|
// use, not for exporting to callers.
|
||||||
|
func set[T scopeT](s T, cat categorizer, v string) T {
|
||||||
|
s[cat.String()] = v
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// granularity describes the granularity (directory || item)
|
// granularity describes the granularity (directory || item)
|
||||||
// of the data in scope.
|
// of the data in scope.
|
||||||
func granularity[T scopeT](s T) string {
|
func granularity[T scopeT](s T) string {
|
||||||
@ -151,8 +199,8 @@ func granularity[T scopeT](s T) string {
|
|||||||
|
|
||||||
// returns true if the category is included in the scope's category type,
|
// returns true if the category is included in the scope's category type,
|
||||||
// and the value is set to Any().
|
// and the value is set to Any().
|
||||||
func isAnyTarget[T scopeT](s T, cat categorizer) bool {
|
func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool {
|
||||||
if !s.categorizer().includesType(cat) {
|
if !typeAndCategoryMatches(cat, s.categorizer()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return s[cat.String()] == AnyTgt
|
return s[cat.String()] == AnyTgt
|
||||||
@ -254,7 +302,7 @@ func scopesByCategory[T scopeT, C categoryT](
|
|||||||
for _, sc := range scopes {
|
for _, sc := range scopes {
|
||||||
for _, cat := range cats {
|
for _, cat := range cats {
|
||||||
t := T(sc)
|
t := T(sc)
|
||||||
if t.categorizer().includesType(cat) {
|
if typeAndCategoryMatches(cat, t.categorizer()) {
|
||||||
m[cat] = append(m[cat], t)
|
m[cat] = append(m[cat], t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,9 +361,9 @@ func passes[T scopeT](
|
|||||||
// the categorizer.
|
// the categorizer.
|
||||||
// Standard expectations apply: None() or missing values always fail, Any()
|
// Standard expectations apply: None() or missing values always fail, Any()
|
||||||
// always succeeds.
|
// always succeeds.
|
||||||
func matchesPathValues[T scopeT](
|
func matchesPathValues[T scopeT, C categoryT](
|
||||||
sc T,
|
sc T,
|
||||||
cat categorizer,
|
cat C,
|
||||||
pathValues map[categorizer]string,
|
pathValues map[categorizer]string,
|
||||||
) bool {
|
) bool {
|
||||||
for _, c := range cat.pathKeys() {
|
for _, c := range cat.pathKeys() {
|
||||||
@ -334,7 +382,8 @@ func matchesPathValues[T scopeT](
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// all parts of the scope must match
|
// all parts of the scope must match
|
||||||
if !isAnyTarget(sc, c) {
|
cc := c.(C)
|
||||||
|
if !isAnyTarget(sc, cc) {
|
||||||
if !common.ContainsString(target, pv) {
|
if !common.ContainsString(target, pv) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -342,3 +391,36 @@ func matchesPathValues[T scopeT](
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// categorizer funcs
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// categoryMatches returns true if:
|
||||||
|
// - neither type is 'unknown'
|
||||||
|
// - either type is the root type
|
||||||
|
// - the leaf types match
|
||||||
|
func categoryMatches[C categoryT](a, b C) bool {
|
||||||
|
u := a.unknownCat()
|
||||||
|
if a == u || b == u {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
r := a.rootCat()
|
||||||
|
if a == r || b == r {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.leafCat() == b.leafCat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeAndCategoryMatches returns true if:
|
||||||
|
// - both parameters are the same categoryT type
|
||||||
|
// - the category matches for both types
|
||||||
|
func typeAndCategoryMatches[C categoryT](a C, b categorizer) bool {
|
||||||
|
bb, ok := b.(C)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return categoryMatches(a, bb)
|
||||||
|
}
|
||||||
|
|||||||
@ -124,10 +124,10 @@ func appendScopes[T scopeT](to []scope, scopes ...[]T) []scope {
|
|||||||
|
|
||||||
// scopes retrieves the list of scopes in the selector.
|
// scopes retrieves the list of scopes in the selector.
|
||||||
// future TODO: if Inclues is nil, return filters.
|
// future TODO: if Inclues is nil, return filters.
|
||||||
func (s *Selector) scopes() []scope {
|
func scopes[T scopeT](s Selector) []T {
|
||||||
scopes := []scope{}
|
scopes := []T{}
|
||||||
for _, v := range s.Includes {
|
for _, v := range s.Includes {
|
||||||
scopes = append(scopes, v)
|
scopes = append(scopes, T(v))
|
||||||
}
|
}
|
||||||
return scopes
|
return scopes
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user