diff --git a/.amock.json b/.amock.json
new file mode 100644
index 0000000..6f228eb
--- /dev/null
+++ b/.amock.json
@@ -0,0 +1,7 @@
+{
+ "host": "localhost",
+ "port": 8080,
+ "entities": [
+ "user.json"
+ ]
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8bcfa99
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+### Go template
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+vendor/
+pkg/
+
+# Go workspace file
+go.work
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..a9d7db9
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# GitHub Copilot persisted chat sessions
+/copilot/chatSessions
diff --git a/.idea/amock.iml b/.idea/amock.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/.idea/amock.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/developer-tools.xml b/.idea/developer-tools.xml
new file mode 100644
index 0000000..69a9b9c
--- /dev/null
+++ b/.idea/developer-tools.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/golinter.xml b/.idea/golinter.xml
new file mode 100644
index 0000000..b7207b2
--- /dev/null
+++ b/.idea/golinter.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml
new file mode 100644
index 0000000..646c3a2
--- /dev/null
+++ b/.idea/jsonSchemas.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c96f213
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/amock b/amock
new file mode 100755
index 0000000..6ad82b8
Binary files /dev/null and b/amock differ
diff --git a/amock.db b/amock.db
new file mode 100644
index 0000000..0b745ff
Binary files /dev/null and b/amock.db differ
diff --git a/generator.go b/generator.go
new file mode 100644
index 0000000..6d4481a
--- /dev/null
+++ b/generator.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+
+ "github.com/timshannon/bolthold"
+)
+
+func GenerateEntity(entity EntityJSON, table *Table) (Entity, *Table) {
+ fields := make(Entity, len(entity))
+
+ for key, value := range entity {
+ fields[key], table = GenerateField(value, table)
+ }
+
+ return fields, table
+}
+
+func HydrateDatabase(db Database) Database {
+ var entityJSON EntityJSON
+
+ store, err := bolthold.Open("amock.db", 0666, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for key, table := range db.Tables {
+ var updated Table
+ updated, store = CreateTable(&table, entityJSON, store)
+ db.Tables[key] = updated
+ }
+
+ return db
+}
+
+func CreateTable(table *Table, entityJSON EntityJSON, store *bolthold.Store) (Table, *bolthold.Store) {
+ raw, err := os.ReadFile(table.Definition)
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = json.Unmarshal(raw, &entityJSON)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ entities := make([]Entity, config.InitCount)
+
+ for i := 0; i < config.InitCount; i++ {
+ entities[i], table = GenerateEntity(entityJSON, table)
+ }
+
+ b, _ := json.Marshal(entities)
+
+ filename := table.Name + ".amock.json"
+
+ _ = os.WriteFile(filename, b, os.ModePerm)
+
+ table.File = filename
+
+ return *table, store
+}
diff --git a/generator/date.go b/generator/date.go
new file mode 100644
index 0000000..1f63ab0
--- /dev/null
+++ b/generator/date.go
@@ -0,0 +1,124 @@
+package generator
+
+import (
+ "strings"
+ "time"
+
+ "github.com/brianvoe/gofakeit/v7"
+)
+
+type DateGenerator struct {
+ Generator
+}
+
+func (g DateGenerator) Date(args ...string) any {
+ if len(args) > 0 {
+ format := args[0]
+ switch format {
+ case "ANSIC":
+ return gofakeit.Date().Format(time.ANSIC)
+ case "UnixDate":
+ return gofakeit.Date().Format(time.UnixDate)
+ case "RubyDate":
+ return gofakeit.Date().Format(time.RubyDate)
+ case "RFC822":
+ return gofakeit.Date().Format(time.RFC822)
+ case "RFC822Z":
+ return gofakeit.Date().Format(time.RFC822Z)
+ case "RFC850":
+ return gofakeit.Date().Format(time.RFC850)
+ case "RFC1123":
+ return gofakeit.Date().Format(time.RFC1123)
+ case "RFC1123Z":
+ return gofakeit.Date().Format(time.RFC1123Z)
+ case "RFC3339":
+ return gofakeit.Date().Format(time.RFC3339)
+ case "RFC3339Nano":
+ return gofakeit.Date().Format(time.RFC3339Nano)
+ default:
+ return gofakeit.Date().Format(formatToGoFormat(format))
+ }
+ }
+
+ return gofakeit.Date()
+}
+
+func (g DateGenerator) Timestamp() int64 {
+ return gofakeit.DateRange(time.Unix(0, 0), gofakeit.FutureDate()).Unix()
+}
+
+func (g DateGenerator) Day() int {
+ return gofakeit.Day()
+}
+
+func (g DateGenerator) Month(args ...string) any {
+ if len(args) > 0 {
+ if args[0] == "string" {
+ return gofakeit.MonthString()
+ }
+ }
+
+ return gofakeit.Month()
+}
+
+func (g DateGenerator) Year() int {
+ return gofakeit.Year()
+}
+
+func (g DateGenerator) WeekDay() string {
+ return gofakeit.WeekDay()
+}
+
+func (g DateGenerator) Future() time.Time {
+ return gofakeit.FutureDate()
+}
+
+func (g DateGenerator) Past() time.Time {
+ return gofakeit.PastDate()
+}
+
+func formatToGoFormat(format string) string {
+ format = strings.Replace(format, "ddd", "_2", -1)
+ format = strings.Replace(format, "dd", "02", -1)
+ format = strings.Replace(format, "d", "2", -1)
+
+ format = strings.Replace(format, "HH", "15", -1)
+
+ format = strings.Replace(format, "hh", "03", -1)
+ format = strings.Replace(format, "h", "3", -1)
+
+ format = strings.Replace(format, "mm", "04", -1)
+ format = strings.Replace(format, "m", "4", -1)
+
+ format = strings.Replace(format, "ss", "05", -1)
+ format = strings.Replace(format, "s", "5", -1)
+
+ format = strings.Replace(format, "yyyy", "2006", -1)
+ format = strings.Replace(format, "yy", "06", -1)
+ format = strings.Replace(format, "y", "06", -1)
+
+ format = strings.Replace(format, "SSS", "000", -1)
+
+ format = strings.Replace(format, "a", "pm", -1)
+ format = strings.Replace(format, "aa", "PM", -1)
+
+ format = strings.Replace(format, "MMMM", "January", -1)
+ format = strings.Replace(format, "MMM", "Jan", -1)
+ format = strings.Replace(format, "MM", "01", -1)
+ format = strings.Replace(format, "M", "1", -1)
+
+ format = strings.Replace(format, "ZZ", "-0700", -1)
+
+ if !strings.Contains(format, "Z07") {
+ format = strings.Replace(format, "Z", "-07", -1)
+ }
+
+ format = strings.Replace(format, "zz:zz", "Z07:00", -1)
+ format = strings.Replace(format, "zzzz", "Z0700", -1)
+ format = strings.Replace(format, "z", "MST", -1)
+
+ format = strings.Replace(format, "EEEE", "Monday", -1)
+ format = strings.Replace(format, "E", "Mon", -1)
+
+ return format
+}
diff --git a/generator/enum.go b/generator/enum.go
new file mode 100644
index 0000000..d4a5feb
--- /dev/null
+++ b/generator/enum.go
@@ -0,0 +1,11 @@
+package generator
+
+import "github.com/brianvoe/gofakeit/v7"
+
+type EnumGenerator struct {
+ Generator
+}
+
+func (g EnumGenerator) Enum(args ...string) string {
+ return gofakeit.RandomString(args)
+}
diff --git a/generator/generator.go b/generator/generator.go
new file mode 100644
index 0000000..915f89e
--- /dev/null
+++ b/generator/generator.go
@@ -0,0 +1,13 @@
+package generator
+
+import "github.com/brianvoe/gofakeit/v7"
+
+type Generator struct{}
+
+type BoolGenerator struct {
+ Generator
+}
+
+func (g BoolGenerator) Bool() bool {
+ return gofakeit.Bool()
+}
diff --git a/generator/id.go b/generator/id.go
new file mode 100644
index 0000000..6efb9eb
--- /dev/null
+++ b/generator/id.go
@@ -0,0 +1,25 @@
+package generator
+
+import (
+ "strconv"
+
+ "github.com/brianvoe/gofakeit/v7"
+)
+
+type IDGenerator struct {
+ Generator
+}
+
+func (g IDGenerator) UUID() string {
+ return gofakeit.UUID()
+}
+
+func (g IDGenerator) Sequence(args ...string) uint {
+ if len(args) > 0 {
+ id, _ := strconv.Atoi(args[0])
+
+ return uint(id)
+ }
+
+ return 1
+}
diff --git a/generator/number.go b/generator/number.go
new file mode 100644
index 0000000..65b2191
--- /dev/null
+++ b/generator/number.go
@@ -0,0 +1,82 @@
+package generator
+
+import (
+ "math"
+ "strconv"
+
+ "github.com/brianvoe/gofakeit/v7"
+)
+
+type NumberGenerator struct {
+ Generator
+}
+
+func randRangeInt(min int, max int) int {
+ return gofakeit.IntRange(min, max)
+}
+func randRangeFloat(min float64, max float64) float64 {
+ return gofakeit.Float64Range(min, max)
+}
+
+func (g NumberGenerator) Number() int {
+ return gofakeit.Int()
+}
+
+func (g NumberGenerator) Float() float64 {
+ return gofakeit.Float64()
+}
+
+func (g NumberGenerator) FloatRange(min string, max string) float64 {
+ if min == "x" && max == "x" {
+ return g.Float()
+ }
+
+ if min == "x" {
+ fMax, _ := strconv.ParseFloat(max, 64)
+ return randRangeFloat(0, fMax)
+ } else if max == "x" {
+ fMin, _ := strconv.ParseFloat(min, 64)
+ return randRangeFloat(fMin, math.MaxInt)
+ } else {
+ fMin, _ := strconv.ParseFloat(min, 64)
+ fMax, _ := strconv.ParseFloat(max, 64)
+ return randRangeFloat(fMin, fMax)
+ }
+}
+
+func (g NumberGenerator) Decimal(p string, min string, max string) string {
+ prec, _ := strconv.Atoi(p)
+ if (min == "" && max == "") || (min == "x" && max == "x") {
+ return strconv.FormatFloat(g.Float(), 'f', prec, 32)
+ }
+
+ if min == "x" {
+ fMax, _ := strconv.ParseFloat(max, 32)
+ return strconv.FormatFloat(g.FloatRange("x", strconv.FormatFloat(fMax, 'f', -1, 32)), 'f', prec, 32)
+ } else if max == "x" {
+ fMin, _ := strconv.ParseFloat(min, 32)
+ return strconv.FormatFloat(g.FloatRange(strconv.FormatFloat(fMin, 'f', -1, 32), "x"), 'f', prec, 32)
+ } else {
+ fMin, _ := strconv.ParseFloat(min, 32)
+ fMax, _ := strconv.ParseFloat(max, 32)
+ return strconv.FormatFloat(g.FloatRange(strconv.FormatFloat(fMin, 'f', -1, 32), strconv.FormatFloat(fMax, 'f', -1, 32)), 'f', prec, 32)
+ }
+}
+
+func (g NumberGenerator) Int(min string, max string) int {
+ if min == "x" && max == "x" {
+ return g.Number()
+ }
+
+ if min == "x" {
+ iMax, _ := strconv.Atoi(max)
+ return randRangeInt(0, iMax)
+ } else if max == "x" {
+ iMin, _ := strconv.Atoi(min)
+ return randRangeInt(iMin, math.MaxInt)
+ } else {
+ iMin, _ := strconv.Atoi(min)
+ iMax, _ := strconv.Atoi(max)
+ return randRangeInt(iMin, iMax)
+ }
+}
diff --git a/generator/string.go b/generator/string.go
new file mode 100644
index 0000000..239f35e
--- /dev/null
+++ b/generator/string.go
@@ -0,0 +1,143 @@
+package generator
+
+import (
+ "fmt"
+ "strconv"
+
+ "github.com/brianvoe/gofakeit/v7"
+)
+
+type StringGenerator struct {
+ Generator
+}
+
+func (g StringGenerator) String() string {
+ return gofakeit.Regex("[A-z\\-_+?&*$@/!=#]{3,16}")
+}
+
+func (g StringGenerator) FullName() string {
+ return gofakeit.Name()
+}
+
+func (g StringGenerator) FirstName() string {
+ return gofakeit.FirstName()
+}
+
+func (g StringGenerator) LastName() string {
+ return gofakeit.LastName()
+}
+
+func (g StringGenerator) Email() string {
+ return gofakeit.Email()
+}
+
+func (g StringGenerator) Url() string {
+ return gofakeit.URL()
+}
+
+func (g StringGenerator) Ip() string {
+ return gofakeit.IPv4Address()
+}
+
+func (g StringGenerator) Ipv6() string {
+ return gofakeit.IPv6Address()
+}
+
+func (g StringGenerator) Username() string {
+ return gofakeit.Username()
+}
+
+func (g StringGenerator) Password() string {
+ return gofakeit.Password(true, true, true, true, false, 16)
+}
+
+func (g StringGenerator) Phone() string {
+ return gofakeit.Phone()
+}
+
+func (g StringGenerator) Zip() string {
+ return gofakeit.Zip()
+}
+
+func (g StringGenerator) Country(args ...string) string {
+ if len(args) > 0 {
+ if args[0] == "short" {
+ return gofakeit.CountryAbr()
+ }
+ }
+
+ return gofakeit.Country()
+}
+
+func (g StringGenerator) City() string {
+ return gofakeit.City()
+}
+
+func (g StringGenerator) Street() string {
+ return gofakeit.Street()
+}
+
+func (g StringGenerator) StreetName() string {
+ return gofakeit.StreetName()
+}
+
+func (g StringGenerator) State(args ...string) string {
+ if len(args) > 0 {
+ if args[0] == "short" {
+ return gofakeit.StateAbr()
+ }
+ }
+
+ return gofakeit.State()
+}
+
+func (g StringGenerator) Company() string {
+ return gofakeit.Company()
+}
+
+func (g StringGenerator) Bitcoin() string {
+ return gofakeit.BitcoinAddress()
+}
+
+func (g StringGenerator) Color(args ...string) string {
+ if len(args) > 0 {
+ if args[0] == "hex" {
+ return gofakeit.HexColor()
+ }
+
+ if args[0] == "safe" {
+ return gofakeit.SafeColor()
+ }
+
+ if args[0] == "rgb" {
+ rgb := gofakeit.RGBColor()
+ return fmt.Sprintf("rgb(%d, %d, %d)", rgb[0], rgb[1], rgb[2])
+ }
+ }
+
+ return gofakeit.Color()
+}
+
+func (g StringGenerator) Word() string {
+ return gofakeit.Word()
+}
+
+func (g StringGenerator) Sentence(args ...string) string {
+ if len(args) > 0 {
+ n, _ := strconv.Atoi(args[0])
+
+ return gofakeit.Sentence(n)
+ }
+
+ return gofakeit.Sentence(3)
+}
+
+func (g StringGenerator) Paragraph(args ...string) string {
+ if len(args) <= 0 {
+ return gofakeit.Paragraph(3, 4, 12, "\n\n")
+ }
+
+ n, _ := strconv.Atoi(args[0])
+
+ return gofakeit.Paragraph(n, 4, 12, "\n\n")
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..1569d35
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,18 @@
+module amock
+
+go 1.22
+
+require (
+ github.com/BurntSushi/toml v1.3.2 // indirect
+ github.com/brianvoe/gofakeit/v7 v7.0.2 // indirect
+ github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect
+ github.com/joho/godotenv v1.5.1 // indirect
+ github.com/julienschmidt/httprouter v1.3.0 // indirect
+ github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b // indirect
+ github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 // indirect
+ go.etcd.io/bbolt v1.3.8 // indirect
+ golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
+ golang.org/x/sys v0.15.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+ olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..30201c0
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,40 @@
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/brianvoe/gofakeit/v7 v7.0.2 h1:jzYT7Ge3RDHw7J1CM1kwu0OQywV9vbf2qSGxBS72TCY=
+github.com/brianvoe/gofakeit/v7 v7.0.2/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
+github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4=
+github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b h1:9L56kn3D7E9jd2R9U9p7tfzaBaLTVPt4HgnrP+g2VGk=
+github.com/oriser/regroup v0.0.0-20230527212431-1b00c9bdbc5b/go.mod h1:6eb1+OYHjOvThrtgEVue70NTfmzkalZgohRtndAUUbI=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629 h1:6JyscwjLxdI0S7GTDtcQXpxjsldBYwNXi4jfSpZEMzE=
+github.com/timshannon/bolthold v0.0.0-20231129192944-dca5178aa629/go.mod h1:PCFYfAEfKT+Nd6zWvUpsXduMR1bXFLf0uGSlEF05MCI=
+go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
+go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
+go.etcd.io/gofail v0.1.0/go.mod h1:VZBCXYGZhHAinaBiiqYvuDynvahNsAyLFwB3kEHKz1M=
+golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
+golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ=
+olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..71d4979
--- /dev/null
+++ b/main.go
@@ -0,0 +1,146 @@
+package main
+
+import (
+ "errors"
+ "log"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/ilyakaznacheev/cleanenv"
+)
+
+var ConfigPaths = []string{
+ ".amock.json",
+ ".amockrc",
+ ".amock.json.json",
+ ".amock.json.yml",
+ ".amock.json.yaml",
+ ".amock.json.toml",
+ "amock.config",
+ "amock.json",
+ "amock.yml",
+ "amock.yaml",
+ "amock.toml",
+}
+
+type Database struct {
+ Tables map[string]Table
+}
+
+type Table struct {
+ Name string
+ File string
+ Definition string
+ LastAutoID uint
+}
+
+type Config struct {
+ Host string `yaml:"host" env:"HOST" env-default:"localhost"`
+ Port int `yaml:"port" env:"PORT" env-default:"8080"`
+ Dir string `yaml:"dir" env:"DIR"`
+ Entities []string `yaml:"entities" env:"ENTITIES"`
+ InitCount int `yaml:"init_count" env:"INIT_COUNT" env-default:"20"`
+}
+
+type Entity map[string]interface{}
+
+type EntityJSON map[string]string
+
+// func PostRequest(url string, data Entity) {
+// store, _ := bh.Open("db", os.ModePerm, nil)
+//
+// defer func(store *bh.Store) {
+// err := store.Close()
+// if err != nil {
+// log.Fatal(err)
+// }
+// }(store)
+//
+// _ = store.Insert(data["id"], data["value"])
+// }
+//
+// func GetRequest(url string) Entity {
+//
+// }
+//
+// func PostHandler(w http.ResponseWriter, r *http.Request) {
+//
+// _, _ = fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
+// }
+
+// var api EntityJSON
+
+var config *Config
+
+var db Database
+
+func Init() {
+ config, _ = ParseConfigFiles(ConfigPaths...)
+
+ if config == nil {
+ log.Fatal("No configuration file found")
+ }
+
+ db.Tables = make(map[string]Table)
+
+ if config.Dir != "" {
+ dir, err := os.ReadDir(config.Dir)
+
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, entry := range dir {
+ filename := entry.Name()
+
+ if path.Ext(filename) == ".json" {
+ name := strings.ToLower(filename[:len(filename)-5])
+ db.Tables[name] = Table{
+ Name: name,
+ Definition: path.Join(config.Dir, filename),
+ File: filename,
+ LastAutoID: 1,
+ }
+ }
+ }
+ }
+
+ if len(config.Entities) > 0 {
+ for _, entity := range config.Entities {
+ if path.Ext(entity) == ".json" {
+ name := entity[:len(entity)-5]
+ db.Tables[name] = Table{
+ Name: name,
+ Definition: entity,
+ File: path.Base(entity),
+ LastAutoID: 1,
+ }
+ }
+ }
+ }
+
+ db = HydrateDatabase(db)
+}
+
+func main() {
+ Init()
+}
+
+func ParseConfigFiles(files ...string) (*Config, error) {
+ var cfg Config
+
+ for i := 0; i < len(files); i++ {
+ if _, err := os.Stat(files[i]); errors.Is(err, os.ErrNotExist) {
+ continue
+ }
+
+ err := cleanenv.ReadConfig(files[i], &cfg)
+ if err != nil {
+ log.Printf("Error reading configuration from file:%v", files[i])
+ return nil, err
+ }
+ }
+
+ return &cfg, nil
+}
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..6a9cf08
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,146 @@
+package main
+
+import (
+ "reflect"
+ "strconv"
+ "strings"
+
+ "amock/generator"
+ "github.com/oriser/regroup"
+)
+
+var FieldPattern = regroup.MustCompile(`(?P[a-z]+)(?P\.[a-z]+)?(?P:.*)?`)
+var NumberRangePattern = regroup.MustCompile(`(?P(-?[0-9]+(\.[0-9]+)?)|x)?-(?P(-?[0-9]+(\.[0-9]+)?)|x)?`)
+
+type Field struct {
+ Type string `regroup:"type"`
+ Subtype string `regroup:"subtype"`
+ Params string `regroup:"params"`
+}
+
+func GenerateField(field string, table *Table) (any, *Table) {
+ f := &Field{}
+
+ err := FieldPattern.MatchToTarget(field, f)
+
+ if err != nil {
+ panic(err)
+ }
+
+ var subtype, paramStr string
+ var params []string
+ var gen any
+
+ t := f.Type
+ if len(f.Subtype) > 1 {
+ subtype = strings.TrimLeft(f.Subtype, ".")
+ }
+
+ gen = GetGenerator(t, subtype)
+
+ if len(f.Params) > 1 {
+ paramStr = strings.TrimLeft(f.Params, ":")
+
+ if f.Type == "number" {
+ params = strings.Split(paramStr, ",")
+ for i, p := range params {
+ if strings.Contains(p, "-") {
+ groups, _ := NumberRangePattern.Groups(p)
+ if groups["min"] == "" && groups["max"] == "" {
+ params[i] = "x-x"
+ } else {
+ params[i] = groups["min"]
+ params = append(params, groups["max"])
+ }
+ }
+ }
+ } else {
+ params = strings.Split(paramStr, ",")
+ }
+ } else if f.Type == "id" && subtype != "uuid" {
+ params = []string{strconv.Itoa(int(table.LastAutoID))}
+ table.LastAutoID = table.LastAutoID + 1
+ }
+
+ if len(params) > 0 {
+ params2 := make([]reflect.Value, len(params))
+ for i, p := range params {
+ params2[i] = reflect.ValueOf(p)
+ }
+ return reflect.ValueOf(gen).Call(params2)[0].Interface(), table
+ }
+
+ return reflect.ValueOf(gen).Call([]reflect.Value{})[0].Interface(), table
+}
+
+type GeneratorFunc any
+
+type GeneratorMap map[string]GeneratorFunc
+
+var Generators = map[string]GeneratorMap{
+ "string": {
+ "root": generator.StringGenerator{}.String,
+ "name": generator.StringGenerator{}.FullName,
+ "firstname": generator.StringGenerator{}.FirstName,
+ "lastname": generator.StringGenerator{}.LastName,
+ "email": generator.StringGenerator{}.Email,
+ "url": generator.StringGenerator{}.Url,
+ "ip": generator.StringGenerator{}.Ip,
+ "ipv6": generator.StringGenerator{}.Ipv6,
+ "username": generator.StringGenerator{}.Username,
+ "password": generator.StringGenerator{}.Password,
+ "phone": generator.StringGenerator{}.Phone,
+ "zip": generator.StringGenerator{}.Zip,
+ "country": generator.StringGenerator{}.Country,
+ "city": generator.StringGenerator{}.City,
+ "street": generator.StringGenerator{}.Street,
+ "streetName": generator.StringGenerator{}.StreetName,
+ "state": generator.StringGenerator{}.State,
+ "company": generator.StringGenerator{}.Company,
+ "bitcoin": generator.StringGenerator{}.Bitcoin,
+ "color": generator.StringGenerator{}.Color,
+ "word": generator.StringGenerator{}.Word,
+ "sentence": generator.StringGenerator{}.Sentence,
+ "paragraph": generator.StringGenerator{}.Paragraph,
+ },
+ "number": {
+ "root": generator.NumberGenerator{}.Number,
+ "int": generator.NumberGenerator{}.Int,
+ "decimal": generator.NumberGenerator{}.Decimal,
+ "float": generator.NumberGenerator{}.Float,
+ "range": generator.NumberGenerator{}.FloatRange,
+ },
+ "date": {
+ "root": generator.DateGenerator{}.Date,
+ "timestamp": generator.DateGenerator{}.Timestamp,
+ "day": generator.DateGenerator{}.Day,
+ "month": generator.DateGenerator{}.Month,
+ "year": generator.DateGenerator{}.Year,
+ "weekday": generator.DateGenerator{}.WeekDay,
+ "future": generator.DateGenerator{}.Future,
+ "past": generator.DateGenerator{}.Past,
+ },
+ "bool": {
+ "root": generator.BoolGenerator{}.Bool,
+ },
+ "enum": {
+ "root": generator.EnumGenerator{}.Enum,
+ },
+ "id": {
+ "root": generator.IDGenerator{}.Sequence,
+ "sequence": generator.IDGenerator{}.Sequence,
+ "uuid": generator.IDGenerator{}.UUID,
+ },
+}
+
+func GetGenerator(t string, subtype string) any {
+ var gen any
+
+ if subtype == "" {
+ gen = Generators[t]["root"]
+ } else {
+ gen = Generators[t][subtype]
+ }
+
+ return gen
+}
diff --git a/server.go b/server.go
new file mode 100644
index 0000000..69f87ff
--- /dev/null
+++ b/server.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+)
+
+func InitHandlers(config *Config, db Database) {
+ router := httprouter.New()
+
+ for _, table := range db.Tables {
+ router.GET("/"+table.Name, func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
+
+ _, _ = w.Write([]byte("Hello, " + ps.ByName("name") + "!"))
+ })
+ }
+}
diff --git a/user.amock.json b/user.amock.json
new file mode 100755
index 0000000..1aa11c1
--- /dev/null
+++ b/user.amock.json
@@ -0,0 +1,382 @@
+[
+ {
+ "age": 33,
+ "birthday": "1935-07-15",
+ "city": "Scottsdale",
+ "country": "UY",
+ "created_at": 1206091307,
+ "email": "maybellkunde@schiller.name",
+ "id": 1,
+ "is_active": false,
+ "money": "4609.88",
+ "name": "Nedra",
+ "password": "O45IZWZMLS.S5b0f",
+ "role": "user",
+ "street": "25224 New Commonbury",
+ "surname": "Fay",
+ "token": "L[zn\u0026iZ_",
+ "updated_at": 291548669,
+ "username": "Bernhard4193"
+ },
+ {
+ "age": 22,
+ "birthday": "1901-05-22",
+ "city": "Chesapeake",
+ "country": "TH",
+ "created_at": 908138472,
+ "email": "michaelosinski@rodriguez.biz",
+ "id": 2,
+ "is_active": true,
+ "money": "9385.28",
+ "name": "Peter",
+ "password": "02saQY4z\u0026q@.\u0026ZK.",
+ "role": "moderator",
+ "street": "96456 Brookview",
+ "surname": "Dickinson",
+ "token": "srwafNp[",
+ "updated_at": 1232303408,
+ "username": "Langosh6406"
+ },
+ {
+ "age": 40,
+ "birthday": "2011-11-12",
+ "city": "Miami",
+ "country": "GU",
+ "created_at": 1341845598,
+ "email": "violetcremin@hettinger.name",
+ "id": 3,
+ "is_active": true,
+ "money": "4259.82",
+ "name": "Norval",
+ "password": "orLtl$#A2D6Oof08",
+ "role": "moderator",
+ "street": "86636 South Expresswaymouth",
+ "surname": "Waters",
+ "token": "IvN_q",
+ "updated_at": 435270625,
+ "username": "Kertzmann4208"
+ },
+ {
+ "age": 59,
+ "birthday": "1923-08-08",
+ "city": "Scottsdale",
+ "country": "KG",
+ "created_at": 1332018087,
+ "email": "aprilcole@crooks.org",
+ "id": 4,
+ "is_active": false,
+ "money": "9016.02",
+ "name": "Joyce",
+ "password": "!\u0026uOD7HQEoTJJXsD",
+ "role": "admin",
+ "street": "469 Port Parksmouth",
+ "surname": "Wuckert",
+ "token": "vs#VoFd#",
+ "updated_at": 943664159,
+ "username": "Graham5455"
+ },
+ {
+ "age": 32,
+ "birthday": "1908-12-20",
+ "city": "Stockton",
+ "country": "IE",
+ "created_at": 854693590,
+ "email": "jamilcruickshank@hane.biz",
+ "id": 5,
+ "is_active": false,
+ "money": "4235.92",
+ "name": "Isom",
+ "password": "*vU9BEr5P*5uysmp",
+ "role": "moderator",
+ "street": "152 South Forestfurt",
+ "surname": "Bailey",
+ "token": "TEuy-_l",
+ "updated_at": 1192321334,
+ "username": "Spinka3404"
+ },
+ {
+ "age": 50,
+ "birthday": "1908-01-31",
+ "city": "San Francisco",
+ "country": "KI",
+ "created_at": 1550621657,
+ "email": "kristianmurazik@ortiz.io",
+ "id": 6,
+ "is_active": false,
+ "money": "27.76",
+ "name": "Amari",
+ "password": "a?6d9FJsh_z\u00266r*2",
+ "role": "admin",
+ "street": "715 Spurshaven",
+ "surname": "Abernathy",
+ "token": "Ym??d",
+ "updated_at": 1261506506,
+ "username": "Yundt1500"
+ },
+ {
+ "age": 48,
+ "birthday": "1926-11-04",
+ "city": "Long Beach",
+ "country": "TM",
+ "created_at": 1034774350,
+ "email": "salmalarkin@ferry.net",
+ "id": 7,
+ "is_active": true,
+ "money": "9534.51",
+ "name": "Allan",
+ "password": "UWWCE@5GR!R373wl",
+ "role": "admin",
+ "street": "805 Lightstown",
+ "surname": "Schmitt",
+ "token": "mFJ*_O",
+ "updated_at": 1424365721,
+ "username": "Little1607"
+ },
+ {
+ "age": 64,
+ "birthday": "1982-07-10",
+ "city": "Portland",
+ "country": "VI",
+ "created_at": 1466252331,
+ "email": "kianavonrueden@rohan.net",
+ "id": 8,
+ "is_active": false,
+ "money": "7503.30",
+ "name": "Hilda",
+ "password": "8@HBy\u0026R5jsYQO00q",
+ "role": "user",
+ "street": "121 Curveville",
+ "surname": "Bosco",
+ "token": "\u003d\u003dRj+KNYj",
+ "updated_at": 1068951714,
+ "username": "Gibson3260"
+ },
+ {
+ "age": 31,
+ "birthday": "1925-02-13",
+ "city": "Anaheim",
+ "country": "IS",
+ "created_at": 1120258952,
+ "email": "kylerbrakus@hermiston.name",
+ "id": 9,
+ "is_active": true,
+ "money": "928.46",
+ "name": "Mozelle",
+ "password": "8YnI.f_y8z6#I5?$",
+ "role": "admin",
+ "street": "375 East Forgesberg",
+ "surname": "Hackett",
+ "token": "OdMAaM",
+ "updated_at": 1356776590,
+ "username": "Barrows4929"
+ },
+ {
+ "age": 34,
+ "birthday": "1943-01-14",
+ "city": "Denver",
+ "country": "PS",
+ "created_at": 693936427,
+ "email": "madonnakautzer@koelpin.info",
+ "id": 10,
+ "is_active": false,
+ "money": "2981.66",
+ "name": "Sydnee",
+ "password": "uQ_!8-Zqv66M-X?J",
+ "role": "user",
+ "street": "120 New Plainsburgh",
+ "surname": "Mayer",
+ "token": "t@!]RI$s",
+ "updated_at": 880459894,
+ "username": "Pollich1684"
+ },
+ {
+ "age": 42,
+ "birthday": "1918-09-30",
+ "city": "Colorado Springs",
+ "country": "SG",
+ "created_at": 673194548,
+ "email": "jackelinekeeling@kertzmann.name",
+ "id": 11,
+ "is_active": false,
+ "money": "7372.15",
+ "name": "Rhiannon",
+ "password": "KLO3iw!l\u0026X#aCSg9",
+ "role": "moderator",
+ "street": "8188 New Millberg",
+ "surname": "Larson",
+ "token": "ogxSQfzH$",
+ "updated_at": 95891321,
+ "username": "Bosco3097"
+ },
+ {
+ "age": 22,
+ "birthday": "2021-08-05",
+ "city": "Charlotte",
+ "country": "RU",
+ "created_at": 53542959,
+ "email": "zariamayer@willms.biz",
+ "id": 12,
+ "is_active": true,
+ "money": "695.16",
+ "name": "Brian",
+ "password": "*6jKSL-Jb9Xa9D-7",
+ "role": "user",
+ "street": "980 South Summitchester",
+ "surname": "Schultz",
+ "token": "^V-?t",
+ "updated_at": 517839523,
+ "username": "Brekke2461"
+ },
+ {
+ "age": 23,
+ "birthday": "1988-06-25",
+ "city": "El Paso",
+ "country": "AL",
+ "created_at": 1572357702,
+ "email": "dorthyschoen@breitenberg.io",
+ "id": 13,
+ "is_active": false,
+ "money": "8838.26",
+ "name": "Sterling",
+ "password": "UVfCWb4Dv5O6*TAX",
+ "role": "admin",
+ "street": "32224 New Capemouth",
+ "surname": "Ebert",
+ "token": "EgTa",
+ "updated_at": 240610002,
+ "username": "Heathcote7111"
+ },
+ {
+ "age": 42,
+ "birthday": "1908-04-29",
+ "city": "Austin",
+ "country": "SA",
+ "created_at": 1037206047,
+ "email": "montanaharber@crist.io",
+ "id": 14,
+ "is_active": false,
+ "money": "4479.90",
+ "name": "Amparo",
+ "password": "?02v48*_W?2CNz7M",
+ "role": "admin",
+ "street": "5452 Port Meadowchester",
+ "surname": "Champlin",
+ "token": "/zHk",
+ "updated_at": 412049759,
+ "username": "Lang1245"
+ },
+ {
+ "age": 59,
+ "birthday": "2012-07-22",
+ "city": "Raleigh",
+ "country": "FM",
+ "created_at": 1641447373,
+ "email": "denisebert@tillman.info",
+ "id": 15,
+ "is_active": false,
+ "money": "502.29",
+ "name": "Natalie",
+ "password": "RqFzm4SJ8ip?2#rG",
+ "role": "moderator",
+ "street": "5197 Missionville",
+ "surname": "Bartell",
+ "token": "Yy*U",
+ "updated_at": 195871529,
+ "username": "Smitham7834"
+ },
+ {
+ "age": 42,
+ "birthday": "1947-01-22",
+ "city": "Arlington",
+ "country": "CD",
+ "created_at": 1366447398,
+ "email": "cordelianader@oreilly.org",
+ "id": 16,
+ "is_active": true,
+ "money": "2120.37",
+ "name": "Elna",
+ "password": "HQVXi.LDl7TNoc6N",
+ "role": "admin",
+ "street": "7808 New Lightland",
+ "surname": "Johns",
+ "token": "rqL$",
+ "updated_at": 532160132,
+ "username": "Haag6886"
+ },
+ {
+ "age": 43,
+ "birthday": "1992-06-30",
+ "city": "San Bernardino",
+ "country": "ML",
+ "created_at": 1403744305,
+ "email": "reeceveum@beier.biz",
+ "id": 17,
+ "is_active": false,
+ "money": "4421.14",
+ "name": "Dax",
+ "password": "bYU$E8vV52r-5x9$",
+ "role": "moderator",
+ "street": "18079 South Vistaburgh",
+ "surname": "Larson",
+ "token": "zbNzFW",
+ "updated_at": 336614920,
+ "username": "Wilderman5042"
+ },
+ {
+ "age": 24,
+ "birthday": "1939-07-18",
+ "city": "Lincoln",
+ "country": "VC",
+ "created_at": 1535157542,
+ "email": "markkoch@kessler.org",
+ "id": 18,
+ "is_active": false,
+ "money": "2868.76",
+ "name": "Rhiannon",
+ "password": "VDI_GKrp\u0026dJASQO7",
+ "role": "user",
+ "street": "801 South Plainsland",
+ "surname": "Ledner",
+ "token": "KF^",
+ "updated_at": 1393119774,
+ "username": "Stanton6153"
+ },
+ {
+ "age": 43,
+ "birthday": "1984-07-23",
+ "city": "Durham",
+ "country": "EH",
+ "created_at": 552308313,
+ "email": "masonstehr@green.org",
+ "id": 19,
+ "is_active": false,
+ "money": "8077.05",
+ "name": "Lauren",
+ "password": "Rb95!04W57-5N!-N",
+ "role": "user",
+ "street": "119 North Curvefort",
+ "surname": "Heaney",
+ "token": "V+Y",
+ "updated_at": 166887067,
+ "username": "Kessler5971"
+ },
+ {
+ "age": 46,
+ "birthday": "1959-06-27",
+ "city": "Baton Rouge",
+ "country": "EH",
+ "created_at": 892589738,
+ "email": "tamarakassulke@conroy.com",
+ "id": 20,
+ "is_active": false,
+ "money": "4827.46",
+ "name": "Delphia",
+ "password": "Ge#aJf826H*YyWi0",
+ "role": "moderator",
+ "street": "7611 Commonmouth",
+ "surname": "Koelpin",
+ "token": "zsVtw",
+ "updated_at": 1135341110,
+ "username": "Kemmer9957"
+ }
+]
diff --git a/user.json b/user.json
new file mode 100644
index 0000000..329ad73
--- /dev/null
+++ b/user.json
@@ -0,0 +1,19 @@
+{
+ "id": "id.sequence",
+ "name": "string.firstname",
+ "surname": "string.lastname",
+ "age": "number.int:18-64",
+ "birthday": "date:yyyy-MM-dd",
+ "money": "number.decimal:2,0-10000",
+ "email": "string.email",
+ "password": "string.password",
+ "username": "string.username",
+ "city": "string.city",
+ "street": "string.street",
+ "country": "string.country:short",
+ "role": "enum:admin,user,moderator",
+ "is_active": "bool",
+ "token": "string",
+ "created_at": "date.timestamp",
+ "updated_at": "date.timestamp"
+}