mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
119 lines
3.0 KiB
Go
119 lines
3.0 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package label
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/modules/options"
|
|
|
|
"go.yaml.in/yaml/v4"
|
|
)
|
|
|
|
type labelFile struct {
|
|
Labels []*Label `yaml:"labels"`
|
|
}
|
|
|
|
// ErrTemplateLoad represents a "ErrTemplateLoad" kind of error.
|
|
type ErrTemplateLoad struct {
|
|
TemplateFile string
|
|
OriginalError error
|
|
}
|
|
|
|
// IsErrTemplateLoad checks if an error is a ErrTemplateLoad.
|
|
func IsErrTemplateLoad(err error) bool {
|
|
_, ok := err.(ErrTemplateLoad)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrTemplateLoad) Error() string {
|
|
return fmt.Sprintf("failed to load label template file %q: %v", err.TemplateFile, err.OriginalError)
|
|
}
|
|
|
|
// LoadTemplateFile loads the label template file by given file name, returns a slice of Label structs.
|
|
func LoadTemplateFile(fileName string) ([]*Label, error) {
|
|
data, err := options.Labels(fileName)
|
|
if err != nil {
|
|
return nil, ErrTemplateLoad{fileName, fmt.Errorf("LoadTemplateFile: %w", err)}
|
|
}
|
|
|
|
if strings.HasSuffix(fileName, ".yaml") || strings.HasSuffix(fileName, ".yml") {
|
|
return parseYamlFormat(fileName, data)
|
|
}
|
|
return parseLegacyFormat(fileName, data)
|
|
}
|
|
|
|
func parseYamlFormat(fileName string, data []byte) ([]*Label, error) {
|
|
lf := &labelFile{}
|
|
|
|
if err := yaml.Unmarshal(data, lf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Validate label data and fix colors
|
|
for _, l := range lf.Labels {
|
|
l.Color = strings.TrimSpace(l.Color)
|
|
if len(l.Name) == 0 || len(l.Color) == 0 {
|
|
return nil, ErrTemplateLoad{fileName, errors.New("label name and color are required fields")}
|
|
}
|
|
color, err := NormalizeColor(l.Color)
|
|
if err != nil {
|
|
return nil, ErrTemplateLoad{fileName, fmt.Errorf("bad HTML color code '%s' in label: %s", l.Color, l.Name)}
|
|
}
|
|
l.Color = color
|
|
}
|
|
|
|
return lf.Labels, nil
|
|
}
|
|
|
|
func parseLegacyFormat(fileName string, data []byte) ([]*Label, error) {
|
|
lines := strings.Split(string(data), "\n")
|
|
list := make([]*Label, 0, len(lines))
|
|
for i := range lines {
|
|
line := strings.TrimSpace(lines[i])
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
|
|
parts, description, _ := strings.Cut(line, ";")
|
|
|
|
color, labelName, ok := strings.Cut(parts, " ")
|
|
if !ok {
|
|
return nil, ErrTemplateLoad{fileName, fmt.Errorf("line is malformed: %s", line)}
|
|
}
|
|
|
|
color, err := NormalizeColor(color)
|
|
if err != nil {
|
|
return nil, ErrTemplateLoad{fileName, fmt.Errorf("bad HTML color code '%s' in line: %s", color, line)}
|
|
}
|
|
|
|
list = append(list, &Label{
|
|
Name: strings.TrimSpace(labelName),
|
|
Color: color,
|
|
Description: strings.TrimSpace(description),
|
|
})
|
|
}
|
|
|
|
return list, nil
|
|
}
|
|
|
|
// LoadTemplateDescription loads the labels from a template file, returns a description string by joining each Label.Name with comma
|
|
func LoadTemplateDescription(fileName string) (string, error) {
|
|
var buf strings.Builder
|
|
list, err := LoadTemplateFile(fileName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for i := range list {
|
|
if i > 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(list[i].Name)
|
|
}
|
|
return buf.String(), nil
|
|
}
|