func PromptCustomResource[T any]()

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
}