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
}