mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-10 06:48:04 -05:00
## CHANGES - Add case-insensitive vendor lookup in VendorsManager - Implement model name normalization in GetChatter method - Add FilterByVendor method with case-insensitive matching - Add FindModelNameCaseInsensitive helper for model queries - Update group/item comparison to use case-insensitive checks - Store vendors with lowercase keys internally - Add comprehensive tests for case-insensitive functionality - Fix vendor filtering for model listing command
183 lines
4.5 KiB
Go
183 lines
4.5 KiB
Go
package util
|
||
|
||
import (
|
||
"fmt"
|
||
"sort"
|
||
"strings"
|
||
|
||
"github.com/samber/lo"
|
||
)
|
||
|
||
func NewGroupsItemsSelector[I any](selectionLabel string,
|
||
getItemLabel func(I) string) *GroupsItemsSelector[I] {
|
||
|
||
return &GroupsItemsSelector[I]{SelectionLabel: selectionLabel,
|
||
GetItemKey: getItemLabel,
|
||
GroupsItems: make([]*GroupItems[I], 0),
|
||
}
|
||
}
|
||
|
||
type GroupItems[I any] struct {
|
||
Group string
|
||
Items []I
|
||
}
|
||
|
||
func (o *GroupItems[I]) Count() int {
|
||
return len(o.Items)
|
||
}
|
||
|
||
func (o *GroupItems[I]) ContainsItemBy(predicate func(item I) bool) (ret bool) {
|
||
ret = lo.ContainsBy(o.Items, predicate)
|
||
return
|
||
}
|
||
|
||
type GroupsItemsSelector[I any] struct {
|
||
SelectionLabel string
|
||
GetItemKey func(I) string
|
||
|
||
GroupsItems []*GroupItems[I]
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) AddGroupItems(group string, items ...I) {
|
||
o.GroupsItems = append(o.GroupsItems, &GroupItems[I]{group, items})
|
||
}
|
||
|
||
// getSortedGroupsItems returns a new slice of GroupItems with both groups and their items
|
||
// sorted alphabetically in a case-insensitive manner. The original GroupsItems are not modified.
|
||
func (o *GroupsItemsSelector[I]) getSortedGroupsItems() []*GroupItems[I] {
|
||
// Copy and sort groups (case‑insensitive)
|
||
sortedGroupsItems := make([]*GroupItems[I], len(o.GroupsItems))
|
||
copy(sortedGroupsItems, o.GroupsItems)
|
||
sort.SliceStable(sortedGroupsItems, func(i, j int) bool {
|
||
return strings.ToLower(sortedGroupsItems[i].Group) < strings.ToLower(sortedGroupsItems[j].Group)
|
||
})
|
||
|
||
// For each group, sort its items
|
||
for i, groupItems := range sortedGroupsItems {
|
||
sortedItems := make([]I, len(groupItems.Items))
|
||
copy(sortedItems, groupItems.Items)
|
||
sort.SliceStable(sortedItems, func(i, j int) bool {
|
||
return strings.ToLower(o.GetItemKey(sortedItems[i])) < strings.ToLower(o.GetItemKey(sortedItems[j]))
|
||
})
|
||
|
||
// Create a new GroupItems with the sorted items
|
||
sortedGroupsItems[i] = &GroupItems[I]{
|
||
Group: groupItems.Group,
|
||
Items: sortedItems,
|
||
}
|
||
}
|
||
|
||
return sortedGroupsItems
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) GetGroupAndItemByItemNumber(number int) (group string, item I, err error) {
|
||
var currentItemNumber int
|
||
found := false
|
||
|
||
sortedGroupsItems := o.getSortedGroupsItems()
|
||
|
||
for _, groupItems := range sortedGroupsItems {
|
||
if currentItemNumber+len(groupItems.Items) < number {
|
||
currentItemNumber += len(groupItems.Items)
|
||
continue
|
||
}
|
||
|
||
for _, groupItem := range groupItems.Items {
|
||
currentItemNumber++
|
||
if currentItemNumber == number {
|
||
group = groupItems.Group
|
||
item = groupItem
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
|
||
if found {
|
||
break
|
||
}
|
||
}
|
||
|
||
if !found {
|
||
err = fmt.Errorf("number %d is out of range", number)
|
||
}
|
||
return
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) Print(shellCompleteList bool) {
|
||
// Only print the section header if not in plain output mode
|
||
if !shellCompleteList {
|
||
fmt.Printf("\n%v:\n", o.SelectionLabel)
|
||
}
|
||
|
||
var currentItemIndex int
|
||
sortedGroupsItems := o.getSortedGroupsItems()
|
||
|
||
for _, groupItems := range sortedGroupsItems {
|
||
if !shellCompleteList {
|
||
fmt.Println()
|
||
fmt.Printf("%s\n\n", groupItems.Group)
|
||
}
|
||
|
||
for _, item := range groupItems.Items {
|
||
currentItemIndex++
|
||
if shellCompleteList {
|
||
// plain mode: "index key"
|
||
fmt.Printf("%s\n", o.GetItemKey(item))
|
||
} else {
|
||
// formatted mode: "[index] key"
|
||
fmt.Printf("\t[%d]\t%s\n", currentItemIndex, o.GetItemKey(item))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) HasGroup(group string) (ret bool) {
|
||
for _, groupItems := range o.GroupsItems {
|
||
if ret = strings.EqualFold(groupItems.Group, group); ret {
|
||
break
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) FindGroupsByItemFirst(item I) (ret string) {
|
||
itemKey := o.GetItemKey(item)
|
||
|
||
for _, groupItems := range o.GroupsItems {
|
||
if groupItems.ContainsItemBy(func(groupItem I) bool {
|
||
groupItemKey := o.GetItemKey(groupItem)
|
||
return strings.EqualFold(groupItemKey, itemKey)
|
||
}) {
|
||
ret = groupItems.Group
|
||
break
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
func (o *GroupsItemsSelector[I]) FindGroupsByItem(item I) (groups []string) {
|
||
itemKey := o.GetItemKey(item)
|
||
|
||
for _, groupItems := range o.GroupsItems {
|
||
if groupItems.ContainsItemBy(func(groupItem I) bool {
|
||
groupItemKey := o.GetItemKey(groupItem)
|
||
return strings.EqualFold(groupItemKey, itemKey)
|
||
}) {
|
||
groups = append(groups, groupItems.Group)
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
func ReturnItem(item string) string {
|
||
return item
|
||
}
|
||
|
||
func NewGroupsItemsSelectorString(selectionLabel string) *GroupsItemsSelectorString {
|
||
return &GroupsItemsSelectorString{GroupsItemsSelector: NewGroupsItemsSelector(selectionLabel, ReturnItem)}
|
||
}
|
||
|
||
type GroupsItemsSelectorString struct {
|
||
*GroupsItemsSelector[string]
|
||
}
|