drawio-export/pkg/drawio/export.go
Алексей Бадяев b32f3ab88a
Some checks failed
golangci-lint / lint (stable, ubuntu-latest) (push) Failing after 4s
golangci-lint / lint (stable, windows-latest) (push) Failing after 12s
build / build (push) Failing after 27s
build / build_windows (push) Successful in 52s
Добавлен линтер golangci-lint
2024-10-17 23:29:54 +07:00

223 lines
4.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package drawio
import (
"bufio"
"errors"
"io"
"io/fs"
"os"
"os/exec"
"path"
"strconv"
"strings"
xmlparser "github.com/tamerh/xml-stream-parser"
)
// Создать нового экспортёра диаграмм
func New(opt ...Option) Exporter {
options := &Options{
Application: "drawio",
Output: "export",
Format: Format("pdf"),
}
for _, opt := range opt {
opt.apply(options)
}
return NewWithOptions(options)
}
// Создать нового экспортёра с параметрами
func NewWithOptions(opts *Options) Exporter {
exp := Exporter{opts: opts}
if opts.openFile == nil {
exp.openFile = func(name string) (io.ReadCloser, error) {
return os.Open(name)
}
} else {
exp.openFile = opts.openFile
}
if opts.readDir == nil {
exp.readDir = os.ReadDir
} else {
exp.readDir = opts.readDir
}
return exp
}
// Экспортёр диаграмм
type Exporter struct {
opts *Options // Параметры экспортёра диаграмм
openFile OpenFileFunc // Открыть файл
readDir ReadDirFunc // Прочитать папку на диске
}
// Экспорт диаграмм из указанный файлов или папок
func (exp Exporter) Export(fileOrDir ...string) error {
var (
commands = []*exec.Cmd{}
errs = []error{}
args = exp.opts.Args()
info fs.FileInfo
)
for _, item := range fileOrDir {
var err error
if info, err = os.Stat(item); err != nil {
errs = append(errs, err)
continue
}
var addCommands []*exec.Cmd
if info.IsDir() {
addCommands, err = exp.ExportDir(item, args)
} else {
addCommands, err = exp.ExportFile(item, args)
}
commands = append(commands, addCommands...)
if err != nil {
errs = append(errs, err)
}
}
if err := RunSequence(commands...); err != nil {
errs = append(errs, err)
}
return errors.Join(errs...)
}
// Экспорт диаграмм из всех файлов в папке
func (exp Exporter) ExportDir(
dirPath string, args []string, subDir ...string,
) ([]*exec.Cmd, error) {
var (
commands = []*exec.Cmd{}
err error
entries []fs.DirEntry
errs []error
)
if entries, err = exp.readDir(dirPath); err != nil {
return commands, err
}
for _, entry := range entries {
name := entry.Name()
var addCommands []*exec.Cmd
if entry.IsDir() {
if exp.opts.Recursive {
deep := len(subDir)
outDir := make([]string, deep+1)
copy(outDir, subDir)
outDir[deep] = name
addCommands, err = exp.ExportDir(path.Join(dirPath, name), args, outDir...)
} else {
continue
}
} else {
addCommands, err = exp.ExportFile(path.Join(dirPath, name), args, subDir...)
}
commands = append(commands, addCommands...)
if err != nil {
errs = append(errs, err)
}
}
return commands, errors.Join(errs...)
}
// Экспорт файла с диаграммами
func (exp Exporter) ExportFile(
filePath string, args []string, subDir ...string,
) ([]*exec.Cmd, error) {
const AdditionalArgsNumber = 10
var (
commands = []*exec.Cmd{}
err error
file io.ReadCloser
diagrams []string
)
if file, err = exp.openFile(filePath); err != nil {
return commands, err
}
defer func() { _ = file.Close() }()
if diagrams, err = Diagrams(file); err != nil {
return commands, err
}
outDirs := make([]string, 1, len(subDir)+1)
outDirs[0] = exp.opts.OutDir()
outDirs = append(outDirs, subDir...)
output := path.Join(outDirs...)
for index, name := range diagrams {
outName := strings.TrimSuffix(path.Base(filePath), path.Ext(filePath))
if !exp.opts.RemovePageSuffix || len(diagrams) > 1 {
outName += "-" + name
}
outName += exp.opts.OutExt()
drawioArgs := make([]string, len(args), len(args)+AdditionalArgsNumber)
copy(drawioArgs, args)
drawioArgs = append(drawioArgs,
"--page-index", strconv.Itoa(index),
"--output", path.Join(output, outName),
"--export", filePath,
)
if exp.opts.EnableXvfb {
drawioArgs = append(drawioArgs,
"--disable-gpu", "--headless", "--no-sandbox")
}
cmd := exec.Command(exp.opts.App(), drawioArgs...) //nolint:gosec
commands = append(commands, cmd)
}
return commands, err
}
// Разбирает данные и возвращает срез имён диаграмм в них
func Diagrams(reader io.Reader) ([]string, error) {
var (
result = []string{}
br = bufio.NewReader(reader)
parser = xmlparser.NewXMLParser(
br, "mxfile", "diagram",
).ParseAttributesOnly("diagram")
)
for xml := range parser.Stream() {
if xml.Err != nil {
return result, xml.Err
}
if items, ok := xml.Childs["diagram"]; ok {
for _, item := range items {
result = append(result, item.Attrs["name"])
}
}
}
return result, nil
}