readysite / website / internal / assist / helpers.go
2.9 KB
helpers.go
package assist

import (
	"encoding/json"
	"strings"

	"github.com/readysite/readysite/website/models"
)

// jsonResult marshals a value to JSON string.
func jsonResult(v any) (string, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

// clonePage creates a deep copy of a page for mutation tracking.
func clonePage(p *models.Page) *models.Page {
	return &models.Page{
		ParentID: p.ParentID,
		Position: p.Position,
	}
}

// cloneCollection creates a deep copy of a collection for mutation tracking.
func cloneCollection(c *models.Collection) *models.Collection {
	return &models.Collection{
		Name:        c.Name,
		Description: c.Description,
		Schema:      c.Schema,
		ListRule:    c.ListRule,
		ViewRule:    c.ViewRule,
		CreateRule:  c.CreateRule,
		UpdateRule:  c.UpdateRule,
		DeleteRule:  c.DeleteRule,
		System:      c.System,
	}
}

// cloneDocument creates a deep copy of a document for mutation tracking.
func cloneDocument(d *models.Document) *models.Document {
	return &models.Document{
		CollectionID: d.CollectionID,
		Data:         d.Data,
	}
}

// clonePartial creates a deep copy of a partial for mutation tracking.
func clonePartial(p *models.Partial) *models.Partial {
	return &models.Partial{
		Name:        p.Name,
		Description: p.Description,
		HTML:        p.HTML,
		Published:   p.Published,
	}
}

// cloneFile creates a deep copy of a file for mutation tracking.
func cloneFile(f *models.File) *models.File {
	return &models.File{
		Name:      f.Name,
		MimeType:  f.MimeType,
		Size:      f.Size,
		UserID:    f.UserID,
		Published: f.Published,
		Path:      f.Path,
	}
}

// unescapeHTML fixes double-escaped JSON sequences in HTML content.
// This handles cases where the AI sends back content with literal \n and \"
// instead of actual newlines and quotes.
func unescapeHTML(s string) string {
	// Only unescape if we detect the pattern of double-escaped content
	// (contains literal \n or \" sequences)
	if !strings.Contains(s, `\n`) && !strings.Contains(s, `\"`) {
		return s
	}

	// Replace common JSON escape sequences with their actual characters
	s = strings.ReplaceAll(s, `\n`, "\n")
	s = strings.ReplaceAll(s, `\r`, "\r")
	s = strings.ReplaceAll(s, `\t`, "\t")
	s = strings.ReplaceAll(s, `\"`, `"`)
	s = strings.ReplaceAll(s, `\\`, `\`)

	return s
}

// slugify converts a string to a URL-friendly slug.
func slugify(s string) string {
	s = strings.ToLower(s)
	s = strings.TrimSpace(s)

	// Replace common separators with hyphens
	for _, sep := range []string{" ", "_", ".", ","} {
		s = strings.ReplaceAll(s, sep, "-")
	}

	// Remove non-alphanumeric characters except hyphens
	var result strings.Builder
	for _, r := range s {
		if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' {
			result.WriteRune(r)
		}
	}
	s = result.String()

	// Collapse multiple hyphens
	for strings.Contains(s, "--") {
		s = strings.ReplaceAll(s, "--", "-")
	}

	// Trim leading/trailing hyphens
	s = strings.Trim(s, "-")

	return s
}
← Back