diff --git a/src/pkg/selectors/exchange.go b/src/pkg/selectors/exchange.go index c7af07e90..caa889fc6 100644 --- a/src/pkg/selectors/exchange.go +++ b/src/pkg/selectors/exchange.go @@ -140,32 +140,12 @@ func (s *exchange) Include(scopes ...[]ExchangeScope) { // Scopes retrieves the list of exchangeScopes in the selector. func (s *exchange) Scopes() []ExchangeScope { - scopes := s.scopes() - es := make([]ExchangeScope, len(scopes)) - for i := range scopes { - es[i] = ExchangeScope(scopes[i]) - } - return es + return scopes[ExchangeScope](s.Selector) } // ------------------- // 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. // One scope is created per combination of users,folders,contacts. // 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 { scopes = append( 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 { scopes = append( scopes, - makeExchangeUserScope(u, Group, ExchangeContactFolder, folders), + makeScope[ExchangeScope](u, Group, ExchangeContactFolder, folders), ) } return scopes @@ -217,7 +197,7 @@ func (s *exchange) Events(users, events []string) []ExchangeScope { for _, u := range users { scopes = append( scopes, - makeExchangeUserScope(u, Item, ExchangeEvent, events), + makeScope[ExchangeScope](u, Item, ExchangeEvent, events), ) } return scopes @@ -237,7 +217,7 @@ func (s *exchange) Mails(users, folders, mails []string) []ExchangeScope { for _, f := range folders { scopes = append( 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 { scopes = append( scopes, - makeExchangeUserScope(u, Group, ExchangeMailFolder, folders), + makeScope[ExchangeScope](u, Group, ExchangeMailFolder, folders), ) } return scopes @@ -271,23 +251,15 @@ func (s *exchange) Users(users []string) []ExchangeScope { users = normalize(users) scopes := []ExchangeScope{} for _, u := range users { - scopes = append(scopes, makeExchangeUserScope(u, Group, ExchangeContactFolder, Any())) - scopes = append(scopes, makeExchangeUserScope(u, Item, ExchangeEvent, Any())) - scopes = append(scopes, makeExchangeUserScope(u, Group, ExchangeMailFolder, Any())) + scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeContactFolder, Any())) + scopes = append(scopes, makeScope[ExchangeScope](u, Item, ExchangeEvent, Any())) + scopes = append(scopes, makeScope[ExchangeScope](u, Group, ExchangeMailFolder, Any())) } return scopes } -func makeExchangeFilterScope(cat, filterCat exchangeCategory, vs []string) ExchangeScope { - return ExchangeScope{ - scopeKeyGranularity: Filter, - scopeKeyCategory: cat.String(), - scopeKeyInfoFilter: filterCat.String(), - scopeKeyResource: Filter, - scopeKeyDataType: cat.leafType().String(), - filterCat.String(): join(vs...), - } -} +// ------------------- +// Filter Factories // Produces an exchange mail received-after filter scope. // 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. func (sr *ExchangeRestore) MailReceivedAfter(timeStrings string) []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. func (sr *ExchangeRestore) MailReceivedBefore(timeStrings string) []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] func (sr *ExchangeRestore) MailSender(senderIDs []string) []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] func (sr *ExchangeRestore) MailSubject(subjectSubstrings []string) []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 } -// 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, // (ex: Unknown), the receiver itself is returned. -// Ex: ExchangeContactFolder.leafType() => ExchangeContact -// Ex: ExchangeEvent.leafType() => ExchangeEvent -// Ex: ExchangeUser.leafType() => ExchangeUser -func (ec exchangeCategory) leafType() exchangeCategory { +// Ex: ExchangeContactFolder.leafCat() => ExchangeContact +// Ex: ExchangeEvent.leafCat() => ExchangeEvent +// Ex: ExchangeUser.leafCat() => ExchangeUser +func (ec exchangeCategory) leafCat() categorizer { switch ec { case ExchangeContact, ExchangeContactFolder: return ExchangeContact @@ -448,28 +420,14 @@ func (ec exchangeCategory) leafType() exchangeCategory { return ec } -// isType checks if either the receiver is a supertype of the parameter, -// or if the parameter is a supertype of the receiver. -// if either value is an unknown types, the comparison is always false. -// 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() +// rootCat returns the root category type. +func (ec exchangeCategory) rootCat() categorizer { + return ExchangeUser } -// includesType returns true if it matches the isType check for -// the receiver's service category. -func (ec exchangeCategory) includesType(cat categorizer) bool { - c, ok := cat.(exchangeCategory) - if !ok { - return false - } - return ec.isType(c) +// unknownCat returns the unknown category type. +func (ec exchangeCategory) unknownCat() categorizer { + return ExchangeCategoryUnknown } // 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. 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: // s.IncludesCategory(selector.ExchangeMail) 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, @@ -584,8 +542,7 @@ func (s ExchangeScope) Get(cat exchangeCategory) []string { // sets a value by category to the scope. Only intended for internal use. func (s ExchangeScope) set(cat exchangeCategory, v string) ExchangeScope { - s[cat.String()] = v - return s + return set(s, cat, v) } // setDefaults ensures that contact folder, mail folder, and user category @@ -630,7 +587,7 @@ func (s ExchangeScope) matchesEntry( entry details.DetailsEntry, ) bool { // 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 diff --git a/src/pkg/selectors/exchange_test.go b/src/pkg/selectors/exchange_test.go index f9076a2bd..4a6dfff85 100644 --- a/src/pkg/selectors/exchange_test.go +++ b/src/pkg/selectors/exchange_test.go @@ -956,7 +956,7 @@ func (suite *ExchangeSourceSuite) TestIsAny() { } } -func (suite *ExchangeSourceSuite) TestExchangeCategory_LeafType() { +func (suite *ExchangeSourceSuite) TestExchangeCategory_leafCat() { table := []struct { cat exchangeCategory expect exchangeCategory @@ -971,69 +971,7 @@ func (suite *ExchangeSourceSuite) TestExchangeCategory_LeafType() { } for _, test := range table { suite.T().Run(test.cat.String(), func(t *testing.T) { - assert.Equal(t, test.expect, test.cat.leafType(), 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)) + assert.Equal(t, test.expect, test.cat.leafCat(), test.cat.String()) }) } } diff --git a/src/pkg/selectors/helpers_test.go b/src/pkg/selectors/helpers_test.go index 8a4074fad..07970a74e 100644 --- a/src/pkg/selectors/helpers_test.go +++ b/src/pkg/selectors/helpers_test.go @@ -17,8 +17,8 @@ const ( var _ categorizer = unknownCatStub -func (sc mockCategorizer) String() string { - switch sc { +func (mc mockCategorizer) String() string { + switch mc { case leafCatStub: return "leaf" case rootCatStub: @@ -27,21 +27,23 @@ func (sc mockCategorizer) String() string { return "unknown" } -func (sc mockCategorizer) includesType(cat categorizer) bool { - switch sc { - case rootCatStub: - return cat == rootCatStub - case leafCatStub: - return true - } - return false +func (mc mockCategorizer) leafCat() categorizer { + return mc } -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"} } -func (sc mockCategorizer) pathKeys() []categorizer { +func (mc mockCategorizer) pathKeys() []categorizer { return []categorizer{rootCatStub, leafCatStub} } diff --git a/src/pkg/selectors/onedrive.go b/src/pkg/selectors/onedrive.go index baebd97ba..1a3a9dfbe 100644 --- a/src/pkg/selectors/onedrive.go +++ b/src/pkg/selectors/onedrive.go @@ -102,21 +102,6 @@ func (s *onedrive) Filter(scopes ...[]OneDriveScope) { 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. // One scope is created per user entry. // 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) scopes := []OneDriveScope{} for _, u := range users { - scopes = append(scopes, makeOnedriveUserScope(u, Group, OneDriveUser, users)) + scopes = append(scopes, makeScope[OneDriveScope](u, Group, OneDriveUser, users)) } return scopes } // Scopes retrieves the list of onedriveScopes in the selector. func (s *onedrive) Scopes() []OneDriveScope { - scopes := s.scopes() - ss := make([]OneDriveScope, len(scopes)) - for i := range scopes { - ss[i] = OneDriveScope(scopes[i]) - } - return ss + return scopes[OneDriveScope](s.Selector) } // --------------------------------------------------------------------------- @@ -150,7 +130,7 @@ func (s *onedrive) Scopes() []OneDriveScope { type onedriveCategory int // interface compliance checks -// var _ categorizer = OneDriveCategoryUnknown +var _ categorizer = OneDriveCategoryUnknown //go:generate go run golang.org/x/tools/cmd/stringer -type=onedriveCategory const ( @@ -177,37 +157,23 @@ var oneDrivePathSet = map[categorizer][]categorizer{ 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, // (ex: Unknown), the receiver itself is returned. -// Ex: ServiceTypeFolder.leafType() => ServiceTypeItem -// Ex: ServiceUser.leafType() => ServiceUser -func (c onedriveCategory) leafType() onedriveCategory { +// Ex: ServiceTypeFolder.leafCat() => ServiceTypeItem +// Ex: ServiceUser.leafCat() => ServiceUser +func (c onedriveCategory) leafCat() categorizer { return c } -// isType checks if either the receiver is a supertype of the parameter, -// or if the parameter is a supertype of the receiver. -// if either value is an unknown types, the comparison is always false. -// 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() +// rootCat returns the root category type. +func (c onedriveCategory) rootCat() categorizer { + return OneDriveUser } -// includesType returns true if it matches the isType check for -// the receiver's service category. -func (c onedriveCategory) includesType(cat categorizer) bool { - cc, ok := cat.(onedriveCategory) - if !ok { - return false - } - return c.isType(cc) +// unknownCat returns the unknown category type. +func (c onedriveCategory) unknownCat() categorizer { + return OneDriveCategoryUnknown } // 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. 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: // s.IncludesCategory(selector.OneDriveFile) 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 @@ -321,7 +287,7 @@ func (s OneDriveScope) matchesEntry( entry details.DetailsEntry, ) bool { // 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 diff --git a/src/pkg/selectors/scopes.go b/src/pkg/selectors/scopes.go index 5862a8f04..c05ad0835 100644 --- a/src/pkg/selectors/scopes.go +++ b/src/pkg/selectors/scopes.go @@ -17,10 +17,18 @@ type ( // String should return the human readable name of the category. String() string - // includesType should return true if the parameterized category is, contextually - // within the service, a subset of the receiver category. Ex: a Mail category - // is a subset of a MailFolder category. - includesType(categorizer) bool + // leafCat should return the lowest level type matching the category. If the type + // has multiple leaf types (ex: the root category) or no leaves (ex: unknown values), + // the same value is returned. Otherwise, if the receiver is an intermediary type, + // 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 // 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 // data type, and the target string is included in the scope. -func contains[T scopeT](s T, cat categorizer, target string) bool { - if !s.categorizer().includesType(cat) { +func contains[T scopeT, C categoryT](s T, cat C, target string) bool { + if !typeAndCategoryMatches(cat, s.categorizer()) { return false } if len(target) == 0 { @@ -143,6 +184,13 @@ func getCatValue[T scopeT](s T, cat categorizer) []string { 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) // of the data in scope. 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, // and the value is set to Any(). -func isAnyTarget[T scopeT](s T, cat categorizer) bool { - if !s.categorizer().includesType(cat) { +func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool { + if !typeAndCategoryMatches(cat, s.categorizer()) { return false } return s[cat.String()] == AnyTgt @@ -254,7 +302,7 @@ func scopesByCategory[T scopeT, C categoryT]( for _, sc := range scopes { for _, cat := range cats { t := T(sc) - if t.categorizer().includesType(cat) { + if typeAndCategoryMatches(cat, t.categorizer()) { m[cat] = append(m[cat], t) } } @@ -313,9 +361,9 @@ func passes[T scopeT]( // the categorizer. // Standard expectations apply: None() or missing values always fail, Any() // always succeeds. -func matchesPathValues[T scopeT]( +func matchesPathValues[T scopeT, C categoryT]( sc T, - cat categorizer, + cat C, pathValues map[categorizer]string, ) bool { for _, c := range cat.pathKeys() { @@ -334,7 +382,8 @@ func matchesPathValues[T scopeT]( return false } // all parts of the scope must match - if !isAnyTarget(sc, c) { + cc := c.(C) + if !isAnyTarget(sc, cc) { if !common.ContainsString(target, pv) { return false } @@ -342,3 +391,36 @@ func matchesPathValues[T scopeT]( } 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) +} diff --git a/src/pkg/selectors/selectors.go b/src/pkg/selectors/selectors.go index c4b6d5b5c..97327adfe 100644 --- a/src/pkg/selectors/selectors.go +++ b/src/pkg/selectors/selectors.go @@ -124,10 +124,10 @@ func appendScopes[T scopeT](to []scope, scopes ...[]T) []scope { // scopes retrieves the list of scopes in the selector. // future TODO: if Inclues is nil, return filters. -func (s *Selector) scopes() []scope { - scopes := []scope{} +func scopes[T scopeT](s Selector) []T { + scopes := []T{} for _, v := range s.Includes { - scopes = append(scopes, v) + scopes = append(scopes, T(v)) } return scopes }