160 lines
4.2 KiB
Go
160 lines
4.2 KiB
Go
package internal
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
)
|
|
|
|
// Generator provides the ability to generate a diagram.
|
|
type Generator interface {
|
|
// Generate generates the given diagram.
|
|
Generate(diagram *Diagram) error
|
|
// CleanUp removes any diagrams that haven't used within the given duration.
|
|
CleanUp(duration time.Duration) error
|
|
}
|
|
|
|
// NewGenerator returns a generator that can be used to generate diagrams.
|
|
func NewGenerator(cache DiagramCache, mermaidCLIPath string, inPath string, outPath string, puppeteerConfigPath string) Generator {
|
|
return &cachingGenerator{
|
|
cache: cache,
|
|
mermaidCLIPath: mermaidCLIPath,
|
|
inPath: inPath,
|
|
outPath: outPath,
|
|
puppeteerConfigPath: puppeteerConfigPath,
|
|
}
|
|
}
|
|
|
|
// cachingGenerator is an implementation of Generator.
|
|
type cachingGenerator struct {
|
|
cache DiagramCache
|
|
mermaidCLIPath string
|
|
inPath string
|
|
outPath string
|
|
puppeteerConfigPath string
|
|
}
|
|
|
|
// Generate generates the given diagram.
|
|
func (c cachingGenerator) Generate(diagram *Diagram) error {
|
|
has, err := c.cache.Has(diagram)
|
|
if err != nil {
|
|
return fmt.Errorf("cache.Has failed: %w", err)
|
|
}
|
|
if has {
|
|
cached, err := c.cache.Get(diagram)
|
|
if err != nil {
|
|
return fmt.Errorf("cache.Get failed: %w", err)
|
|
}
|
|
*diagram = *cached
|
|
|
|
// Update diagram last touched date
|
|
diagram.Touch()
|
|
if err := c.cache.Store(diagram); err != nil {
|
|
return fmt.Errorf("cache.Store failed: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
diagram.Touch()
|
|
if err := c.generate(diagram); err != nil {
|
|
return fmt.Errorf("cachingGenerater.generate failed: %w", err)
|
|
}
|
|
if err := c.cache.Store(diagram); err != nil {
|
|
return fmt.Errorf("cache.Store failed: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// generate does the actual file generation.
|
|
func (c cachingGenerator) generate(diagram *Diagram) error {
|
|
id, err := diagram.ID()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get diagram ID: %w", err)
|
|
}
|
|
|
|
inPath := fmt.Sprintf("%s/%s.mmd", c.inPath, id)
|
|
outPath := fmt.Sprintf("%s/%s.%s", c.outPath, id, diagram.imgType)
|
|
|
|
if err := ioutil.WriteFile(inPath, diagram.description, 0644); err != nil {
|
|
return fmt.Errorf("could not write to input file [%s]: %w", inPath, err)
|
|
}
|
|
|
|
_, err = os.Stat(c.mermaidCLIPath)
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("mermaid executable does not exist: %w", err)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("could not stat mermaid executable: %w", err)
|
|
}
|
|
|
|
args := []string{
|
|
"-i", inPath,
|
|
"-o", outPath,
|
|
}
|
|
if c.puppeteerConfigPath != "" {
|
|
args = append(args, "-p", c.puppeteerConfigPath)
|
|
}
|
|
|
|
cmd := exec.Command(c.mermaidCLIPath, args...)
|
|
var stdOut bytes.Buffer
|
|
var stdErr bytes.Buffer
|
|
cmd.Stdout = bufio.NewWriter(&stdOut)
|
|
cmd.Stderr = bufio.NewWriter(&stdErr)
|
|
if err := cmd.Run(); err != nil {
|
|
return fmt.Errorf("failed when executing mermaid: %w: %s: %s", err, string(stdOut.Bytes()), string(stdErr.Bytes()))
|
|
}
|
|
log.Printf("Generated: %s: %s: %s", id, string(stdOut.Bytes()), string(stdErr.Bytes()))
|
|
|
|
diagram.Output = outPath
|
|
|
|
return nil
|
|
}
|
|
|
|
// CleanUp removes any diagrams that haven't used within the given duration.
|
|
func (c cachingGenerator) CleanUp(duration time.Duration) error {
|
|
log.Printf("Running cleanup")
|
|
diagrams, err := c.cache.GetAll()
|
|
if err != nil {
|
|
return fmt.Errorf("could not get cached diagrams: %w", err)
|
|
}
|
|
for _, d := range diagrams {
|
|
if !d.TouchedInDuration(duration) {
|
|
if err := c.delete(d); err != nil {
|
|
return fmt.Errorf("could not delete diagram: %w", err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// delete removes any diagrams that haven't used within the given duration.
|
|
func (c cachingGenerator) delete(diagram *Diagram) error {
|
|
id, err := diagram.ID()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get diagram ID: %w", err)
|
|
}
|
|
|
|
log.Printf("Cleaning up diagram: %s", id)
|
|
|
|
inPath := fmt.Sprintf("%s/%s.mmd", c.inPath, id)
|
|
outPath := fmt.Sprintf("%s/%s.svg", c.outPath, id)
|
|
|
|
if err := os.Remove(inPath); err != nil {
|
|
return fmt.Errorf("could not delete diagram input: %w", err)
|
|
}
|
|
if err := os.Remove(outPath); err != nil {
|
|
return fmt.Errorf("could not delete diagram output: %w", err)
|
|
}
|
|
if err := c.cache.Delete(diagram); err != nil {
|
|
return fmt.Errorf("could not remove diagram from cache: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|