package internal import ( "crypto/md5" "encoding/base64" "encoding/hex" "fmt" "strings" "sync" "time" ) // NewDiagram returns a new diagram. func NewDiagram(description []byte, imgType string) *Diagram { // Debug: Log what we received fmt.Printf("DEBUG: Received %d bytes\n", len(description)) fmt.Printf("DEBUG: Raw bytes: %q\n", string(description)) fmt.Printf("DEBUG: Contains newlines: %t\n", strings.Contains(string(description), "\n")) trimmed := strings.TrimSpace(string(description)) fmt.Printf("DEBUG: After TrimSpace: %q\n", trimmed) fmt.Printf("DEBUG: Still contains newlines: %t\n", strings.Contains(trimmed, "\n")) // Decode NEWLINE markers back to actual newlines for complex diagrams if strings.Contains(trimmed, " NEWLINE ") { trimmed = strings.ReplaceAll(trimmed, " NEWLINE ", "\n") fmt.Printf("DEBUG: After NEWLINE decoding: %q\n", trimmed) fmt.Printf("DEBUG: Now contains newlines: %t\n", strings.Contains(trimmed, "\n")) } // Decode pipe separators back to newlines for quadrant charts if strings.Contains(trimmed, " | ") { trimmed = strings.ReplaceAll(trimmed, " | ", "\n") fmt.Printf("DEBUG: After pipe decoding: %q\n", trimmed) fmt.Printf("DEBUG: Now contains newlines: %t\n", strings.Contains(trimmed, "\n")) } return &Diagram{ description: []byte(trimmed), lastTouched: time.Now(), mu: &sync.RWMutex{}, imgType: imgType, } } // Diagram represents a single diagram. type Diagram struct { // id is the ID of the Diagram id string // description is the description of the diagram. description []byte // Output is the filepath to the output file. Output string // mu is a mutex to protect the last touched value. mu *sync.RWMutex // lastTouched is the time that the diagram was last used. lastTouched time.Time // the type of image to generate svg or png imgType string } // Touch updates the last touched time of the diagram. func (d *Diagram) Touch() { d.mu.Lock() defer d.mu.Unlock() d.lastTouched = time.Now() } // TouchedInDuration returns true if the diagram has been touched in the given duration. func (d *Diagram) TouchedInDuration(duration time.Duration) bool { d.mu.Lock() defer d.mu.Unlock() return time.Now().Add(-duration).Before(d.lastTouched) } // ID returns an ID for the diagram. // The ID is set from the diagram description. func (d *Diagram) ID() (string, error) { if d.id != "" { return d.id, nil } encoded := base64.StdEncoding.EncodeToString(d.description) hash := md5.Sum([]byte(encoded)) d.id = hex.EncodeToString(hash[:]) + d.imgType return d.id, nil } // Description returns the diagram description. func (d *Diagram) Description() []byte { return d.description } // Description returns the diagram description. func (d *Diagram) WithDescription(description []byte) *Diagram { d.description = description d.id = "" return d }