You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
4.8 KiB
Go

package main
import (
"database/sql"
"fmt"
"log"
"github.com/goccy/go-graphviz/cgraph"
_ "github.com/mattn/go-sqlite3"
)
func NewMapper(fn string) (AardMapper, error) {
var am AardMapper
db, err := sql.Open("sqlite3", fn)
if err != nil {
return am, fmt.Errorf("error opening mapper sqlite3 database: %s\n", err)
}
am.DB = db
return am, nil
}
type AardMapper struct {
DB *sql.DB
RoomCache map[string]Room
AreaCache map[string]Area
}
func (am AardMapper) Areas() (map[string]Area, error) {
areas := make(map[string]Area)
rows, err := am.DB.Query("select uid, name, texture, color, flags from areas;")
defer rows.Close()
if err != nil {
return nil, fmt.Errorf("error selecting areas: %w", err)
}
for rows.Next() {
a := Area{}
err := rows.Scan(&a.Uid, &a.Name, &a.Texture, &a.Color, &a.Flags)
if err != nil {
return nil, fmt.Errorf("error scanning Areas: %d", err)
}
areas[a.Uid.String] = a
}
return areas, nil
}
func (am AardMapper) Area(uid string) (Area, error) {
a, ok := am.AreaCache[uid]
if ok {
return a, nil
}
a = Area{}
row := am.DB.QueryRow("select uid, name, flags, color, texture from areas where uid = :uid")
if err := row.Scan(&a.Uid, &a.Name, &a.Flags, &a.Color, &a.Texture); err != nil {
if err == sql.ErrNoRows {
return a, fmt.Errorf("no area found: %w", err)
}
}
return a, nil
}
func (am AardMapper) Rooms() ([]Room, error) {
var uidCount int
uidCountRow := am.DB.QueryRow("select count(uid) from rooms;")
uidCountRow.Scan(&uidCount)
log.Println("uidCount:", uidCount)
rows, err := am.DB.Query("select uid from rooms;")
if err != nil {
return nil, fmt.Errorf("error querying room UIDs: %w", err)
}
var uids = make([]string, uidCount)
i := 0
for rows.Next() {
var uid string
if err := rows.Scan(&uid); err != nil {
return nil, fmt.Errorf("error querying room %q from uid: %w", uid, err)
}
uids[i] = uid
}
rooms := make([]Room, uidCount)
for idx, uid := range uids {
room, err := am.Room(uid)
if err != nil {
return rooms, fmt.Errorf("error fetching room: %s", err)
}
rooms[idx] = room
}
return rooms, nil
}
func (am AardMapper) Room(uid string) (Room, error) {
r := Room{}
row := am.DB.QueryRow("select uid,name,building,info,notes,flags,area,norecall,noportal from rooms where uid = ?", uid)
if err := row.Scan(&r.Uid, &r.Name, &r.Building, &r.Info, &r.Notes, &r.Flags, &r.AreaName, &r.Norecall, &r.Noportal); err != nil {
if err == sql.ErrNoRows {
return r, fmt.Errorf("no room found: %w", err)
}
log.Println("room found:", r.Uid.String)
}
return r, nil
}
func (am AardMapper) Exits(uid string) ([]RoomExit, error) {
exits := make([]RoomExit, 0)
rows, err := am.DB.Query("select r.uid,r.name,r.building,r.info,r.notes,r.flags,r.area, r.norecall,r.noportal,e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid where uid=:uid;", uid)
// rows, err := am.DB.Query("select e.dir,e.fromuid,e.touid,e.level from rooms r INNER JOIN exits e ON r.uid = e.fromuid;")
defer rows.Close()
if err != nil {
return nil, fmt.Errorf("error selecting areas: %w", err)
}
for rows.Next() {
e := RoomExit{}
// err := rows.Scan(&e.Dir, &e.Fromuid, &e.Touid, &e.Level)
err := rows.Scan(&e.Uid, &e.Name, &e.Building, &e.Info, &e.Notes, &e.Flags, &e.AreaName, &e.Norecall, &e.Noportal, &e.Dir, &e.Fromuid, &e.Touid, &e.Level)
if err != nil {
return nil, fmt.Errorf("error scanning Areas: %d", err)
}
exits = append(exits, e)
}
return exits, nil
}
func (am AardMapper) Walk(uid string, g *cgraph.Graph) error {
originRoom, err := am.Room(uid)
if err != nil {
return fmt.Errorf("error querying origin room for walk %q: %w", uid, err)
}
area, err := am.Area(originRoom.AreaName.String)
if err != nil {
return fmt.Errorf("error querying origin area for walk %q: %w", uid, err)
}
sg := g.SubGraph("cluster_"+area.Name.String, 1)
sg.SetLabel(area.Name.String)
originNode, err := sg.CreateNode(originRoom.Uid.String)
if err != nil {
return fmt.Errorf("error creating origin node for room %s: %w", originRoom.Name.String, err)
}
sg.SetLabel(originRoom.Name.String)
exits, err := am.Exits(originRoom.Uid.String)
if err != nil {
return fmt.Errorf("error fetching exits for origin room %s: %w", originRoom.Name.String, err)
}
for _, exit := range exits {
destRoom, err := am.Room(exit.Touid.String)
if err != nil {
return fmt.Errorf("error querying destination room %s: %w", uid, err)
}
destNode, err := sg.CreateNode(destRoom.Uid.String)
destNode.SetLabel(destRoom.Name.String)
if err != nil {
return fmt.Errorf("error creating destination node for room %s: %w", originRoom.Name.String, err)
}
// maybe i'll save recursion for later
// am.Walk(exit.Touid.String, g) // gotta prevent cycles. check if edge already exists and return?
sg.CreateEdge(originRoom.Uid.String+">"+originRoom.Uid.String, originNode, destNode)
}
return nil
}