flamenco/internal/manager/config/variables.go
Sybren A. Stüvel 02fac6a4df Change Go package name from git.blender.org to projects.blender.org
Change the package base name of the Go code, from
`git.blender.org/flamenco` to `projects.blender.org/studio/flamenco`.

The old location, `git.blender.org`, has no longer been use since the
[migration to Gitea][1]. The new package names now reflect the actual
location where Flamenco is hosted.

[1]: https://code.blender.org/2023/02/new-blender-development-infrastructure/
2023-08-01 12:42:31 +02:00

156 lines
5.2 KiB
Go

package config
import (
"fmt"
"strings"
"github.com/rs/zerolog/log"
"projects.blender.org/studio/flamenco/pkg/crosspath"
)
type ValueToVariableReplacer struct {
twoWayVars map[string]string // Mapping from variable name to value.
}
// VariableExpander expands variables and applies two-way variable replacement to the values.
type VariableExpander struct {
oneWayVars map[string]string // Mapping from variable name to value.
managerTwoWayVars map[string]string // Mapping from variable name to value for the Manager platform.
targetTwoWayVars map[string]string // Mapping from variable name to value for the target platform.
targetPlatform VariablePlatform
}
// NewVariableToValueConverter returns a ValueToVariableReplacer for the given audience & platform.
func (c *Conf) NewVariableToValueConverter(audience VariableAudience, platform VariablePlatform) *ValueToVariableReplacer {
// Get the variables for the given audience & platform.
twoWayVars := c.GetTwoWayVariables(audience, platform)
if len(twoWayVars) == 0 {
log.Debug().
Str("audience", string(audience)).
Str("platform", string(platform)).
Msg("no two-way variables defined for this platform given this audience")
}
return &ValueToVariableReplacer{
twoWayVars: twoWayVars,
}
}
// NewVariableExpander returns a new VariableExpander for the given audience & platform.
func (c *Conf) NewVariableExpander(audience VariableAudience, platform VariablePlatform) *VariableExpander {
// Get the variables for the given audience & platform.
varsForPlatform := c.getVariables(audience, platform)
if len(varsForPlatform) == 0 {
log.Warn().
Str("audience", string(audience)).
Str("platform", string(platform)).
Msg("no variables defined for this platform given this audience")
}
return &VariableExpander{
oneWayVars: varsForPlatform,
managerTwoWayVars: c.GetTwoWayVariables(audience, c.currentGOOS),
targetTwoWayVars: c.GetTwoWayVariables(audience, platform),
targetPlatform: platform,
}
}
// ValueToVariableReplacer replaces any variable values it recognises in
// valueToConvert to the actual variable. For example, `/path/to/file.blend` can
// be changed to `{my_storage}/file.blend`.
func (vvc *ValueToVariableReplacer) Replace(valueToConvert string) string {
result := valueToConvert
for varName, varValue := range vvc.twoWayVars {
if !isValueMatch(result, varValue) {
continue
}
result = vvc.join(varName, result[len(varValue):])
}
log.Debug().
Str("from", valueToConvert).
Str("to", result).
Msg("first step of two-way variable replacement")
return result
}
func (vvc *ValueToVariableReplacer) join(varName, value string) string {
return fmt.Sprintf("{%s}%s", varName, value)
}
// isValueMatch returns whether `valueToMatch` starts with `variableValue`.
// When `variableValue` is a Windows path (with backslash separators), it is
// also tested with forward slashes against `valueToMatch`.
func isValueMatch(valueToMatch, variableValue string) bool {
if strings.HasPrefix(valueToMatch, variableValue) {
return true
}
// If the variable value has a backslash, assume it is a Windows path.
// Convert it to slash notation just to see if that would provide a
// match.
if strings.ContainsRune(variableValue, '\\') {
slashedValue := crosspath.ToSlash(variableValue)
return strings.HasPrefix(valueToMatch, slashedValue)
}
return false
}
// Replace converts "{variable name}" to the value that belongs to the audience and platform.
func (ve *VariableExpander) Expand(valueToExpand string) string {
expanded := valueToExpand
// Expand variables from {varname} to their value for the target platform.
for varname, varvalue := range ve.oneWayVars {
placeholder := fmt.Sprintf("{%s}", varname)
expanded = strings.Replace(expanded, placeholder, varvalue, -1)
}
// Go through the two-way variables, to make sure that the result of
// expanding variables gets the two-way variables applied as well. This is
// necessary to make implicitly-defined variable, which are only defined for
// the Manager's platform, usable for the target platform.
//
// Practically, this replaces "value for the Manager platform" with "value
// for the target platform".
isPathValue := false
for varname, managerValue := range ve.managerTwoWayVars {
targetValue, ok := ve.targetTwoWayVars[varname]
if !ok {
continue
}
if !isValueMatch(expanded, managerValue) {
continue
}
expanded = ve.join(targetValue, expanded[len(managerValue):], ve.targetPlatform)
// Since two-way variables are meant for path replacement, we know this
// should be a path.
isPathValue = true
}
if isPathValue {
expanded = crosspath.ToPlatform(expanded, string(ve.targetPlatform))
}
return expanded
}
func (ve *VariableExpander) join(valueFromVariable, suffix string, platform VariablePlatform) string {
result := valueFromVariable + suffix
if platform == VariablePlatformWindows {
// 'result' may now be of the form `F:some\path\to\file`, where `F:` comes
// from `valueFromVariable` and the rest is the suffix. This is not an
// absolute path, and needs a separator between the drive letter and the
// rest of the path.
return crosspath.EnsureDriveAbsolute(result)
}
return result
}