internal/ui/importlist/importlist_delegate.go (135 lines of code) (raw):

package importlist import ( "fmt" "strings" "github.com/Azure/aztfexport/internal/tfaddr" "github.com/Azure/aztfexport/internal/ui/aztfexportclient" "github.com/Azure/aztfexport/internal/ui/common" "github.com/magodo/tfadd/providers/azapi" "github.com/magodo/tfadd/providers/azurerm" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" ) func NewImportItemDelegate(providerName string) list.ItemDelegate { d := list.NewDefaultDelegate() d.UpdateFunc = func(msg tea.Msg, m *list.Model) (ret tea.Cmd) { sel := m.SelectedItem() if sel == nil { return nil } selItem := sel.(Item) idx := selItem.idx var cmds []tea.Cmd defer func() { cmd := m.SetItem(idx, selItem) cmds = append(cmds, cmd) ret = tea.Batch(cmds...) }() // For the item that is not focused (i.e. the textinput is not focused) if !selItem.textinput.Focused() { switch msg := msg.(type) { case tea.KeyMsg: switch { case msg.Type == tea.KeyEnter: // Clear the validation error that were set. selItem.v.ValidateError = nil // Clear the imported flag that were set, which means this resource will be imported again. // This allows the user to change its mind for importing this resource as another resource type. // (e.g. vm resource -> either azurerm_virtual_machine or azurerm_linux_virtual_machine) if selItem.v.Imported { cmd := aztfexportclient.CleanTFState(selItem.v.TFAddr.String()) cmds = append(cmds, cmd) selItem.v.Imported = false } // Clear the is recommended flag that were set. selItem.v.IsRecommended = false // "Enter" focus current selected item setListKeyMapEnabled(m, false) cmd := selItem.textinput.Focus() cmds = append(cmds, cmd) return } } return } // The item is focused. switch msg := msg.(type) { case tea.KeyMsg: switch msg.Type { case tea.KeyEnter, tea.KeyEsc: // Enter and ESC un-focus the textinput setListKeyMapEnabled(m, true) selItem.textinput.Blur() // Validate the input and update the selItem.v addr, err := parseInput(selItem.textinput.Value(), providerName) if err != nil { cmd := m.NewStatusMessage(common.ErrorMsgStyle.Render(err.Error())) cmds = append(cmds, cmd) selItem.v.ValidateError = err return } // Check the uniqueness of the resource name among the resource type // TODO: this is not ideal to construct the resource name mapping everytime. tfNames := map[string]map[string]bool{} for i, item := range m.Items() { v := item.(Item).v if v.Skip() || m.Index() == i { continue } if _, ok := tfNames[v.TFAddr.Type]; !ok { tfNames[v.TFAddr.Type] = map[string]bool{} } tfNames[v.TFAddr.Type][v.TFAddr.Name] = true } if mm, ok := tfNames[addr.Type]; ok && mm[addr.Name] { err := fmt.Errorf("%q already exists", addr) cmd := m.NewStatusMessage(common.ErrorMsgStyle.Render(err.Error())) cmds = append(cmds, cmd) selItem.v.ValidateError = err return } selItem.v.ValidateError = nil selItem.v.TFAddr = *addr selItem.v.TFAddrCache = *addr return } } var cmd tea.Cmd selItem.textinput, cmd = selItem.textinput.Update(msg) cmds = append(cmds, cmd) return } return d } func setListKeyMapEnabled(m *list.Model, enabled bool) { m.KeyMap.CursorUp.SetEnabled(enabled) m.KeyMap.CursorDown.SetEnabled(enabled) m.KeyMap.NextPage.SetEnabled(enabled) m.KeyMap.PrevPage.SetEnabled(enabled) m.KeyMap.GoToStart.SetEnabled(enabled) m.KeyMap.GoToEnd.SetEnabled(enabled) m.KeyMap.Filter.SetEnabled(enabled) if enabled { m.KeyMap.ClearFilter.SetEnabled(m.FilterState() == list.FilterApplied) } else { m.KeyMap.ClearFilter.SetEnabled(enabled) } // m.KeyMap.CancelWhileFiltering.SetEnabled(enabled) // m.KeyMap.AcceptWhileFiltering.SetEnabled(enabled) m.KeyMap.ShowFullHelp.SetEnabled(enabled) m.KeyMap.CloseFullHelp.SetEnabled(enabled) m.KeyMap.Quit.SetEnabled(enabled) if enabled { bindKeyHelps(m, newListKeyMap().ToBindings()) } else { bindKeyHelps(m, nil) } } func parseInput(input string, providerName string) (*tfaddr.TFAddr, error) { v := strings.TrimSpace(input) if v == "" { return &tfaddr.TFAddr{}, nil } addr, err := tfaddr.ParseTFResourceAddr(v) if err != nil { return nil, err } var ok bool switch providerName { case "azurerm": _, ok = azurerm.ProviderSchemaInfo.ResourceSchemas[addr.Type] case "azapi": _, ok = azapi.ProviderSchemaInfo.ResourceSchemas[addr.Type] } if !ok { return nil, fmt.Errorf("Invalid resource type %q", addr.Type) } return addr, nil }