drawio-export/pkg/drawio/export.go

189 lines
4.6 KiB
Go
Raw Normal View History

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 = func(name string) ([]os.DirEntry, error) {
return os.ReadDir(name)
}
} 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{}
err error
errs = []error{}
args = exp.opts.Args()
info fs.FileInfo
)
for _, item := range fileOrDir {
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) {
var (
commands = []*exec.Cmd{}
err error
file io.ReadCloser
diagrams []string
)
if file, err = exp.openFile(filePath); err != nil {
return commands, err
}
defer 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 i, 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)+4)
copy(drawioArgs, args)
drawioArgs = append(drawioArgs,
"--page-index", strconv.Itoa(i),
"--output", path.Join(output, outName),
"--export", filePath,
"--no-sandbox",
)
cmd := exec.Command(exp.opts.App(), drawioArgs...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
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
}