134 lines
2.9 KiB
Go
134 lines
2.9 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"strings"
|
||
|
|
||
|
"zombiezen.com/go/sqlite"
|
||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||
|
)
|
||
|
|
||
|
type Result struct{ Expected, Actual fs.FileMode }
|
||
|
|
||
|
type SQLiteEmitter struct {
|
||
|
conn *sqlite.Conn
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteEmitter) Close() error {
|
||
|
return s.conn.Close()
|
||
|
}
|
||
|
|
||
|
func sqlViewColSpec() string {
|
||
|
var cols []string
|
||
|
var whos = []rune{'o', 'g', 'u'}
|
||
|
var whats = []rune{'x', 'w', 'r'}
|
||
|
for _, fieldName := range []string{"expected", "actual"} {
|
||
|
cols = append(cols, fmt.Sprintf("%s", fieldName))
|
||
|
for iwho, who := range whos {
|
||
|
cols = append(cols, fmt.Sprintf("%[1]s & (7 << %[3]d * 3) >> (%[3]d * 3) AS %[1]s_%[2]c", fieldName, who, iwho))
|
||
|
for iwhat, what := range whats {
|
||
|
cols = append(cols, fmt.Sprintf("%[1]s & (1 << %[3]d * 3 + %[5]d) >> (%[3]d * 3 + %[5]d) AS %[1]s_%[2]c%[4]c", fieldName, who, iwho, what, iwhat))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return strings.Join(cols, ",\n")
|
||
|
}
|
||
|
|
||
|
const createTable = `CREATE TABLE result_raw (
|
||
|
expected INT NOT NULL,
|
||
|
actual INT NOT NULL
|
||
|
);`
|
||
|
|
||
|
var createView string = `CREATE VIEW result AS
|
||
|
SELECT
|
||
|
` + sqlViewColSpec() + `
|
||
|
FROM result_raw;`
|
||
|
|
||
|
func NewSQLite(path string) (*SQLiteEmitter, error) {
|
||
|
conn, err := sqlite.OpenConn(path, sqlite.OpenCreate|sqlite.OpenReadWrite|sqlite.OpenWAL)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
fmt.Println(createView)
|
||
|
err = sqlitex.Execute(conn, createTable, &sqlitex.ExecOptions{})
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("NewSQLite: cannot create table: %w", err)
|
||
|
}
|
||
|
err = sqlitex.Execute(conn, createView, &sqlitex.ExecOptions{})
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("NewSQLite: cannot create view: %w", err)
|
||
|
}
|
||
|
return &SQLiteEmitter{
|
||
|
conn: conn,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (s *SQLiteEmitter) Emit(r Result) error {
|
||
|
return sqlitex.Execute(s.conn, "INSERT INTO result_raw(expected, actual) VALUES (?, ?)", &sqlitex.ExecOptions{
|
||
|
Args: []any{r.Expected, r.Actual},
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func emitCSV(r Result) error {
|
||
|
_, err := fmt.Printf("%03o,%03o\n", r.Expected, r.Actual)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("emitCSV: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func getData(emit func(Result) error) (err error) {
|
||
|
f, err := os.CreateTemp("", "go-perm-test-*")
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
path := f.Name()
|
||
|
f.Close()
|
||
|
|
||
|
for mode := fs.FileMode(0); mode <= os.ModePerm; mode++ {
|
||
|
err := os.Chmod(path, mode)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("cannot set mode 0%03o on %s: %w", mode, path, err)
|
||
|
}
|
||
|
info, err := os.Stat(path)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("cannot stat %s: %w", path, err)
|
||
|
}
|
||
|
result := Result{
|
||
|
Expected: mode,
|
||
|
Actual: info.Mode(),
|
||
|
}
|
||
|
err = emit(result)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func run() error {
|
||
|
var emit func(Result) error
|
||
|
if len(os.Args) < 2 {
|
||
|
emit = emitCSV
|
||
|
} else {
|
||
|
dbPath := os.Args[1]
|
||
|
db, err := NewSQLite(dbPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer db.Close()
|
||
|
emit = db.Emit
|
||
|
}
|
||
|
return getData(emit)
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
err := run()
|
||
|
if err != nil {
|
||
|
fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err)
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
}
|