This commit is contained in:
@@ -1,21 +1,28 @@
|
||||
package steps
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"miasma-installer/tui/styles"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/bubbles/list"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type Disk struct {
|
||||
name, description string
|
||||
name string
|
||||
description string
|
||||
size string
|
||||
}
|
||||
|
||||
func (i Disk) FilterValue() string { return i.name }
|
||||
func (i Disk) Title() string { return i.name }
|
||||
func (i Disk) Description() string { return i.description }
|
||||
func (i Disk) Description() string { return fmt.Sprintf("%s - %s", i.size, i.description) }
|
||||
|
||||
type ItemDelegate struct{}
|
||||
|
||||
@@ -30,7 +37,6 @@ func (d ItemDelegate) Render(w io.Writer, m list.Model, index int, item list.Ite
|
||||
|
||||
s := fmt.Sprintf("%d. %s - %s", index+1, disk.Title(), disk.Description())
|
||||
|
||||
// focus styles
|
||||
if index == m.Index() {
|
||||
s = styles.ActiveItemStyle.Render("> " + s)
|
||||
} else {
|
||||
@@ -43,27 +49,85 @@ type DiskSelModel struct {
|
||||
list list.Model
|
||||
Finished bool
|
||||
SelectedDisk string
|
||||
loading bool
|
||||
}
|
||||
|
||||
type disksLoadedMsg struct {
|
||||
disks []list.Item
|
||||
}
|
||||
|
||||
func NewDiskSelModel(width, height int) DiskSelModel {
|
||||
// This is a placeholder - I need to update this to a tea.Cmd to scan the system for drives
|
||||
disks := []list.Item{
|
||||
Disk{name: "/dev/sda", description: "500GB Samsung NVME"},
|
||||
Disk{name: "/dev/sdb", description: "1TB Baracuda HDD"},
|
||||
Disk{name: "/dev/loop0", description: "2GB Fanxiang SSD"},
|
||||
}
|
||||
|
||||
l := list.New(disks, ItemDelegate{}, width, height-5)
|
||||
l := list.New([]list.Item{}, ItemDelegate{}, width, height-5)
|
||||
l.Title = "Select Installation Disk"
|
||||
l.SetShowStatusBar(false)
|
||||
l.SetFilteringEnabled(false)
|
||||
l.Styles.Title = styles.TitleStyle
|
||||
|
||||
return DiskSelModel{list: l}
|
||||
return DiskSelModel{
|
||||
list: l,
|
||||
loading: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (m DiskSelModel) Init() tea.Cmd {
|
||||
return nil
|
||||
return loadDisks
|
||||
}
|
||||
|
||||
func loadDisks() tea.Msg {
|
||||
disks, err := scanDisks()
|
||||
if err != nil {
|
||||
return disksLoadedMsg{disks: []list.Item{
|
||||
Disk{name: "error", description: err.Error(), size: ""},
|
||||
}}
|
||||
}
|
||||
return disksLoadedMsg{disks: disks}
|
||||
}
|
||||
|
||||
func scanDisks() ([]list.Item, error) {
|
||||
cmd := exec.Command("lsblk", "-ndo", "NAME,SIZE,TYPE,MODEL")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list disks: %w", err)
|
||||
}
|
||||
|
||||
var disks []list.Item
|
||||
scanner := bufio.NewScanner(strings.NewReader(string(output)))
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
name := fields[0]
|
||||
size := fields[1]
|
||||
diskType := fields[2]
|
||||
model := ""
|
||||
if len(fields) > 3 {
|
||||
model = strings.Join(fields[3:], " ")
|
||||
}
|
||||
|
||||
if diskType != "disk" {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, "loop") || strings.HasPrefix(name, "sr") {
|
||||
continue
|
||||
}
|
||||
|
||||
disks = append(disks, Disk{
|
||||
name: "/dev/" + name,
|
||||
size: size,
|
||||
description: model,
|
||||
})
|
||||
}
|
||||
|
||||
if len(disks) == 0 {
|
||||
return nil, fmt.Errorf("no suitable disks found")
|
||||
}
|
||||
|
||||
return disks, nil
|
||||
}
|
||||
|
||||
func (m DiskSelModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
@@ -72,8 +136,13 @@ func (m DiskSelModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case disksLoadedMsg:
|
||||
m.loading = false
|
||||
m.list.SetItems(msg.disks)
|
||||
return m, nil
|
||||
|
||||
case tea.KeyMsg:
|
||||
if msg.String() == "enter" {
|
||||
if !m.loading && msg.String() == "enter" {
|
||||
if item, ok := m.list.SelectedItem().(Disk); ok {
|
||||
m.SelectedDisk = item.name
|
||||
m.Finished = true
|
||||
@@ -81,13 +150,46 @@ func (m DiskSelModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
m.list, cmd = m.list.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m DiskSelModel) View() string {
|
||||
if m.loading {
|
||||
return styles.MainStyle.Render(styles.TitleStyle.Render("Scanning disks..."))
|
||||
}
|
||||
|
||||
s := m.list.View()
|
||||
s += "\n" + styles.HelpStyle.Render("Use ↑/↓ to navigate, [Enter] to select disk.")
|
||||
return s
|
||||
}
|
||||
|
||||
func formatBytes(bytes uint64) string {
|
||||
const unit = 1024
|
||||
if bytes < unit {
|
||||
return fmt.Sprintf("%d B", bytes)
|
||||
}
|
||||
div, exp := uint64(unit), 0
|
||||
for n := bytes / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
|
||||
func getDiskSize(device string) (string, error) {
|
||||
data, err := os.ReadFile(fmt.Sprintf("/sys/block/%s/size", strings.TrimPrefix(device, "/dev/")))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
sectors, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bytes := sectors * 512
|
||||
return formatBytes(bytes), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user