mirror of
https://github.com/Dadido3/go-typst.git
synced 2025-11-20 03:49:34 +00:00
- Move all Typst arguments into the respective Args methods the Options* types - Simplify the logic in the Caller implementations - Add docker volume handling for other commands than compile - Get rid of options_test.go as its test got replaced with the TestCLI_FontsWithFontPaths test
156 lines
4.0 KiB
Go
156 lines
4.0 KiB
Go
// Copyright (c) 2025 David Vogel
|
|
//
|
|
// This software is released under the MIT License.
|
|
// https://opensource.org/licenses/MIT
|
|
|
|
package typst
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
)
|
|
|
|
// Theoretically it's possible to use the Docker SDK directly:
|
|
// https://docs.docker.com/reference/api/engine/sdk/examples/
|
|
// But that dependency is unnecessarily huge, therefore we will just call the Docker executable.
|
|
|
|
// The default Docker image to use.
|
|
// This is the latest supported version of Typst.
|
|
const DockerDefaultImage = "ghcr.io/typst/typst:0.14.0"
|
|
|
|
// Docker allows you to invoke commands on a Typst Docker image.
|
|
type Docker struct {
|
|
Image string // The image to use, defaults to the latest supported offical Typst Docker image if left empty. See: typst.DockerDefaultImage.
|
|
WorkingDirectory string // The working directory of Docker. When left empty, Docker will be run with the process's current working directory.
|
|
|
|
// Additional bind-mounts or volumes that are passed via "--volume" flag to Docker.
|
|
// For details, see: https://docs.docker.com/engine/storage/volumes/#syntax
|
|
//
|
|
// Example:
|
|
// typst.Docker{Volumes: []string{".:/markup"}} // This bind mounts the current working directory to "/markup" inside the container.
|
|
// typst.Docker{Volumes: []string{"/usr/share/fonts:/usr/share/fonts"}} // This makes all system fonts available to Typst running inside the container.
|
|
Volumes []string
|
|
}
|
|
|
|
// Ensure that Docker implements the Caller interface.
|
|
var _ Caller = Docker{}
|
|
|
|
// args returns docker related arguments.
|
|
func (d Docker) args() []string {
|
|
image := DockerDefaultImage
|
|
if d.Image != "" {
|
|
image = d.Image
|
|
}
|
|
|
|
// Argument -i is needed for stdio to work.
|
|
args := []string{"run", "-i"}
|
|
|
|
// Add mounts.
|
|
for _, volume := range d.Volumes {
|
|
args = append(args, "-v", volume)
|
|
}
|
|
|
|
// Which docker image to use.
|
|
args = append(args, image)
|
|
|
|
return args
|
|
}
|
|
|
|
// VersionString returns the Typst version as a string.
|
|
func (d Docker) VersionString() (string, error) {
|
|
args := append(d.args(), "--version")
|
|
|
|
cmd := exec.Command("docker", args...)
|
|
|
|
var output, errBuffer bytes.Buffer
|
|
cmd.Stdout = &output
|
|
cmd.Stderr = &errBuffer
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
switch err := err.(type) {
|
|
case *exec.ExitError:
|
|
return "", ParseStderr(errBuffer.String(), err)
|
|
default:
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return output.String(), nil
|
|
}
|
|
|
|
// Fonts returns all fonts that are available to Typst.
|
|
// The options parameter is optional, and can be nil.
|
|
func (d Docker) Fonts(options *OptionsFonts) ([]string, error) {
|
|
args := d.args()
|
|
|
|
if options == nil {
|
|
options = new(OptionsFonts)
|
|
}
|
|
args = append(args, options.Args()...)
|
|
|
|
cmd := exec.Command("docker", args...)
|
|
|
|
var output, errBuffer bytes.Buffer
|
|
cmd.Stdout = &output
|
|
cmd.Stderr = &errBuffer
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
switch err := err.(type) {
|
|
case *exec.ExitError:
|
|
return nil, ParseStderr(errBuffer.String(), err)
|
|
default:
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
var result []string
|
|
scanner := bufio.NewScanner(&output)
|
|
for scanner.Scan() {
|
|
result = append(result, scanner.Text())
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Compile takes a Typst document from input, and renders it into the output writer.
|
|
// The options parameter is optional, and can be nil.
|
|
func (d Docker) Compile(input io.Reader, output io.Writer, options *OptionsCompile) error {
|
|
args := d.args()
|
|
|
|
// From here on come Typst arguments.
|
|
|
|
if options == nil {
|
|
options = new(OptionsCompile)
|
|
}
|
|
args = append(args, options.Args()...)
|
|
|
|
cmd := exec.Command("docker", args...)
|
|
cmd.Dir = d.WorkingDirectory
|
|
cmd.Stdin = input
|
|
cmd.Stdout = output
|
|
|
|
errBuffer := bytes.Buffer{}
|
|
cmd.Stderr = &errBuffer
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
switch err := err.(type) {
|
|
case *exec.ExitError:
|
|
if err.ExitCode() >= 125 {
|
|
// Most likely docker related error.
|
|
// TODO: Find a better way to distinguish between Typst or Docker errors.
|
|
return fmt.Errorf("exit code %d: %s", err.ExitCode(), errBuffer.String())
|
|
} else {
|
|
// Typst related error.
|
|
return ParseStderr(errBuffer.String(), err)
|
|
}
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|