ECL, default scale, and preferred alignment entries
This commit is contained in:
@@ -29,6 +29,7 @@ type nativeDataset struct {
|
||||
Spec datasetTextSpec
|
||||
Columns []string
|
||||
ValueEncodings map[string]project.TopDataValueEncodingConfig
|
||||
ValueDefaults map[string]any
|
||||
CompareReference bool
|
||||
}
|
||||
|
||||
@@ -238,6 +239,7 @@ func buildNativeUnchecked(p *project.Project, opts NativeBuildOptions, progress
|
||||
return BuildResult{}, err
|
||||
}
|
||||
datasets = applyTopDataValueEncodings(datasets, p.Config.TopData.ValueEncodings)
|
||||
datasets = applyTopDataValueDefaults(datasets, p.Config.TopData.ValueDefaults)
|
||||
registryDatasets, err := collectGeneratedRegistryDatasets(dataDir)
|
||||
if err != nil {
|
||||
return BuildResult{}, err
|
||||
@@ -427,6 +429,25 @@ func applyTopDataValueEncodings(datasets []nativeDataset, encodings []project.To
|
||||
return out
|
||||
}
|
||||
|
||||
func applyTopDataValueDefaults(datasets []nativeDataset, defaults []project.TopDataValueDefaultConfig) []nativeDataset {
|
||||
if len(defaults) == 0 {
|
||||
return datasets
|
||||
}
|
||||
out := append([]nativeDataset(nil), datasets...)
|
||||
for index := range out {
|
||||
for _, valueDefault := range defaults {
|
||||
if filepath.ToSlash(strings.TrimSpace(valueDefault.Dataset)) != out[index].Name {
|
||||
continue
|
||||
}
|
||||
if out[index].ValueDefaults == nil {
|
||||
out[index].ValueDefaults = map[string]any{}
|
||||
}
|
||||
out[index].ValueDefaults[strings.ToLower(strings.TrimSpace(valueDefault.Column))] = valueDefault.Value
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func nativeCompileGroup(datasetName string) string {
|
||||
head, _, ok := strings.Cut(datasetName, "/")
|
||||
if !ok {
|
||||
@@ -3682,6 +3703,17 @@ func (r *valueResolver) resolveField(row map[string]any, field string, stack []s
|
||||
}
|
||||
|
||||
value, ok := lookupField(row, field)
|
||||
if !ok {
|
||||
if defaultValue, hasDefault := r.valueDefaultForField(field); hasDefault {
|
||||
value = defaultValue
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if ok && isNullLike(value) {
|
||||
if defaultValue, hasDefault := r.valueDefaultForField(field); hasDefault {
|
||||
value = defaultValue
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
resolved := tlkCompiledValue{Value: nullValue}
|
||||
r.cache[rowKey][field] = resolved
|
||||
@@ -3712,6 +3744,13 @@ func (r *valueResolver) resolveValue(row map[string]any, field string, value any
|
||||
} else if ok {
|
||||
return r.resolveTLKPayload(row, field, payload, stack)
|
||||
}
|
||||
if encoding, ok := r.valueEncodingForField(field); ok && strings.TrimSpace(encoding.Mode) == "alignment_hex_list" {
|
||||
encoded, err := encodeConfiguredAlignmentHexList(value, encoding)
|
||||
if err != nil {
|
||||
return tlkCompiledValue{}, fmt.Errorf("field %s: %w", field, err)
|
||||
}
|
||||
return tlkCompiledValue{Value: encoded}, nil
|
||||
}
|
||||
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
@@ -3790,6 +3829,14 @@ func (r *valueResolver) valueEncodingForField(field string) (project.TopDataValu
|
||||
return encoding, ok
|
||||
}
|
||||
|
||||
func (r *valueResolver) valueDefaultForField(field string) (any, bool) {
|
||||
if len(r.dataset.ValueDefaults) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value, ok := r.dataset.ValueDefaults[strings.ToLower(strings.TrimSpace(field))]
|
||||
return value, ok
|
||||
}
|
||||
|
||||
func encodeConfiguredValueList(obj map[string]any, encoding project.TopDataValueEncodingConfig) (string, error) {
|
||||
if strings.TrimSpace(encoding.Mode) != "packed_hex_list" {
|
||||
return "", fmt.Errorf("unsupported value encoding mode %q", encoding.Mode)
|
||||
@@ -3826,6 +3873,136 @@ func encodeConfiguredValueList(obj map[string]any, encoding project.TopDataValue
|
||||
return formatConfiguredPackedHexList(values, encoding), nil
|
||||
}
|
||||
|
||||
func encodeConfiguredAlignmentHexList(raw any, encoding project.TopDataValueEncodingConfig) (string, error) {
|
||||
values, err := expandConfiguredAlignmentList(raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return formatConfiguredPackedHexList(values, encoding), nil
|
||||
}
|
||||
|
||||
func expandConfiguredAlignmentList(raw any) ([]int, error) {
|
||||
if isNullLike(raw) {
|
||||
return []int{0}, nil
|
||||
}
|
||||
switch typed := raw.(type) {
|
||||
case map[string]any:
|
||||
for key := range typed {
|
||||
if key != "list" {
|
||||
return nil, fmt.Errorf("alignment list object contains unsupported key %q", key)
|
||||
}
|
||||
}
|
||||
return expandConfiguredAlignmentList(typed["list"])
|
||||
case []any:
|
||||
values := make([]int, 0, len(typed))
|
||||
for _, item := range typed {
|
||||
value, err := parseConfiguredAlignmentValue(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
if len(values) == 0 {
|
||||
return []int{0}, nil
|
||||
}
|
||||
return values, nil
|
||||
case string:
|
||||
trimmed := strings.TrimSpace(typed)
|
||||
if trimmed == "" {
|
||||
return []int{0}, nil
|
||||
}
|
||||
if strings.Contains(trimmed, ",") {
|
||||
parts := strings.Split(trimmed, ",")
|
||||
values := make([]int, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
value, err := parseConfiguredAlignmentValue(part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, value)
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
value, err := parseConfiguredAlignmentValue(trimmed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []int{value}, nil
|
||||
default:
|
||||
value, err := parseConfiguredAlignmentValue(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []int{value}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfiguredAlignmentValue(raw any) (int, error) {
|
||||
switch typed := raw.(type) {
|
||||
case int:
|
||||
return validateConfiguredAlignmentNumeric(typed)
|
||||
case float64:
|
||||
if typed != float64(int(typed)) {
|
||||
return 0, fmt.Errorf("alignment value %v must be an integer", typed)
|
||||
}
|
||||
return validateConfiguredAlignmentNumeric(int(typed))
|
||||
case string:
|
||||
trimmed := strings.TrimSpace(typed)
|
||||
if trimmed == "" || trimmed == "0" || strings.EqualFold(trimmed, "0x00") {
|
||||
return 0, nil
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(trimmed), "0x") {
|
||||
parsed, err := strconv.ParseInt(trimmed[2:], 16, 0)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("alignment value %q must be a two-letter code or hex value", typed)
|
||||
}
|
||||
return validateConfiguredAlignmentNumeric(int(parsed))
|
||||
}
|
||||
code := strings.ToUpper(trimmed)
|
||||
if code == "TN" {
|
||||
return 0x01, nil
|
||||
}
|
||||
if len(code) != 2 {
|
||||
return 0, fmt.Errorf("alignment code %q must use two letters", typed)
|
||||
}
|
||||
first, ok := configuredAlignmentAxisValue(code[0], true)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("alignment code %q has invalid law-chaos letter %q", typed, string(code[0]))
|
||||
}
|
||||
second, ok := configuredAlignmentAxisValue(code[1], false)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("alignment code %q has invalid good-evil letter %q", typed, string(code[1]))
|
||||
}
|
||||
return first | second, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("alignment value %v must be a two-letter code or integer", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func validateConfiguredAlignmentNumeric(value int) (int, error) {
|
||||
if value < 0 || value > 31 {
|
||||
return 0, fmt.Errorf("alignment value %d outside range 0-31", value)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func configuredAlignmentAxisValue(letter byte, lawChaos bool) (int, bool) {
|
||||
switch letter {
|
||||
case 'N':
|
||||
return 0x01, true
|
||||
case 'L':
|
||||
return 0x02, lawChaos
|
||||
case 'C':
|
||||
return 0x04, lawChaos
|
||||
case 'G':
|
||||
return 0x08, !lawChaos
|
||||
case 'E':
|
||||
return 0x10, !lawChaos
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func formatConfiguredPackedHexList(values []int, encoding project.TopDataValueEncodingConfig) string {
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user