124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
package step
|
|
|
|
import "os"
|
|
import "io"
|
|
import "time"
|
|
import "strings"
|
|
import "path/filepath"
|
|
import "html/template"
|
|
|
|
// Document represents a STEP file.
|
|
type Document struct {
|
|
Author string
|
|
Title string
|
|
Extends string
|
|
Meta Meta
|
|
|
|
// WORM:
|
|
environment *Environment
|
|
name string
|
|
parseTime time.Time
|
|
template *template.Template
|
|
}
|
|
|
|
// Execute executes this document and writes the result to the output. This
|
|
// method is safe for concurrent use by multiple goroutines.
|
|
func (this *Document) Execute (output io.Writer, data ExecutionData) error {
|
|
if this.Extends == "" {
|
|
// no parent
|
|
return this.template.Execute(output, data)
|
|
}
|
|
|
|
// execute into string builder
|
|
outputBuilder := strings.Builder { }
|
|
// TODO catch template errors here and offset their row number by the
|
|
// number of rows taken up by the front matter
|
|
err := this.template.Execute(&outputBuilder, data)
|
|
// ignore ErrExecutionCanceled, because it's meant to gracefully stop
|
|
// the template execution
|
|
if err != nil { return err }
|
|
|
|
// execute parent with this document's result
|
|
parent, err := this.environment.LoadRelative(this.Extends, this)
|
|
if err != nil { return err }
|
|
return parent.Execute(output, ExecutionData {
|
|
Data: data.Data,
|
|
Child: &ExecutionResult {
|
|
Author: this.Author,
|
|
Title: this.Title,
|
|
Extends: this.Extends,
|
|
Meta: this.Meta,
|
|
Body: template.HTML(outputBuilder.String()),
|
|
},
|
|
})
|
|
}
|
|
|
|
// Environment returns the environment this document is in.
|
|
func (this *Document) Environment () *Environment {
|
|
return this.environment
|
|
}
|
|
|
|
// Abs returns an absolute representation of the given path. If the path is an
|
|
// absolute path already, it is returned as-is. If the path is a relative path,
|
|
// it is treated as relative to the current working directory. If the path is a
|
|
// relative path beginning with "." or "..", it is treated as relative to this
|
|
// document. The result is cleaned.
|
|
func (this *Document) Abs (name string) (string, error) {
|
|
if filepath.IsAbs(name) {
|
|
return filepath.Clean(name), nil
|
|
}
|
|
if strings.HasPrefix(name, ".") {
|
|
directory := this.dir()
|
|
return filepath.Abs(filepath.Join(directory, name))
|
|
}
|
|
return filepath.Abs(name)
|
|
}
|
|
|
|
// Rel returns the given path relative to the current working directory. As a
|
|
// special case, any relative path beginning with "." or ".." is treated as
|
|
// being relative to this document before it is made relative to the current
|
|
// working directory. The result is cleaned.
|
|
func (this *Document) Rel (name string) (string, error) {
|
|
if filepath.IsAbs(name) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil { return "", err }
|
|
return filepath.Rel(cwd, name)
|
|
}
|
|
if strings.HasPrefix(name, ".") {
|
|
directory := this.dir()
|
|
return filepath.Join(directory, name), nil
|
|
}
|
|
return name, nil
|
|
}
|
|
|
|
// Name returns the document's name, which is a path relative to its
|
|
// environment.
|
|
func (this *Document) Name () string {
|
|
return this.name
|
|
}
|
|
|
|
func (this *Document) dir () string {
|
|
directory := this.name
|
|
ext := filepath.Ext(directory)
|
|
if ext != "" {
|
|
directory = filepath.Dir(directory)
|
|
}
|
|
return directory
|
|
}
|
|
|
|
// ExecutionData is data made available to documents as they are being exeucted.
|
|
type ExecutionData struct {
|
|
Data any // Custom data
|
|
Child *ExecutionResult // The child document's result, if applicable
|
|
}
|
|
|
|
// ExecutionResult is the result of executing a document.
|
|
type ExecutionResult struct {
|
|
Author string
|
|
Title string
|
|
Extends string
|
|
|
|
Meta Meta
|
|
Body template.HTML
|
|
}
|