in cli/azd/pkg/prompt/prompt_service.go [666:819]
func PromptCustomResource[T any](ctx context.Context, options CustomResourceOptions[T]) (*T, error) {
mergedSelectorOptions := &SelectOptions{}
if options.SelectorOptions == nil {
options.SelectorOptions = &SelectOptions{}
}
defaultSelectorOptions := &SelectOptions{
Message: "Select resource",
LoadingMessage: "Loading resources...",
HelpMessage: "Choose a resource for your project.",
AllowNewResource: ux.Ptr(true),
ForceNewResource: ux.Ptr(false),
NewResourceMessage: "Create new resource",
DisplayNumbers: ux.Ptr(true),
DisplayCount: 10,
}
if err := mergo.Merge(mergedSelectorOptions, options.SelectorOptions, mergo.WithoutDereference); err != nil {
return nil, err
}
if err := mergo.Merge(mergedSelectorOptions, defaultSelectorOptions, mergo.WithoutDereference); err != nil {
return nil, err
}
allowNewResource := mergedSelectorOptions.AllowNewResource != nil && *mergedSelectorOptions.AllowNewResource
forceNewResource := mergedSelectorOptions.ForceNewResource != nil && *mergedSelectorOptions.ForceNewResource
var resources []*T
var selectedIndex *int
if forceNewResource {
allowNewResource = true
selectedIndex = ux.Ptr(0)
} else {
loadingSpinner := ux.NewSpinner(&ux.SpinnerOptions{
Text: options.SelectorOptions.LoadingMessage,
})
err := loadingSpinner.Run(ctx, func(ctx context.Context) error {
resourceList, err := options.LoadData(ctx)
if err != nil {
return err
}
resources = resourceList
return nil
})
if err != nil {
return nil, err
}
if !allowNewResource && len(resources) == 0 {
return nil, ErrNoResourcesFound
}
if options.SortResource != nil {
slices.SortFunc(resources, options.SortResource)
}
var defaultIndex *int
if options.Selected != nil {
for i, resource := range resources {
if options.Selected(resource) {
defaultIndex = &i
break
}
}
}
hasCustomDisplay := options.DisplayResource != nil
var choices []*ux.SelectChoice
if allowNewResource {
choices = make([]*ux.SelectChoice, len(resources)+1)
choices[0] = &ux.SelectChoice{
Label: mergedSelectorOptions.NewResourceMessage,
}
if defaultIndex != nil {
*defaultIndex++
}
} else {
choices = make([]*ux.SelectChoice, len(resources))
}
for i, resource := range resources {
var displayValue string
if hasCustomDisplay {
customDisplayValue, err := options.DisplayResource(resource)
if err != nil {
return nil, err
}
displayValue = customDisplayValue
} else {
displayValue = fmt.Sprintf("%v", resource)
}
choice := &ux.SelectChoice{
Value: displayValue,
Label: displayValue,
}
if allowNewResource {
choices[i+1] = choice
} else {
choices[i] = choice
}
}
resourceSelector := ux.NewSelect(&ux.SelectOptions{
Message: mergedSelectorOptions.Message,
HelpMessage: mergedSelectorOptions.HelpMessage,
DisplayCount: mergedSelectorOptions.DisplayCount,
DisplayNumbers: mergedSelectorOptions.DisplayNumbers,
Hint: mergedSelectorOptions.Hint,
EnableFiltering: mergedSelectorOptions.EnableFiltering,
Writer: mergedSelectorOptions.Writer,
Choices: choices,
SelectedIndex: defaultIndex,
})
userSelectedIndex, err := resourceSelector.Ask(ctx)
if err != nil {
return nil, err
}
if userSelectedIndex == nil {
return nil, ErrNoResourceSelected
}
selectedIndex = userSelectedIndex
}
var selectedResource *T
// Create new resource
if allowNewResource && *selectedIndex == 0 {
selectedResource = &options.NewResourceValue
} else {
// If a new resource is allowed, decrement the selected index
if allowNewResource {
*selectedIndex--
}
selectedResource = resources[*selectedIndex]
}
return selectedResource, nil
}