internal/ui/progress/progress.go (120 lines of code) (raw):

package progress import ( "context" "fmt" "github.com/Azure/aztfexport/pkg/meta" "github.com/Azure/aztfexport/internal/ui/aztfexportclient" "github.com/Azure/aztfexport/internal/ui/common" prog "github.com/charmbracelet/bubbles/progress" tea "github.com/charmbracelet/bubbletea" ) type result struct { item meta.ImportItem emoji string } type Model struct { ctx context.Context c meta.Meta l meta.ImportList idx int parallelism int results []result progress prog.Model } func NewModel(ctx context.Context, c meta.Meta, parallelism int, l meta.ImportList) Model { return Model{ ctx: ctx, c: c, l: l, idx: 0, parallelism: parallelism, results: make([]result, common.ProgressShowLastResults), progress: prog.NewModel(prog.WithDefaultGradient()), } } func (m Model) Init() tea.Cmd { if m.iterationDone() { return aztfexportclient.FinishImport(m.l) } n := m.parallelism if m.idx+m.parallelism > len(m.l) { n = len(m.l) - m.idx } return tea.Batch( aztfexportclient.ImportItems(m.ctx, m.c, m.l[m.idx:m.idx+n]), ) } func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.progress.Width = msg.Width - 4 return m, nil // FrameMsg is sent when the progress bar wants to animate itself case prog.FrameMsg: progressModel, cmd := m.progress.Update(msg) m.progress = progressModel.(prog.Model) return m, cmd case aztfexportclient.ImportItemsDoneMsg: var cmds []tea.Cmd // Update results items := msg.Items for i := range items { m.l[m.idx+i] = items[i] emoji := common.RandomHappyEmoji() if items[i].ImportError != nil { emoji = common.WarningEmoji } res := result{ item: items[i], emoji: emoji, } m.results = append(m.results[1:], res) } m.idx += m.parallelism if m.iterationDone() { cmds = append(cmds, m.progress.SetPercent(1)) cmds = append(cmds, aztfexportclient.FinishImport(m.l)) return m, tea.Batch(cmds...) } cmds = append(cmds, m.progress.SetPercent(float64(m.idx)/float64(len(m.l)))) n := m.parallelism if m.idx+m.parallelism > len(m.l) { n = len(m.l) - m.idx } cmds = append(cmds, aztfexportclient.ImportItems(m.ctx, m.c, m.l[m.idx:m.idx+n])) return m, tea.Batch(cmds...) default: return m, nil } } func (m Model) View() string { msg := "" if len(m.l) > m.idx { item := m.l[m.idx] if item.Skip() { msg = fmt.Sprintf(" Skipping %s...", item.TFResourceId) } else { msg = fmt.Sprintf(" Importing %s...", item.TFResourceId) } } s := fmt.Sprintf(" %s\n\n", msg) for _, res := range m.results { // This indicates the state before the item is inserted as the to results. if res.item.TFResourceId == "" { s += "...\n" } else { switch { case res.item.Skip(): s += fmt.Sprintf("%s %s skipped\n", res.emoji, res.item.TFResourceId) default: if res.item.ImportError == nil { s += fmt.Sprintf("%s %s import successfully\n", res.emoji, res.item.TFResourceId) } else { s += fmt.Sprintf("%s %s import failed\n", res.emoji, res.item.TFResourceId) } } } } s += "\n\n" + m.progress.View() return s } func (m Model) iterationDone() bool { return m.idx >= len(m.l) }