From b056e997a7c67ce5db47d25566035ffe8074b071 Mon Sep 17 00:00:00 2001
From: Garet Halliday <me@garet.holiday>
Date: Thu, 28 Sep 2023 18:27:00 -0500
Subject: [PATCH] matchers to json

---
 lib/gat/app.go                   |   6 +-
 lib/gat/gatcaddyfile/README.md   |  13 +---
 lib/gat/gatcaddyfile/gattype.go  |  22 +++++-
 lib/gat/gatcaddyfile/matchers.go | 130 +++++++++++++++++++++++++++++++
 lib/gat/route.go                 |   4 +-
 lib/gat/server.go                |   7 +-
 6 files changed, 162 insertions(+), 20 deletions(-)
 create mode 100644 lib/gat/gatcaddyfile/matchers.go

diff --git a/lib/gat/app.go b/lib/gat/app.go
index 17867557..f0793687 100644
--- a/lib/gat/app.go
+++ b/lib/gat/app.go
@@ -18,9 +18,9 @@ import (
 )
 
 type Config struct {
-	StatLogPeriod dur.Duration     `json:"stat_log_period"`
-	Listen        []ListenerConfig `json:"listen"`
-	Servers       []ServerConfig   `json:"servers"`
+	StatLogPeriod dur.Duration     `json:"stat_log_period,omitempty"`
+	Listen        []ListenerConfig `json:"listen,omitempty"`
+	Servers       []ServerConfig   `json:"servers,omitempty"`
 }
 
 func init() {
diff --git a/lib/gat/gatcaddyfile/README.md b/lib/gat/gatcaddyfile/README.md
index dac46a9f..4a2141f8 100644
--- a/lib/gat/gatcaddyfile/README.md
+++ b/lib/gat/gatcaddyfile/README.md
@@ -3,12 +3,7 @@ Server blocks will be matched in order by their keys. Different SSL configuratio
 (due to technical limitations).
 
 ## Directives
-| Directive        | Description                                                              |
-|------------------|--------------------------------------------------------------------------|
-| ssl              | ssl configuration for this server                                        |
-| allow_parameters | set which initial parameters are allowed                                 |
-| user             | rewrite username                                                         |
-| password         | use global password instead of password provided by pool                 |
-| database         | rewrite database                                                         |
-| parameters       | rewrite parameters                                                       |
-| {provider}       | a pool provider. if pool is not found, the next provider will be checked |
+| Directive | Description                       |
+|-----------|-----------------------------------|
+| ssl       | ssl configuration for this server |
+| {handler} | each handler will be run in order |
diff --git a/lib/gat/gatcaddyfile/gattype.go b/lib/gat/gatcaddyfile/gattype.go
index 38eea92f..3b96dc93 100644
--- a/lib/gat/gatcaddyfile/gattype.go
+++ b/lib/gat/gatcaddyfile/gattype.go
@@ -1,11 +1,11 @@
 package gatcaddyfile
 
 import (
-	"log"
-
 	"github.com/caddyserver/caddy/v2"
 	"github.com/caddyserver/caddy/v2/caddyconfig"
 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
+
+	"gfx.cafe/gfx/pggat/lib/gat"
 )
 
 func init() {
@@ -15,9 +15,23 @@ func init() {
 type ServerType struct{}
 
 func (ServerType) Setup(blocks []caddyfile.ServerBlock, m map[string]any) (*caddy.Config, []caddyconfig.Warning, error) {
+	var config caddy.Config
+	var warnings []caddyconfig.Warning
+
+	var app gat.App
+
 	for _, block := range blocks {
-		log.Printf("%#v", block)
+		var server gat.ServerConfig
+
+		server.Match = MatcherFromConnectionStrings(block.Keys, &warnings)
+
+		app.Servers = append(app.Servers, server)
+	}
+
+	if config.AppsRaw == nil {
+		config.AppsRaw = make(caddy.ModuleMap)
 	}
+	config.AppsRaw[string(app.CaddyModule().ID)] = caddyconfig.JSON(app, &warnings)
 
-	return nil, nil, nil
+	return &config, warnings, nil
 }
diff --git a/lib/gat/gatcaddyfile/matchers.go b/lib/gat/gatcaddyfile/matchers.go
new file mode 100644
index 00000000..a85e141e
--- /dev/null
+++ b/lib/gat/gatcaddyfile/matchers.go
@@ -0,0 +1,130 @@
+package gatcaddyfile
+
+import (
+	"encoding/json"
+	"strings"
+
+	"github.com/caddyserver/caddy/v2/caddyconfig"
+
+	"gfx.cafe/gfx/pggat/lib/gat/matchers"
+)
+
+func MatcherFromConnectionStrings(strs []string, warnings *[]caddyconfig.Warning) json.RawMessage {
+	var or matchers.Or
+
+	for _, str := range strs {
+		val := MatcherFromConnectionString(str, warnings)
+		if val != nil {
+			or.Or = append(or.Or, val)
+		}
+	}
+
+	if len(or.Or) == 0 {
+		return nil
+	}
+	if len(or.Or) == 1 {
+		return or.Or[0]
+	}
+
+	return caddyconfig.JSONModuleObject(
+		or,
+		"matcher",
+		"and",
+		warnings,
+	)
+}
+
+// MatcherFromConnectionString converts from the postgres connection string format to a bunch of matchers.
+// Example: postgres://user@address:port/database?parameter_key=parameter_value
+func MatcherFromConnectionString(str string, warnings *[]caddyconfig.Warning) json.RawMessage {
+	// strip optional postgres://
+	str = strings.TrimPrefix(str, "postgres://")
+
+	if str == "" {
+		return nil
+	}
+
+	var and matchers.And
+
+	var parametersString string
+	str, parametersString, _ = strings.Cut(str, "?")
+
+	if parametersString != "" {
+		var parameters matchers.StartupParameters
+		parameters.Parameters = make(map[string]string)
+		kvs := strings.Split(parametersString, "&")
+		for _, kv := range kvs {
+			k, v, _ := strings.Cut(kv, "=")
+			parameters.Parameters[k] = v
+		}
+		and.And = append(
+			and.And,
+			caddyconfig.JSONModuleObject(
+				parameters,
+				"matcher",
+				"startup_parameters",
+				warnings,
+			),
+		)
+	}
+
+	var database matchers.Database
+	str, database.Database, _ = strings.Cut(str, "/")
+	if database.Database != "" {
+		and.And = append(
+			and.And,
+			caddyconfig.JSONModuleObject(
+				database,
+				"matcher",
+				"database",
+				warnings,
+			),
+		)
+	}
+
+	var address = matchers.LocalAddress{
+		Network: "tcp",
+	}
+	var user matchers.User
+	var ok bool
+	user.User, address.Address, ok = strings.Cut(str, "@")
+	if !ok {
+		address.Address = user.User
+		user.User = ""
+	} else if user.User != "" {
+		and.And = append(
+			and.And,
+			caddyconfig.JSONModuleObject(
+				user,
+				"matcher",
+				"user",
+				warnings,
+			),
+		)
+	}
+	if address.Address != "" {
+		and.And = append(
+			and.And,
+			caddyconfig.JSONModuleObject(
+				address,
+				"matcher",
+				"local_address",
+				warnings,
+			),
+		)
+	}
+
+	if len(and.And) == 0 {
+		return nil
+	}
+	if len(and.And) == 1 {
+		return and.And[0]
+	}
+
+	return caddyconfig.JSONModuleObject(
+		and,
+		"matcher",
+		"and",
+		warnings,
+	)
+}
diff --git a/lib/gat/route.go b/lib/gat/route.go
index 9a3a9db9..dc8e97fb 100644
--- a/lib/gat/route.go
+++ b/lib/gat/route.go
@@ -8,8 +8,8 @@ import (
 )
 
 type RouteConfig struct {
-	Match  json.RawMessage `json:"match" caddy:"namespace=pggat.matchers inline_key=matcher"`
-	Handle json.RawMessage `json:"handle" caddy:"namespace=pggat.handlers inline_key=handler"`
+	Match  json.RawMessage `json:"match,omitempty" caddy:"namespace=pggat.matchers inline_key=matcher"`
+	Handle json.RawMessage `json:"handle,omitempty" caddy:"namespace=pggat.handlers inline_key=handler"`
 }
 
 type Route struct {
diff --git a/lib/gat/server.go b/lib/gat/server.go
index 0cdc7bf8..0bcdda4e 100644
--- a/lib/gat/server.go
+++ b/lib/gat/server.go
@@ -16,8 +16,8 @@ import (
 )
 
 type ServerConfig struct {
-	Match  json.RawMessage `json:"match" caddy:"namespace=pggat.matchers inline_key=matcher"`
-	Routes []RouteConfig   `json:"routes"`
+	Match  json.RawMessage `json:"match,omitempty" caddy:"namespace=pggat.matchers inline_key=matcher"`
+	Routes []RouteConfig   `json:"routes,omitempty"`
 }
 
 type Server struct {
@@ -80,6 +80,9 @@ func (T *Server) Serve(conn *fed.Conn) {
 			continue
 		}
 
+		if route.handle == nil {
+			continue
+		}
 		err := route.handle.Handle(conn)
 		if err != nil {
 			if errors.Is(err, io.EOF) {
-- 
GitLab