add html and server interpreters

these are basic and lack most features.
the server seems to work the best.
the html on the other hand is really rough and doesn't seem to work yet.
but it does build the pages and they have all the shapes and sections we
wanted. More work to come. :)
This commit is contained in:
2025-08-25 00:50:55 -06:00
parent cf3ad736b7
commit d36e1bfd86
5 changed files with 681 additions and 6 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
/html-output/
/masonry.exe

View File

@ -14,6 +14,7 @@ func main() {
tailwindCmd(),
setupCmd(),
vueGenCmd(),
serveCmd(), // New command for server interpreter
}
app := &cli.App{

View File

@ -464,3 +464,111 @@ func generateHTML(inputFile, outputDir string) error {
fmt.Printf("Successfully generated %d HTML file(s)\n", len(htmlFiles))
return nil
}
func serveCmd() *cli.Command {
return &cli.Command{
Name: "serve",
Usage: "Generate and run a simple HTTP server from a Masonry file",
Description: "This command parses a Masonry file and generates a simple Go HTTP server with in-memory database.",
Category: "development",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "file",
Usage: "Path to the Masonry file to interpret",
Required: true,
Aliases: []string{"f"},
},
&cli.StringFlag{
Name: "output",
Usage: "Output file for the generated server code",
Value: "server.go",
Aliases: []string{"o"},
},
&cli.BoolFlag{
Name: "run",
Usage: "Run the server after generating it",
Value: false,
Aliases: []string{"r"},
},
},
Action: func(c *cli.Context) error {
masonryFile := c.String("file")
outputFile := c.String("output")
shouldRun := c.Bool("run")
fmt.Printf("Parsing Masonry file: %s\n", masonryFile)
// Read the Masonry file
content, err := os.ReadFile(masonryFile)
if err != nil {
return fmt.Errorf("error reading Masonry file: %w", err)
}
// Parse the Masonry file
parser, err := participle.Build[lang.AST](
participle.Unquote("String"),
)
if err != nil {
return fmt.Errorf("error building parser: %w", err)
}
ast, err := parser.ParseString("", string(content))
if err != nil {
return fmt.Errorf("error parsing Masonry file: %w", err)
}
// Generate server code using the server interpreter
serverInterpreter := interpreter.NewServerInterpreter()
serverCode, err := serverInterpreter.Interpret(*ast)
if err != nil {
return fmt.Errorf("error interpreting Masonry file: %w", err)
}
// Write the generated server code to the output file
err = os.WriteFile(outputFile, []byte(serverCode), 0644)
if err != nil {
return fmt.Errorf("error writing server code to file: %w", err)
}
fmt.Printf("Server code generated successfully: %s\n", outputFile)
if shouldRun {
fmt.Println("Installing dependencies...")
// Initialize go module if it doesn't exist
if _, err := os.Stat("go.mod"); os.IsNotExist(err) {
cmd := exec.Command("go", "mod", "init", "masonry-server")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("error initializing go module: %w", err)
}
}
// Install required dependencies
dependencies := []string{
"github.com/google/uuid",
"github.com/gorilla/mux",
}
for _, dep := range dependencies {
fmt.Printf("Installing %s...\n", dep)
cmd := exec.Command("go", "get", dep)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("error installing dependency %s: %w", dep, err)
}
}
fmt.Printf("Running server from %s...\n", outputFile)
cmd := exec.Command("go", "run", outputFile)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
return nil
},
}
}

View File

@ -10,6 +10,7 @@ import (
type HTMLInterpreter struct {
entities map[string]*lang.Entity
pages map[string]*lang.Page
server *lang.Server
}
// NewHTMLInterpreter creates a new HTML interpreter
@ -40,7 +41,7 @@ func (hi *HTMLInterpreter) escapeHTML(s string) string {
// GenerateHTML converts a Masonry AST to HTML output
func (hi *HTMLInterpreter) GenerateHTML(ast *lang.AST) (map[string]string, error) {
// First pass: collect entities and pages
// First pass: collect entities, pages, and server config
for _, def := range ast.Definitions {
if def.Entity != nil {
hi.entities[def.Entity.Name] = def.Entity
@ -48,6 +49,9 @@ func (hi *HTMLInterpreter) GenerateHTML(ast *lang.AST) (map[string]string, error
if def.Page != nil {
hi.pages[def.Page.Name] = def.Page
}
if def.Server != nil {
hi.server = def.Server
}
}
// Second pass: generate HTML for each page
@ -135,6 +139,86 @@ func (hi *HTMLInterpreter) generatePageHTML(page *lang.Page) (string, error) {
// JavaScript for interactivity
html.WriteString(" <script>\n")
// API Base URL configuration
apiBaseURL := "http://localhost:8080"
if hi.server != nil {
host := "localhost"
port := 8080
for _, setting := range hi.server.Settings {
if setting.Host != nil {
host = *setting.Host
}
if setting.Port != nil {
port = *setting.Port
}
}
apiBaseURL = fmt.Sprintf("http://%s:%d", host, port)
}
html.WriteString(fmt.Sprintf(" const API_BASE_URL = '%s';\n", apiBaseURL))
html.WriteString(" \n")
html.WriteString(" // API helper functions\n")
html.WriteString(" async function apiRequest(method, endpoint, data = null) {\n")
html.WriteString(" const config = {\n")
html.WriteString(" method: method,\n")
html.WriteString(" headers: {\n")
html.WriteString(" 'Content-Type': 'application/json',\n")
html.WriteString(" },\n")
html.WriteString(" };\n")
html.WriteString(" \n")
html.WriteString(" if (data) {\n")
html.WriteString(" config.body = JSON.stringify(data);\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" try {\n")
html.WriteString(" const response = await fetch(API_BASE_URL + endpoint, config);\n")
html.WriteString(" \n")
html.WriteString(" if (!response.ok) {\n")
html.WriteString(" throw new Error(`HTTP error! status: ${response.status}`);\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" if (response.status === 204) {\n")
html.WriteString(" return null; // No content\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" return await response.json();\n")
html.WriteString(" } catch (error) {\n")
html.WriteString(" console.error('API request failed:', error);\n")
html.WriteString(" alert('Error: ' + error.message);\n")
html.WriteString(" throw error;\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Entity-specific API functions\n")
// Generate API functions for each entity
for entityName := range hi.entities {
entityLower := strings.ToLower(entityName)
entityPlural := entityLower + "s"
html.WriteString(fmt.Sprintf(" async function list%s() {\n", entityName))
html.WriteString(fmt.Sprintf(" return await apiRequest('GET', '/%s');\n", entityPlural))
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(fmt.Sprintf(" async function get%s(id) {\n", entityName))
html.WriteString(fmt.Sprintf(" return await apiRequest('GET', '/%s/' + id);\n", entityPlural))
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(fmt.Sprintf(" async function create%s(data) {\n", entityName))
html.WriteString(fmt.Sprintf(" return await apiRequest('POST', '/%s', data);\n", entityPlural))
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(fmt.Sprintf(" async function update%s(id, data) {\n", entityName))
html.WriteString(fmt.Sprintf(" return await apiRequest('PUT', '/%s/' + id, data);\n", entityPlural))
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(fmt.Sprintf(" async function delete%s(id) {\n", entityName))
html.WriteString(fmt.Sprintf(" return await apiRequest('DELETE', '/%s/' + id);\n", entityPlural))
html.WriteString(" }\n")
html.WriteString(" \n")
}
html.WriteString(" // Tab functionality\n")
html.WriteString(" function showTab(tabName) {\n")
html.WriteString(" const tabs = document.querySelectorAll('.tab-content');\n")
@ -153,13 +237,110 @@ func (hi *HTMLInterpreter) generatePageHTML(page *lang.Page) (string, error) {
html.WriteString(" document.getElementById(modalId).style.display = 'none';\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Form validation and submission\n")
html.WriteString(" function submitForm(formId) {\n")
html.WriteString(" // Enhanced form submission with API integration\n")
html.WriteString(" async function submitForm(formId, entityType = null) {\n")
html.WriteString(" const form = document.getElementById(formId);\n")
html.WriteString(" const formData = new FormData(form);\n")
html.WriteString(" console.log('Form submitted:', Object.fromEntries(formData));\n")
html.WriteString(" const data = Object.fromEntries(formData);\n")
html.WriteString(" \n")
html.WriteString(" console.log('Form data:', data);\n")
html.WriteString(" \n")
html.WriteString(" if (entityType) {\n")
html.WriteString(" try {\n")
html.WriteString(" const result = await window['create' + entityType](data);\n")
html.WriteString(" console.log('Created:', result);\n")
html.WriteString(" alert(entityType + ' created successfully!');\n")
html.WriteString(" form.reset();\n")
html.WriteString(" // Refresh any tables showing this entity type\n")
html.WriteString(" await refreshTables(entityType);\n")
html.WriteString(" } catch (error) {\n")
html.WriteString(" console.error('Form submission failed:', error);\n")
html.WriteString(" }\n")
html.WriteString(" } else {\n")
html.WriteString(" alert('Form submitted successfully!');\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Load data into tables\n")
html.WriteString(" async function loadTableData(tableId, entityType) {\n")
html.WriteString(" try {\n")
html.WriteString(" const data = await window['list' + entityType]();\n")
html.WriteString(" const table = document.getElementById(tableId);\n")
html.WriteString(" const tbody = table.querySelector('tbody');\n")
html.WriteString(" \n")
html.WriteString(" if (!data || data.length === 0) {\n")
html.WriteString(" tbody.innerHTML = '<tr><td colspan=\"100%\">No data found</td></tr>';\n")
html.WriteString(" return;\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" tbody.innerHTML = '';\n")
html.WriteString(" data.forEach(item => {\n")
html.WriteString(" const row = document.createElement('tr');\n")
html.WriteString(" const headers = table.querySelectorAll('thead th');\n")
html.WriteString(" \n")
html.WriteString(" headers.forEach((header, index) => {\n")
html.WriteString(" const cell = document.createElement('td');\n")
html.WriteString(" const fieldName = header.textContent.toLowerCase().replace(/\\s+/g, '_');\n")
html.WriteString(" cell.textContent = item[fieldName] || item[header.textContent.toLowerCase()] || '';\n")
html.WriteString(" row.appendChild(cell);\n")
html.WriteString(" });\n")
html.WriteString(" \n")
html.WriteString(" // Add action buttons\n")
html.WriteString(" const actionCell = document.createElement('td');\n")
html.WriteString(" actionCell.innerHTML = `\n")
html.WriteString(" <button onclick=\"editItem('${item.id}', '${entityType}')\" class=\"button button-primary\">Edit</button>\n")
html.WriteString(" <button onclick=\"deleteItem('${item.id}', '${entityType}')\" class=\"button button-secondary\">Delete</button>\n")
html.WriteString(" `;\n")
html.WriteString(" row.appendChild(actionCell);\n")
html.WriteString(" \n")
html.WriteString(" tbody.appendChild(row);\n")
html.WriteString(" });\n")
html.WriteString(" } catch (error) {\n")
html.WriteString(" console.error('Failed to load table data:', error);\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Refresh all tables of a specific entity type\n")
html.WriteString(" async function refreshTables(entityType) {\n")
html.WriteString(" const tables = document.querySelectorAll(`table[data-entity=\"${entityType}\"]`);\n")
html.WriteString(" tables.forEach(table => {\n")
html.WriteString(" loadTableData(table.id, entityType);\n")
html.WriteString(" });\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Edit and delete functions\n")
html.WriteString(" async function editItem(id, entityType) {\n")
html.WriteString(" try {\n")
html.WriteString(" const item = await window['get' + entityType](id);\n")
html.WriteString(" console.log('Edit item:', item);\n")
html.WriteString(" // TODO: Populate form with item data for editing\n")
html.WriteString(" alert('Edit functionality coming soon!');\n")
html.WriteString(" } catch (error) {\n")
html.WriteString(" console.error('Failed to fetch item for editing:', error);\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" async function deleteItem(id, entityType) {\n")
html.WriteString(" if (confirm('Are you sure you want to delete this item?')) {\n")
html.WriteString(" try {\n")
html.WriteString(" await window['delete' + entityType](id);\n")
html.WriteString(" alert('Item deleted successfully!');\n")
html.WriteString(" await refreshTables(entityType);\n")
html.WriteString(" } catch (error) {\n")
html.WriteString(" console.error('Failed to delete item:', error);\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" }\n")
html.WriteString(" \n")
html.WriteString(" // Initialize page - load data when page loads\n")
html.WriteString(" document.addEventListener('DOMContentLoaded', function() {\n")
html.WriteString(" // Auto-load data for all tables with data-entity attribute\n")
html.WriteString(" const tables = document.querySelectorAll('table[data-entity]');\n")
html.WriteString(" tables.forEach(table => {\n")
html.WriteString(" const entityType = table.getAttribute('data-entity');\n")
html.WriteString(" loadTableData(table.id, entityType);\n")
html.WriteString(" });\n")
html.WriteString(" });\n")
html.WriteString(" </script>\n")
html.WriteString("</body>\n</html>")

View File

@ -0,0 +1,386 @@
package interpreter
import (
"fmt"
"go/format"
"masonry/lang"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// ServerInterpreter converts Masonry AST to a simple Go HTTP server
type ServerInterpreter struct {
entities map[string]*lang.Entity
endpoints map[string]*lang.Endpoint
server *lang.Server
}
// NewServerInterpreter creates a new server interpreter
func NewServerInterpreter() *ServerInterpreter {
return &ServerInterpreter{
entities: make(map[string]*lang.Entity),
endpoints: make(map[string]*lang.Endpoint),
}
}
// Interpret processes the AST and generates server code
func (si *ServerInterpreter) Interpret(ast lang.AST) (string, error) {
// First pass: collect all definitions
for _, def := range ast.Definitions {
if def.Server != nil {
si.server = def.Server
}
if def.Entity != nil {
si.entities[def.Entity.Name] = def.Entity
}
if def.Endpoint != nil {
key := fmt.Sprintf("%s_%s", def.Endpoint.Method, strings.ReplaceAll(def.Endpoint.Path, "/", "_"))
si.endpoints[key] = def.Endpoint
}
}
// Generate the server code
rawCode, err := si.generateServer()
if err != nil {
return "", err
}
// Format the code to remove unused imports and fix formatting
formattedCode, err := format.Source([]byte(rawCode))
if err != nil {
// If formatting fails, return the raw code with a warning comment
return "// Warning: Code formatting failed, but code should still be functional\n" + rawCode, nil
}
return string(formattedCode), nil
}
// generateServer creates the complete server code
func (si *ServerInterpreter) generateServer() (string, error) {
var code strings.Builder
// Package and imports - only include what's actually used
code.WriteString(`package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
"time"
"github.com/google/uuid"
"github.com/gorilla/mux"
)
`)
// Generate entity structs
code.WriteString("// Entity definitions\n")
for _, entity := range si.entities {
code.WriteString(si.generateEntityStruct(entity))
code.WriteString("\n")
}
// Generate in-memory database
code.WriteString(si.generateInMemoryDB())
// Generate handlers
code.WriteString("// HTTP Handlers\n")
for _, entity := range si.entities {
code.WriteString(si.generateEntityHandlers(entity))
}
// Generate custom endpoint handlers
for _, endpoint := range si.endpoints {
if endpoint.Entity == nil {
code.WriteString(si.generateCustomEndpointHandler(endpoint))
}
}
// Generate main function
code.WriteString(si.generateMainFunction())
return code.String(), nil
}
// generateEntityStruct creates a Go struct for an entity
func (si *ServerInterpreter) generateEntityStruct(entity *lang.Entity) string {
var code strings.Builder
if entity.Description != nil {
code.WriteString(fmt.Sprintf("// %s - %s\n", entity.Name, *entity.Description))
}
code.WriteString(fmt.Sprintf("type %s struct {\n", entity.Name))
// Always add ID field
code.WriteString("\tID string `json:\"id\"`\n")
code.WriteString("\tCreatedAt time.Time `json:\"created_at\"`\n")
code.WriteString("\tUpdatedAt time.Time `json:\"updated_at\"`\n")
for _, field := range entity.Fields {
goType := si.convertToGoType(field.Type)
jsonTag := strings.ToLower(field.Name)
if !field.Required {
goType = "*" + goType
}
code.WriteString(fmt.Sprintf("\t%s %s `json:\"%s\"`\n",
cases.Title(language.English).String(field.Name), goType, jsonTag))
}
code.WriteString("}\n")
return code.String()
}
// convertToGoType maps Masonry types to Go types
func (si *ServerInterpreter) convertToGoType(masonryType string) string {
switch masonryType {
case "string", "text", "email", "url":
return "string"
case "int", "number":
return "int"
case "float":
return "float64"
case "boolean":
return "bool"
case "uuid":
return "string"
case "timestamp":
return "time.Time"
default:
return "string" // default fallback
}
}
// generateInMemoryDB creates the in-memory database structure
func (si *ServerInterpreter) generateInMemoryDB() string {
var code strings.Builder
code.WriteString("// In-memory database\n")
code.WriteString("type InMemoryDB struct {\n")
code.WriteString("\tmu sync.RWMutex\n")
for entityName := range si.entities {
code.WriteString(fmt.Sprintf("\t%s map[string]*%s\n",
strings.ToLower(entityName)+"s", entityName))
}
code.WriteString("}\n\n")
code.WriteString("var db = &InMemoryDB{\n")
for entityName := range si.entities {
code.WriteString(fmt.Sprintf("\t%s: make(map[string]*%s),\n",
strings.ToLower(entityName)+"s", entityName))
}
code.WriteString("}\n\n")
return code.String()
}
// generateEntityHandlers creates CRUD handlers for an entity
func (si *ServerInterpreter) generateEntityHandlers(entity *lang.Entity) string {
var code strings.Builder
entityName := entity.Name
entityLower := strings.ToLower(entityName)
entityPlural := entityLower + "s"
// List handler
code.WriteString(fmt.Sprintf(`func list%sHandler(w http.ResponseWriter, r *http.Request) {
db.mu.RLock()
defer db.mu.RUnlock()
var items []*%s
for _, item := range db.%s {
items = append(items, item)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(items)
}
`, entityName, entityName, entityPlural))
// Get by ID handler
code.WriteString(fmt.Sprintf(`func get%sHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
db.mu.RLock()
item, exists := db.%s[id]
db.mu.RUnlock()
if !exists {
http.Error(w, "%s not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(item)
}
`, entityName, entityPlural, entityName))
// Create handler
code.WriteString(fmt.Sprintf(`func create%sHandler(w http.ResponseWriter, r *http.Request) {
var item %s
if err := json.NewDecoder(r.Body).Decode(&item); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
item.ID = uuid.New().String()
item.CreatedAt = time.Now()
item.UpdatedAt = time.Now()
db.mu.Lock()
db.%s[item.ID] = &item
db.mu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(item)
}
`, entityName, entityName, entityPlural))
// Update handler
code.WriteString(fmt.Sprintf(`func update%sHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
db.mu.Lock()
defer db.mu.Unlock()
existing, exists := db.%s[id]
if !exists {
http.Error(w, "%s not found", http.StatusNotFound)
return
}
var updates %s
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Preserve ID and timestamps
updates.ID = existing.ID
updates.CreatedAt = existing.CreatedAt
updates.UpdatedAt = time.Now()
db.%s[id] = &updates
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(updates)
}
`, entityName, entityPlural, entityName, entityName, entityPlural))
// Delete handler
code.WriteString(fmt.Sprintf(`func delete%sHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
db.mu.Lock()
defer db.mu.Unlock()
if _, exists := db.%s[id]; !exists {
http.Error(w, "%s not found", http.StatusNotFound)
return
}
delete(db.%s, id)
w.WriteHeader(http.StatusNoContent)
}
`, entityName, entityPlural, entityName, entityPlural))
return code.String()
}
// generateCustomEndpointHandler creates handlers for custom endpoints
func (si *ServerInterpreter) generateCustomEndpointHandler(endpoint *lang.Endpoint) string {
var code strings.Builder
handlerName := fmt.Sprintf("%s%sHandler",
strings.ToLower(endpoint.Method),
strings.ReplaceAll(cases.Title(language.English).String(endpoint.Path), "/", ""))
code.WriteString(fmt.Sprintf(`func %s(w http.ResponseWriter, r *http.Request) {
// Custom endpoint: %s %s
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Custom endpoint %s %s",
"status": "success",
})
}
`, handlerName, endpoint.Method, endpoint.Path, endpoint.Method, endpoint.Path))
return code.String()
}
// generateMainFunction creates the main function with routing
func (si *ServerInterpreter) generateMainFunction() string {
var code strings.Builder
code.WriteString("func main() {\n")
code.WriteString("\tr := mux.NewRouter()\n\n")
// Add routes for each entity
for entityName := range si.entities {
entityLower := strings.ToLower(entityName)
entityPlural := entityLower + "s"
code.WriteString(fmt.Sprintf("\t// %s routes\n", entityName))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"/%s\", list%sHandler).Methods(\"GET\")\n",
entityPlural, entityName))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"/%s/{id}\", get%sHandler).Methods(\"GET\")\n",
entityPlural, entityName))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"/%s\", create%sHandler).Methods(\"POST\")\n",
entityPlural, entityName))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"/%s/{id}\", update%sHandler).Methods(\"PUT\")\n",
entityPlural, entityName))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"/%s/{id}\", delete%sHandler).Methods(\"DELETE\")\n",
entityPlural, entityName))
code.WriteString("\n")
}
// Add custom endpoint routes
for _, endpoint := range si.endpoints {
if endpoint.Entity == nil {
handlerName := fmt.Sprintf("%s%sHandler",
strings.ToLower(endpoint.Method),
strings.ReplaceAll(cases.Title(language.English).String(endpoint.Path), "/", ""))
code.WriteString(fmt.Sprintf("\tr.HandleFunc(\"%s\", %s).Methods(\"%s\")\n",
endpoint.Path, handlerName, endpoint.Method))
}
}
// Server configuration
host := "localhost"
port := 8080
if si.server != nil {
for _, setting := range si.server.Settings {
if setting.Host != nil {
host = *setting.Host
}
if setting.Port != nil {
port = *setting.Port
}
}
}
code.WriteString(fmt.Sprintf("\n\taddr := \"%s:%d\"\n", host, port))
code.WriteString("\tfmt.Printf(\"Server starting on %s\\n\", addr)\n")
code.WriteString("\tlog.Fatal(http.ListenAndServe(addr, r))\n")
code.WriteString("}\n")
return code.String()
}