// Debby is a command line tool that links your dolt database to visidata package main import ( "bufio" "fmt" "os" "os/exec" "strings" ) func main() { var choice int var table string var query string var args []string var noninteractive bool var err error if len(os.Args) > 1 { noninteractive = true switch os.Args[1] { case "edit": // Import table choice = 2 table = os.Args[2] case "run": // Run query choice = 1 query = strings.Join(os.Args[2:], " ") case "help": // Print help str := `debby is a command line tool that links your dolt database to visidata Usage: debby [command] [arguments] The commands are: edit Import table run Run query help Print help Runnning debby without any arguments will start debby in interactive mode. Running debby with any other arguments will run it as a dolt command.` fmt.Println(str) return default: // Run dolt command choice = 3 args = os.Args[1:] } } // Menu loop. 1. Run query 2. Edit a table 3. Execute Dolt command 4. Exit for { tables := ReadTableNames() // Execute choice switch choice { case 1: if query == "" { // Request query fmt.Print("Enter your query: ") query = Readline() } // Table name will be empty. Run query err = RunSQL(query, "") if err != nil { fmt.Println(err) } case 2: if table == "" { // Request table name fmt.Print("Enter your table name: ") table = Readline() } // Run query err = RunSQL("select * from "+table+";", table) if err != nil { fmt.Println(err) } case 3: if len(args) == 0 { // Request command. Make dolt bold fmt.Print("\033[1mdolt\033[0m ") argstr := Readline() args = strings.Split(argstr, " ") } // Execute command ExecuteDoltCommand(args...) case 4: // Exit return } // If noninteractive, exit if noninteractive { return } choice = 0 // Print menu // List tables sequentially fmt.Println("Tables: ", strings.Join(tables, ", ")) fmt.Println() fmt.Print("1. Run query ") fmt.Print("2. Edit table ") fmt.Print("3. Execute Dolt command ") fmt.Print("4. Exit ") fmt.Print("- Enter your choice: ") // Read user input choicestr := Readline() choice = int(choicestr[0] - '0') // Reset all variables except choice table = "" query = "" args = []string{} } } func Readline() string { // Read a line from stdin reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') return text[:len(text)-1] } func ExecuteDoltCommand(args ...string) { cmd := exec.Command("dolt", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Run() } func SaveToTable(table, file string) error { // Run dolt table import -r table file cmd := exec.Command("dolt", "table", "import", "-r", table, file) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin return cmd.Run() } func ReadTableNames() []string { // Run dolt ls, ignore the first line and strip spaces for each following line cmd := exec.Command("dolt", "ls") cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { return []string{} } lines := strings.Split(string(out), "\n") tables := []string{} for i, line := range lines { line = strings.TrimSpace(line) if i == 0 || line == "" { continue } tables = append(tables, line) } return tables } func RunSQL(query, table string) error { // Create a temporary file to open in visidata f, err := os.CreateTemp("", "debby-*.csv") if err != nil { return err } defer os.Remove(f.Name()) // Run the query and write the output to the file cmd := exec.Command("dolt", "sql", "-q", query, "-r", "csv") cmd.Stdout = f err = cmd.Run() if err != nil { return err } // Open the file in visidata err = OpenVisidata(f.Name(), "-f", "csv") if err != nil { return err } // If table is not empty, save the file to the table if table != "" { fmt.Println("Saving to table...") err = SaveToTable(table, f.Name()) if err != nil { return err } } // Read the file back into memory return nil } func OpenVisidata(args ...string) error { cmd := exec.Command("visidata", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin return cmd.Run() }