Initial giu version, not very good looking, maybe prefer CharmBracelet Tea
This commit is contained in:
24
cmd/sqv-cli/main.go
Normal file
24
cmd/sqv-cli/main.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
engine "git.pablu.de/pablu/sqv-engine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
file, err := os.ReadFile("test.sql")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m := engine.NewManagerFromFile(string(file))
|
||||||
|
err = m.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Print(m)
|
||||||
|
}
|
||||||
93
cmd/sqv-giu/main.go
Normal file
93
cmd/sqv-giu/main.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
engine "git.pablu.de/pablu/sqv-engine"
|
||||||
|
g "github.com/AllenDang/giu"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
tables []engine.Table
|
||||||
|
enabled []bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Test) loop() {
|
||||||
|
tableMenuItems := make([]g.Widget, len(t.tables))
|
||||||
|
|
||||||
|
for i, table := range t.tables {
|
||||||
|
tableMenuItems[i] = g.MenuItem(table.Name).OnClick(func() {
|
||||||
|
t.enabled[i] = !t.enabled[i]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
g.MainMenuBar().Layout(
|
||||||
|
g.Menu("Tables").Layout(
|
||||||
|
tableMenuItems...,
|
||||||
|
),
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
for i, table := range t.tables {
|
||||||
|
if t.enabled[i] {
|
||||||
|
g.Window(table.Name).Layout(
|
||||||
|
SqlTable(table),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SqlTable(table engine.Table) *g.TableWidget {
|
||||||
|
tableColumns := make([]*g.TableColumnWidget, 0)
|
||||||
|
for _, column := range table.Columns {
|
||||||
|
tableColumns = append(tableColumns, g.TableColumn(column.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
tableRows := make([]*g.TableRowWidget, 0)
|
||||||
|
for _, row := range table.Rows {
|
||||||
|
tableRows = append(tableRows, SqlValueRow(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.Table().Columns(
|
||||||
|
tableColumns...,
|
||||||
|
).Rows(tableRows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SqlValueRow(row engine.Row) *g.TableRowWidget {
|
||||||
|
v := make([]g.Widget, 0)
|
||||||
|
for _, values := range row.Values {
|
||||||
|
v = append(v, g.Label(values))
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.TableRow(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
wnd := g.NewMasterWindow("SQV", 800, 600, 0)
|
||||||
|
|
||||||
|
//nolint:gocritic // should be here for doc
|
||||||
|
// errMarkers = imgui.NewErrorMarkers()
|
||||||
|
file, err := os.ReadFile("test.sql")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
m := engine.NewManagerFromFile(string(file))
|
||||||
|
if err := m.Start(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := Test{
|
||||||
|
tables: m.GetTables(),
|
||||||
|
enabled: make([]bool, len(m.GetTables())),
|
||||||
|
}
|
||||||
|
|
||||||
|
t.tables[2].Rows = []engine.Row{
|
||||||
|
engine.Row{
|
||||||
|
Values: []string{
|
||||||
|
"HELLO", "WORLD", "!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
wnd.Run(t.loop)
|
||||||
|
}
|
||||||
21
go.mod
21
go.mod
@@ -1,3 +1,24 @@
|
|||||||
module git.pablu.de/pablu/sqv-engine
|
module git.pablu.de/pablu/sqv-engine
|
||||||
|
|
||||||
go 1.25.3
|
go 1.25.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/AllenDang/giu v0.14.1
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/AllenDang/cimgui-go v1.3.2-0.20250409185506-6b2ff1aa26b5 // indirect
|
||||||
|
github.com/AllenDang/go-findfont v0.0.0-20200702051237-9f180485aeb8 // indirect
|
||||||
|
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 // indirect
|
||||||
|
github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8 // indirect
|
||||||
|
github.com/mazznoer/csscolorparser v0.1.6 // indirect
|
||||||
|
github.com/napsy/go-css v1.0.0 // indirect
|
||||||
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
|
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||||
|
golang.design/x/hotkey v0.4.1 // indirect
|
||||||
|
golang.design/x/mainthread v0.3.0 // indirect
|
||||||
|
golang.org/x/image v0.27.0 // indirect
|
||||||
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
|
gopkg.in/eapache/queue.v1 v1.1.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -8,6 +8,8 @@ github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3 h1:baVdMKlASEHr
|
|||||||
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
|
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3/go.mod h1:VEPNJUlxl5KdWjDvz6Q1l+rJlxF2i6xqDeGuGAxa87M=
|
||||||
github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8 h1:aczNwZRrReVWrZcqxvDjDmxP1NFISTAu+1Cp+3OCbUg=
|
github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8 h1:aczNwZRrReVWrZcqxvDjDmxP1NFISTAu+1Cp+3OCbUg=
|
||||||
github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8/go.mod h1:Z3+NtD1rjXUVZg97dojhs70i5oneOrZ1xcFKfF/c2Ts=
|
github.com/gucio321/glm-go v0.0.0-20241029220517-e1b5a3e011c8/go.mod h1:Z3+NtD1rjXUVZg97dojhs70i5oneOrZ1xcFKfF/c2Ts=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mazznoer/csscolorparser v0.1.6 h1:uK6p5zBA8HaQZJSInHgHVmkVBodUAy+6snSmKJG7pqA=
|
github.com/mazznoer/csscolorparser v0.1.6 h1:uK6p5zBA8HaQZJSInHgHVmkVBodUAy+6snSmKJG7pqA=
|
||||||
github.com/mazznoer/csscolorparser v0.1.6/go.mod h1:OQRVvgCyHDCAquR1YWfSwwaDcM0LhnSffGnlbOew/3I=
|
github.com/mazznoer/csscolorparser v0.1.6/go.mod h1:OQRVvgCyHDCAquR1YWfSwwaDcM0LhnSffGnlbOew/3I=
|
||||||
github.com/napsy/go-css v1.0.0 h1:I1EiqpOJqo8eshGhm6OQXefXOfNgnp1SLOVfqcTeY2U=
|
github.com/napsy/go-css v1.0.0 h1:I1EiqpOJqo8eshGhm6OQXefXOfNgnp1SLOVfqcTeY2U=
|
||||||
|
|||||||
138
manager.go
Normal file
138
manager.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.pablu.de/pablu/sqv-engine/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
parser *sql.Parser
|
||||||
|
|
||||||
|
tables []Table
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManagerFromFile(sqlTxt string) *Manager {
|
||||||
|
return &Manager{
|
||||||
|
parser: sql.NewParser(strings.NewReader(sqlTxt)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Start() error {
|
||||||
|
for {
|
||||||
|
stmt, err := m.parser.Parse()
|
||||||
|
if err != nil && errors.Is(err, io.EOF) {
|
||||||
|
fmt.Println("Finished parsing")
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := stmt.(type) {
|
||||||
|
case *sql.CreateTableStatement:
|
||||||
|
t, err := m.convertCreateTableStatementToTable(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.tables = append(m.tables, t)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("NOT IMPLEMENTED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Refresh(sqlTxt string) error {
|
||||||
|
m.parser = sql.NewParser(strings.NewReader(sqlTxt))
|
||||||
|
m.tables = make([]Table, 0)
|
||||||
|
|
||||||
|
return m.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetTables() []Table {
|
||||||
|
return m.tables
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetTable(name string) (Table, bool) {
|
||||||
|
index := slices.IndexFunc(m.tables, func(t Table) bool {
|
||||||
|
return t.Name == name
|
||||||
|
})
|
||||||
|
|
||||||
|
if index >= 0 {
|
||||||
|
return m.tables[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
return Table{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) convertCreateTableStatementToTable(cts *sql.CreateTableStatement) (Table, error) {
|
||||||
|
res := Table{
|
||||||
|
Name: cts.TableName,
|
||||||
|
Columns: make([]Column, len(cts.Columns)),
|
||||||
|
Rows: make([]Row, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, column := range cts.Columns {
|
||||||
|
flags := extrasToFlags(column.Extra)
|
||||||
|
var ref *Column = nil
|
||||||
|
if flags.Has(FOREIGN_KEY) {
|
||||||
|
index := slices.IndexFunc(column.Extra, func(c string) bool {
|
||||||
|
return strings.HasPrefix(c, "ref")
|
||||||
|
})
|
||||||
|
refExtra := column.Extra[index]
|
||||||
|
refStr := strings.Split(refExtra, " ")[1]
|
||||||
|
|
||||||
|
s := strings.Split(refStr, ".")
|
||||||
|
tableName := s[0]
|
||||||
|
columnName := s[1]
|
||||||
|
|
||||||
|
refTable, ok := m.GetTable(tableName)
|
||||||
|
if !ok {
|
||||||
|
fmt.Println(m.tables)
|
||||||
|
return Table{}, fmt.Errorf("Reference table '%v' not found", tableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
colIndex := slices.IndexFunc(refTable.Columns, func(c Column) bool {
|
||||||
|
return c.Name == columnName
|
||||||
|
})
|
||||||
|
|
||||||
|
ref = &refTable.Columns[colIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Columns[i] = Column{
|
||||||
|
Type: column.Type,
|
||||||
|
Name: column.Name,
|
||||||
|
Reference: ref,
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extrasToFlags(extras []string) ColumnFlag {
|
||||||
|
res := NONE
|
||||||
|
for _, extra := range extras {
|
||||||
|
|
||||||
|
// This is not good
|
||||||
|
switch strings.Split(extra, " ")[0] {
|
||||||
|
case "PRIMARY_KEY":
|
||||||
|
res |= PRIMARY_KEY
|
||||||
|
case "ref":
|
||||||
|
res |= FOREIGN_KEY
|
||||||
|
case "NOT_NULL":
|
||||||
|
res |= NOT_NULL
|
||||||
|
default:
|
||||||
|
log.Panicf("NOT IMPLEMENTED EXTRA: %v", extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
13
sql/ast.go
13
sql/ast.go
@@ -2,6 +2,10 @@ package sql
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
|
type Statement interface {
|
||||||
|
isEnumValue()
|
||||||
|
}
|
||||||
|
|
||||||
type CreateTableStatement struct {
|
type CreateTableStatement struct {
|
||||||
TableName string
|
TableName string
|
||||||
Columns []Column
|
Columns []Column
|
||||||
@@ -22,3 +26,12 @@ type Column struct {
|
|||||||
Type string
|
Type string
|
||||||
Extra []string
|
Extra []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unused, just for example sake for now
|
||||||
|
type SelectStatement struct {
|
||||||
|
From string
|
||||||
|
Fields []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ *CreateTableStatement) isEnumValue() {}
|
||||||
|
func (_ *SelectStatement) isEnumValue() {}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func NewParser(r io.Reader) *Parser {
|
|||||||
return &Parser{s: NewLexer(r)}
|
return &Parser{s: NewLexer(r)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) Parse() (*CreateTableStatement, error) {
|
func (p *Parser) Parse() (Statement, error) {
|
||||||
tok, ok := p.expectOne(CREATE, EOF)
|
tok, ok := p.expectOne(CREATE, EOF)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, p.unexpectedToken(CREATE, EOF)
|
return nil, p.unexpectedToken(CREATE, EOF)
|
||||||
|
|||||||
@@ -7,7 +7,5 @@ import (
|
|||||||
|
|
||||||
|
|
||||||
func arrayToString(a []Token, delim string) string {
|
func arrayToString(a []Token, delim string) string {
|
||||||
return strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
|
return strings.Trim(strings.ReplaceAll(fmt.Sprint(a), " ", delim), "[]")
|
||||||
//return strings.Trim(strings.Join(strings.Split(fmt.Sprint(a), " "), delim), "[]")
|
|
||||||
//return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(a)), delim), "[]")
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
table.go
27
table.go
@@ -2,33 +2,38 @@ package engine
|
|||||||
|
|
||||||
type Table struct {
|
type Table struct {
|
||||||
Name string
|
Name string
|
||||||
TableValues []TableValue
|
Columns []Column
|
||||||
Rows []Row
|
Rows []Row
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValueFlags uint32
|
type ColumnFlag uint32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PRIMARY_KEY ValueFlags = 1 << iota
|
PRIMARY_KEY ColumnFlag = 1 << iota
|
||||||
FOREIGN_KEY
|
FOREIGN_KEY
|
||||||
NOT_NULL
|
NOT_NULL
|
||||||
|
|
||||||
|
NONE ColumnFlag = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v ValueFlags) Has(flag ValueFlags) bool {
|
func (v ColumnFlag) Has(flag ColumnFlag) bool {
|
||||||
return v&flag == flag
|
return v&flag == flag
|
||||||
}
|
}
|
||||||
|
|
||||||
type TableValue struct {
|
type Column struct {
|
||||||
Type string
|
Type string
|
||||||
Name string
|
Name string
|
||||||
Reference *TableValue
|
Reference *Column
|
||||||
Flags ValueFlags
|
Flags ColumnFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
type Value interface {
|
|
||||||
Representation() string
|
// For testing purposes its string right now
|
||||||
}
|
// type Value interface {
|
||||||
|
// Representation() string
|
||||||
|
// }
|
||||||
|
|
||||||
type Row struct {
|
type Row struct {
|
||||||
Values map[TableValue]Value
|
// This should be map but to Column so its always the right Column but for testing this is a slice now
|
||||||
|
Values []string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,7 @@
|
|||||||
package main
|
CREATE TABLE TEST(
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.pablu.de/pablu/sqv-engine/sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
s := `CREATE TABLE TEST(
|
|
||||||
ID text PRIMARY KEY
|
ID text PRIMARY KEY
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE sessions (
|
|
||||||
session_id text PRIMARY KEY,
|
|
||||||
access_token text NOT NULL,
|
|
||||||
user_email text NOT NULL,
|
|
||||||
FOREIGN KEY(user_email) REFERENCES users(email)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
email text PRIMARY KEY,
|
email text PRIMARY KEY,
|
||||||
username text
|
username text
|
||||||
@@ -60,20 +40,3 @@ CREATE TABLE IF NOT EXISTS auth_states (
|
|||||||
state_id text PRIMARY KEY,
|
state_id text PRIMARY KEY,
|
||||||
code_verifier text NOT NULL
|
code_verifier text NOT NULL
|
||||||
);
|
);
|
||||||
`
|
|
||||||
|
|
||||||
parser := sql.NewParser(strings.NewReader(s))
|
|
||||||
|
|
||||||
for {
|
|
||||||
stmt, err := parser.Parse()
|
|
||||||
if err != nil && errors.Is(err, io.EOF) {
|
|
||||||
fmt.Println("Finished parsing")
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt.Print()
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user