106 lines
2.5 KiB
Go
106 lines
2.5 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"src.lwithers.me.uk/go/htpack/cmd/htpacker/packer"
|
||
|
)
|
||
|
|
||
|
type ctGlobEntry struct {
|
||
|
pattern, contentType string
|
||
|
pathComponents int
|
||
|
}
|
||
|
|
||
|
type ctGlobList []ctGlobEntry
|
||
|
|
||
|
func parseGlobs(flags []string) (ctGlobList, error) {
|
||
|
var ctGlobs ctGlobList
|
||
|
for _, flag := range flags {
|
||
|
// split pattern:content-type
|
||
|
pos := strings.LastIndexByte(flag, ':')
|
||
|
if pos == -1 {
|
||
|
return nil, &parseGlobError{
|
||
|
Value: flag,
|
||
|
Err: "must be pattern:content-type",
|
||
|
}
|
||
|
}
|
||
|
pattern, ct := flag[:pos], flag[pos+1:]
|
||
|
|
||
|
// patterns starting with "/" must match the entire directory
|
||
|
// prefix; otherwise, an arbitrary number of path components are
|
||
|
// allowed prior to the prefix
|
||
|
var pathComponents int
|
||
|
if strings.HasPrefix(pattern, "/") {
|
||
|
pathComponents = -1
|
||
|
pattern = strings.TrimPrefix(pattern, "/")
|
||
|
} else {
|
||
|
pathComponents = 1 + strings.Count(pattern, "/")
|
||
|
}
|
||
|
|
||
|
// test that the pattern's syntax is valid
|
||
|
if _, err := filepath.Match(pattern, "test"); err != nil {
|
||
|
return nil, &parseGlobError{
|
||
|
Value: flag,
|
||
|
Err: err.Error(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ctGlobs = append(ctGlobs, ctGlobEntry{
|
||
|
pattern: pattern,
|
||
|
contentType: ct,
|
||
|
pathComponents: pathComponents,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return ctGlobs, nil
|
||
|
}
|
||
|
|
||
|
// ApplyContentTypes will scan the list of files to pack, matching by filename,
|
||
|
// and on match will apply the given content type.
|
||
|
func (ctGlobs ctGlobList) ApplyContentTypes(ftp packer.FilesToPack) {
|
||
|
for name := range ftp {
|
||
|
for _, entry := range ctGlobs {
|
||
|
testName := trimPathComponents(name, entry.pathComponents)
|
||
|
matched, _ := filepath.Match(entry.pattern, testName)
|
||
|
if matched {
|
||
|
f := ftp[name]
|
||
|
f.ContentType = entry.contentType
|
||
|
ftp[name] = f
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func trimPathComponents(name string, components int) string {
|
||
|
name = strings.TrimPrefix(name, "/") // FilesToPack keys = absolute path
|
||
|
|
||
|
// if we are matching the full prefix, don't otherwise manipulate the
|
||
|
// name
|
||
|
if components < 0 {
|
||
|
return name
|
||
|
}
|
||
|
|
||
|
// otherwise, trim the number of components remaining in the path so
|
||
|
// that we are only matching the trailing path components from the
|
||
|
// FilesToPack key
|
||
|
parts := 1 + strings.Count(name, "/")
|
||
|
for ; parts > components; parts-- {
|
||
|
pos := strings.IndexByte(name, '/')
|
||
|
name = name[pos+1:]
|
||
|
}
|
||
|
return name
|
||
|
}
|
||
|
|
||
|
// parseGlobError is returned from parseGlobs on error.
|
||
|
type parseGlobError struct {
|
||
|
Value string
|
||
|
Err string
|
||
|
}
|
||
|
|
||
|
func (pge *parseGlobError) Error() string {
|
||
|
return fmt.Sprintf("--content-type entry %q: %s", pge.Value, pge.Err)
|
||
|
}
|