Untitled

mail@pastecode.io avatar
unknown
golang
a year ago
5.8 kB
11
Indexable
Never
package main

import (
	"database/sql"
	"html/template"
	"log"
	"net/http"
	"strings"

	"github.com/gorilla/sessions"
	_ "github.com/lib/pq"
)

/******************************************************************************
*
*	Begin execution
*
******************************************************************************/

func main() {
	initdb()
	http.HandleFunc("/", handle_req)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

/******************************************************************************
*
*	Global vars
*
******************************************************************************/

var db *sql.DB

/******************************************************************************
*
*	Structs
*
******************************************************************************/

// Node records
type node struct {
	id             int
	prompt         string
	return_node_id int
	option_name    string
	options        []string
}

// List node options as a string for presentation.
func (n node) optstr() string {
	var opts = n.options
	if n.return_node_id != 0 {
		// The "return" option is available
		opts = append(opts, "return")
	}

	var optstr = "Options: " + strings.Join(opts, ", ")
	return optstr
}

// HTML template variables
type template_vars struct {
	Intro   string
	Prompt  string
	Options string
}

/******************************************************************************
*
*	Initialize database
*
******************************************************************************/

func initdb() {
	var err error

	// postgres_url is defined in secrets.go
	db, err = sql.Open("postgres", postgres_url)

	// Exit program if the database does not connect
	if err != nil {
		panic(err)
	}
}

/******************************************************************************
*
*	Handle HTTP request
*
******************************************************************************/

func handle_req(rw http.ResponseWriter, req *http.Request) {
	// Access session values
	var sess = load_session(req)
	var currn = currn(sess)

	// Access form values
	var opt = form_values(req)

	// Calculate next node
	var nextn = nextn(currn, opt)

	// Update session
	set_currn(sess, nextn)
	sess.Save(req, rw)

	// Render HTML template
	render_template(rw, nextn)
}

func load_session(req *http.Request) *sessions.Session {
	// sessionKey is defined in secrets.go
	var store = sessions.NewCookieStore([]byte(session_key))
	var sess, _ = store.Get(req, "session")
	return sess
}

func form_values(req *http.Request) string {
	return req.FormValue("option")
}

/******************************************************************************
*
*	Render HTML template
*
******************************************************************************/

func set_template_vars(nextn node) template_vars {
	return template_vars{
		Intro:   "Hello, welcome to the Yo'el Mikha'el simulator.",
		Prompt:  nextn.prompt,
		Options: nextn.optstr(),
	}
}

func render_template(rw http.ResponseWriter, nextn node) {
	var tmpl, _ = template.ParseFiles("index.html")
	var tvars = set_template_vars(nextn)
	tmpl.Execute(rw, tvars)
}

/******************************************************************************
*
*	Access session data
*
******************************************************************************/

// Find current node from session
func currn(sess *sessions.Session) node {
	var value, ok = sess.Values["current_node_id"]
	var id int
	if ok {
		id = value.(int)
	}

	if id != 0 {
		// A current node is found in the session.
		return loadn(id)
	} else {
		// A current node has not been found in the session.
		id = select_default_node_id()
		sess.Values["current_node_id"] = id
		return loadn(id)
	}
}

// Set current node in session
func set_currn(sess *sessions.Session, nextn node) {
	sess.Values["current_node_id"] = nextn.id
}

func select_default_node_id() int {
	var row = db.QueryRow(
		"SELECT id FROM nodes " +
			"WHERE return_node_id IS NULL ORDER BY id LIMIT 1")
	var id int
	row.Scan(&id)
	return id
}

/******************************************************************************
*
*	Select records from the database
*
******************************************************************************/

func loadn(id int) node {
	var n node
	select_node_cols(&n, id)
	select_node_options(&n, id)
	return n
}

func select_node_cols(n *node, id int) {
	var row = db.QueryRow(
		"SELECT id, prompt, return_node_id, option_name "+
			"FROM nodes WHERE id = $1", id)
	row.Scan(&n.id, &n.prompt, &n.return_node_id, &n.option_name)
}

func select_node_options(n *node, id int) {
	var rows, _ = db.Query(
		"SELECT option_name FROM nodes "+
			"WHERE return_node_id = $1", id)
	var opt string
	for rows.Next() {
		rows.Scan(&opt)
		n.options = append(n.options, opt)
	}
}

/******************************************************************************
*
*	Calculate next node based on selected option
*
******************************************************************************/

func nextn(currn node, opt string) node {
	var id = select_nextn_id(currn, opt)

	if id != 0 {
		// A next node has been found.
		return loadn(id)
	}

	if opt == "return" {
		return handle_return_opt(currn)
	}

	// There is neither a next node, nor a return node.
	return currn
}

func select_nextn_id(currn node, opt string) int {
	// Look for next node based on return node and option name.
	var row = db.QueryRow(
		"SELECT id FROM nodes "+
			"WHERE return_node_id = $1 AND option_name = $2",
		currn.id, opt)
	var id int
	row.Scan(&id)
	return id
}

func handle_return_opt(currn node) node {
	if currn.return_node_id != 0 {
		// A return node is set
		return loadn(currn.return_node_id)
	} else {
		// No return node is set
		return currn
	}
}