cmd/relui: move implementation to internal/relui

This makes it explicit that the code is not intended to be used outside
this module.

For golang/go#47401

Change-Id: I6c7ca7402b451dbcfac39e504d5fc767811c973d
Reviewed-on: https://go-review.googlesource.com/c/build/+/347292
Trust: Alexander Rakoczy <alex@golang.org>
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/cmd/relui/Dockerfile.test b/cmd/relui/Dockerfile.test
index 64c3302..037fc8a 100644
--- a/cmd/relui/Dockerfile.test
+++ b/cmd/relui/Dockerfile.test
@@ -15,4 +15,4 @@
 
 RUN go build golang.org/x/build/cmd/relui
 
-ENTRYPOINT go test -v ./cmd/relui/...
+ENTRYPOINT go test -v ./cmd/relui/... ./internal/relui/...
diff --git a/cmd/relui/main.go b/cmd/relui/main.go
index 660f909..667bdd8 100644
--- a/cmd/relui/main.go
+++ b/cmd/relui/main.go
@@ -12,8 +12,9 @@
 	"context"
 	"flag"
 	"log"
-	"net/http"
 	"os"
+
+	"golang.org/x/build/internal/relui"
 )
 
 var (
@@ -22,21 +23,14 @@
 
 func main() {
 	ctx := context.Background()
-	d := new(pgStore)
-	if err := d.Connect(ctx, *pgConnect); err != nil {
-		log.Fatalf("d.Connect() = %v", err)
+	s, err := relui.NewServer(ctx, *pgConnect)
+	if err != nil {
+		log.Fatalf("relui.NewServer() = %v", err)
 	}
-	defer d.Close()
-	s := &server{
-		store: d,
-	}
-	http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
-	http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
-	http.Handle("/", fileServerHandler(static, http.HandlerFunc(s.homeHandler)))
 	port := os.Getenv("PORT")
 	if port == "" {
 		port = "8080"
 	}
 	log.Printf("Listening on :" + port)
-	log.Fatal(http.ListenAndServe(":"+port, http.DefaultServeMux))
+	log.Fatal(s.Serve(port))
 }
diff --git a/cmd/relui/content.go b/internal/relui/content.go
similarity index 96%
rename from cmd/relui/content.go
rename to internal/relui/content.go
index ce02062..42e4804 100644
--- a/cmd/relui/content.go
+++ b/internal/relui/content.go
@@ -5,7 +5,7 @@
 //go:build go1.16
 // +build go1.16
 
-package main
+package relui
 
 import "embed"
 
diff --git a/cmd/relui/static/styles.css b/internal/relui/static/styles.css
similarity index 100%
rename from cmd/relui/static/styles.css
rename to internal/relui/static/styles.css
diff --git a/cmd/relui/store.go b/internal/relui/store.go
similarity index 93%
rename from cmd/relui/store.go
rename to internal/relui/store.go
index ea200c9..0cfe4f7 100644
--- a/cmd/relui/store.go
+++ b/internal/relui/store.go
@@ -5,7 +5,7 @@
 //go:build go1.16
 // +build go1.16
 
-package main
+package relui
 
 import (
 	"context"
@@ -22,10 +22,10 @@
 type store interface {
 }
 
-var _ store = (*pgStore)(nil)
+var _ store = (*PgStore)(nil)
 
-// pgStore is a store backed by a Postgres database.
-type pgStore struct {
+// PgStore is a store backed by a Postgres database.
+type PgStore struct {
 	db *pgxpool.Pool
 }
 
@@ -35,7 +35,7 @@
 // Any key/value or URI string compatible with libpq is valid. If the
 // database does not exist, one will be created using the credentials
 // provided.
-func (p *pgStore) Connect(ctx context.Context, connString string) error {
+func (p *PgStore) Connect(ctx context.Context, connString string) error {
 	cfg, err := pgx.ParseConfig(connString)
 	if err != nil {
 		return fmt.Errorf("pgx.ParseConfig() = %w", err)
@@ -52,7 +52,7 @@
 }
 
 // Close closes the pgxpool.Pool.
-func (p *pgStore) Close() {
+func (p *PgStore) Close() {
 	p.db.Close()
 }
 
diff --git a/cmd/relui/store_test.go b/internal/relui/store_test.go
similarity index 98%
rename from cmd/relui/store_test.go
rename to internal/relui/store_test.go
index cc80a5c..a26c831 100644
--- a/cmd/relui/store_test.go
+++ b/internal/relui/store_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package relui
 
 import (
 	"context"
diff --git a/cmd/relui/templates/home.html b/internal/relui/templates/home.html
similarity index 100%
rename from cmd/relui/templates/home.html
rename to internal/relui/templates/home.html
diff --git a/cmd/relui/templates/layout.html b/internal/relui/templates/layout.html
similarity index 100%
rename from cmd/relui/templates/layout.html
rename to internal/relui/templates/layout.html
diff --git a/cmd/relui/templates/new_workflow.html b/internal/relui/templates/new_workflow.html
similarity index 100%
rename from cmd/relui/templates/new_workflow.html
rename to internal/relui/templates/new_workflow.html
diff --git a/cmd/relui/testing/test.css b/internal/relui/testing/test.css
similarity index 100%
rename from cmd/relui/testing/test.css
rename to internal/relui/testing/test.css
diff --git a/cmd/relui/web.go b/internal/relui/web.go
similarity index 68%
rename from cmd/relui/web.go
rename to internal/relui/web.go
index 034b629..e327f9e 100644
--- a/cmd/relui/web.go
+++ b/internal/relui/web.go
@@ -5,10 +5,11 @@
 //go:build go1.16
 // +build go1.16
 
-package main
+package relui
 
 import (
 	"bytes"
+	"context"
 	"html/template"
 	"io"
 	"io/fs"
@@ -42,18 +43,40 @@
 	newWorkflowTmpl = template.Must(template.Must(layoutTmpl.Clone()).ParseFS(templates, "templates/new_workflow.html"))
 )
 
-// server implements the http handlers for relui.
-type server struct {
+// Server implements the http handlers for relui.
+type Server struct {
 	// store is for persisting application state.
 	store store
 }
 
+// NewServer initializes a server with a database connection specified
+// by pgConn. Any libpq compatible connection strings or URIs are
+// supported.
+func NewServer(ctx context.Context, pgConn string) (*Server, error) {
+	d := new(PgStore)
+	if err := d.Connect(ctx, pgConn); err != nil {
+		return nil, err
+	}
+	defer d.Close()
+	s := &Server{
+		store: d,
+	}
+	http.Handle("/workflows/create", http.HandlerFunc(s.createWorkflowHandler))
+	http.Handle("/workflows/new", http.HandlerFunc(s.newWorkflowHandler))
+	http.Handle("/", fileServerHandler(static, http.HandlerFunc(s.homeHandler)))
+	return s, nil
+}
+
+func (s *Server) Serve(port string) error {
+	return http.ListenAndServe(":"+port, http.DefaultServeMux)
+}
+
 type homeResponse struct {
 	Workflows []interface{}
 }
 
 // homeHandler renders the homepage.
-func (s *server) homeHandler(w http.ResponseWriter, _ *http.Request) {
+func (s *Server) homeHandler(w http.ResponseWriter, _ *http.Request) {
 	out := bytes.Buffer{}
 	if err := homeTmpl.Execute(&out, homeResponse{}); err != nil {
 		log.Printf("homeHandler: %v", err)
@@ -64,7 +87,7 @@
 }
 
 // newWorkflowHandler presents a form for creating a new workflow.
-func (s *server) newWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
+func (s *Server) newWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
 	out := bytes.Buffer{}
 	if err := newWorkflowTmpl.Execute(&out, nil); err != nil {
 		log.Printf("newWorkflowHandler: %v", err)
@@ -75,6 +98,6 @@
 }
 
 // createWorkflowHandler persists a new workflow in the datastore.
-func (s *server) createWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
+func (s *Server) createWorkflowHandler(w http.ResponseWriter, _ *http.Request) {
 	http.Error(w, "Unable to create workflow: no workflows configured", http.StatusInternalServerError)
 }
diff --git a/cmd/relui/web_test.go b/internal/relui/web_test.go
similarity index 97%
rename from cmd/relui/web_test.go
rename to internal/relui/web_test.go
index 4b4f2bf..bfed150 100644
--- a/cmd/relui/web_test.go
+++ b/internal/relui/web_test.go
@@ -5,7 +5,7 @@
 //go:build go1.16
 // +build go1.16
 
-package main
+package relui
 
 import (
 	"embed"
@@ -89,7 +89,7 @@
 	req := httptest.NewRequest(http.MethodGet, "/", nil)
 	w := httptest.NewRecorder()
 
-	s := &server{}
+	s := &Server{}
 	s.homeHandler(w, req)
 	resp := w.Result()
 
@@ -102,7 +102,7 @@
 	req := httptest.NewRequest(http.MethodGet, "/workflows/new", nil)
 	w := httptest.NewRecorder()
 
-	s := &server{}
+	s := &Server{}
 	s.newWorkflowHandler(w, req)
 	resp := w.Result()