mast/cli/main.go
aynish b0ec8fefa3 init: working task add parser for go (#5)
Co-authored-by: Anish Lakhwara <anish+git@lakhwara.com>
Reviewed-on: #5
2024-08-31 23:00:05 -07:00

247 lines
4.6 KiB
Go

package main
import (
"database/sql"
"encoding/base64"
"fmt"
"log"
"os"
mast "mast/db"
parser "mast/parser"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
uuid "github.com/gofrs/uuid"
"github.com/gorilla/websocket"
sqlite3 "github.com/mattn/go-sqlite3"
)
type model struct {
db *sql.DB
conn *websocket.Conn
todos []string
cursor int
textInput textinput.Model
inputting bool
}
func initialModel() model {
sql.Register("sqlite3_with_extensions",
&sqlite3.SQLiteDriver{
Extensions: []string{
"./db/crsqlite.so",
},
})
db, err := sql.Open("sqlite3_with_extensions", "db.sqlite")
if err != nil {
panic(err)
}
err = mast.RunMigrations(db)
if err != nil {
panic(err)
}
ti := textinput.New()
ti.Placeholder = "Add a new todo"
ti.Focus()
conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/sync", nil)
if err != nil {
log.Println("Error connecting to WebSocket:", err)
panic(err)
}
m := model{
db: db,
conn: conn,
todos: []string{},
cursor: 0,
textInput: ti,
inputting: false,
}
m.loadTodos()
return m
}
type CRSQLChange struct {
TableName string
PK string
ColumnName string
Value string
ColVersion int64
DBVersion int64
SiteID string
CL int64
Seq int64
}
func (m *model) addTodo(todo string) {
id, err := uuid.NewV7()
if err != nil {
panic("Error generating new UUID")
}
_, err = m.db.Exec(`
INSERT INTO todos (id, content)
VALUES (?, ?)
`, id.String(), todo)
if err != nil {
panic(err)
}
// TODO:
// func sync_changes
rows, err := m.db.Query("SELECT * FROM crsql_changes")
if err != nil {
log.Println("Error querying crsql_changes:", err)
return
}
defer rows.Close()
var changes []CRSQLChange
for rows.Next() {
var change CRSQLChange
var pkRaw []byte
var valueRaw []byte
var siteIDRaw []byte
err := rows.Scan(&change.TableName, &pkRaw, &change.ColumnName, &valueRaw,
&change.ColVersion, &change.DBVersion, &siteIDRaw, &change.CL, &change.Seq)
if err != nil {
log.Println("Error scanning change:", err)
return
}
change.PK = base64.StdEncoding.EncodeToString(pkRaw)
change.Value = base64.StdEncoding.EncodeToString(valueRaw)
change.SiteID = base64.StdEncoding.EncodeToString(siteIDRaw)
changes = append(changes, change)
fmt.Println(change)
}
if len(changes) > 0 {
m.sendToWebSocket(changes)
}
m.loadTodos()
}
func (m *model) sendToWebSocket(changes []CRSQLChange) {
message := struct {
Type string `json:"type"`
Data []CRSQLChange `json:"data"`
}{
Type: "changes",
Data: changes,
}
err := m.conn.WriteJSON(message)
if err != nil {
log.Println("Error sending changes to WebSocket:", err)
panic(err)
}
}
func (m *model) loadTodos() {
rows, err := m.db.Query("SELECT content FROM todos")
if err != nil {
panic(err)
}
defer rows.Close()
m.todos = []string{}
for rows.Next() {
var content string
if err := rows.Scan(&content); err != nil {
panic(err)
}
m.todos = append(m.todos, content)
}
}
func (m model) Init() tea.Cmd {
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, m.quit()
case "j", "down":
m.cursor = (m.cursor + 1) % len(m.todos)
case "k", "up":
m.cursor = (m.cursor - 1 + len(m.todos)) % len(m.todos)
case "a":
if !m.inputting {
m.inputting = true
return m, textinput.Blink
}
case "enter":
if m.inputting {
m.addTodo(m.textInput.Value())
m.textInput.Reset()
m.inputting = false
}
}
}
if m.inputting {
m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}
return m, nil
}
func (m model) quit() tea.Cmd {
m.db.Close()
m.conn.Close()
return tea.Quit
}
func (m model) View() string {
s := "Todo List:\n\n"
for i, todo := range m.todos {
cursor := " "
if m.cursor == i {
cursor = ">"
}
s += fmt.Sprintf("%s %s\n", cursor, todo)
}
s += "\nPress 'a' to add a new todo, 'q' to quit.\n"
if m.inputting {
s += "\n" + m.textInput.View()
}
return s
}
// TODO:
// Remove the parser test and call it when adding a todo
func main() {
if len(os.Args) < 2 {
fmt.Println("Please provide a Taskwarrior add command to parse")
return
}
input := os.Args[1]
result, err := parser.Parse("", []byte(input))
if err != nil {
fmt.Printf("Error parsing input: %v\n", err)
return
}
fmt.Printf("Parsed result: %+v\n", result)
// p := tea.NewProgram(initialModel())
// if _, err := p.Run(); err != nil {
// fmt.Printf("Alas, there's been an error: %v", err)
// os.Exit(1)
// }
}