mirror of
https://github.com/glanceapp/glance.git
synced 2024-12-26 00:49:32 +01:00
153 lines
2.8 KiB
Go
153 lines
2.8 KiB
Go
|
package widget
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"html/template"
|
||
|
"os"
|
||
|
"regexp"
|
||
|
"strconv"
|
||
|
"time"
|
||
|
|
||
|
"gopkg.in/yaml.v3"
|
||
|
)
|
||
|
|
||
|
var HSLColorPattern = regexp.MustCompile(`^(?:hsla?\()?(\d{1,3})(?: |,)+(\d{1,3})%?(?: |,)+(\d{1,3})%?\)?$`)
|
||
|
var EnvFieldPattern = regexp.MustCompile(`^\${([A-Z_]+)}$`)
|
||
|
|
||
|
const (
|
||
|
HSLHueMax = 360
|
||
|
HSLSaturationMax = 100
|
||
|
HSLLightnessMax = 100
|
||
|
)
|
||
|
|
||
|
type HSLColorField struct {
|
||
|
Hue uint16
|
||
|
Saturation uint8
|
||
|
Lightness uint8
|
||
|
}
|
||
|
|
||
|
func (c *HSLColorField) String() string {
|
||
|
return fmt.Sprintf("hsl(%d, %d%%, %d%%)", c.Hue, c.Saturation, c.Lightness)
|
||
|
}
|
||
|
|
||
|
func (c *HSLColorField) AsCSSValue() template.CSS {
|
||
|
return template.CSS(c.String())
|
||
|
}
|
||
|
|
||
|
func (c *HSLColorField) UnmarshalYAML(node *yaml.Node) error {
|
||
|
var value string
|
||
|
|
||
|
if err := node.Decode(&value); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
matches := HSLColorPattern.FindStringSubmatch(value)
|
||
|
|
||
|
if len(matches) != 4 {
|
||
|
return fmt.Errorf("invalid HSL color format: %s", value)
|
||
|
}
|
||
|
|
||
|
hue, err := strconv.ParseUint(matches[1], 10, 16)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if hue > HSLHueMax {
|
||
|
return fmt.Errorf("HSL hue must be between 0 and %d", HSLHueMax)
|
||
|
}
|
||
|
|
||
|
saturation, err := strconv.ParseUint(matches[2], 10, 8)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if saturation > HSLSaturationMax {
|
||
|
return fmt.Errorf("HSL saturation must be between 0 and %d", HSLSaturationMax)
|
||
|
}
|
||
|
|
||
|
lightness, err := strconv.ParseUint(matches[3], 10, 8)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if lightness > HSLLightnessMax {
|
||
|
return fmt.Errorf("HSL lightness must be between 0 and %d", HSLLightnessMax)
|
||
|
}
|
||
|
|
||
|
c.Hue = uint16(hue)
|
||
|
c.Saturation = uint8(saturation)
|
||
|
c.Lightness = uint8(lightness)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var DurationPattern = regexp.MustCompile(`^(\d+)(s|m|h|d)$`)
|
||
|
|
||
|
type DurationField time.Duration
|
||
|
|
||
|
func (d *DurationField) UnmarshalYAML(node *yaml.Node) error {
|
||
|
var value string
|
||
|
|
||
|
if err := node.Decode(&value); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
matches := DurationPattern.FindStringSubmatch(value)
|
||
|
|
||
|
if len(matches) != 3 {
|
||
|
return fmt.Errorf("invalid duration format: %s", value)
|
||
|
}
|
||
|
|
||
|
duration, err := strconv.Atoi(matches[1])
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
switch matches[2] {
|
||
|
case "s":
|
||
|
*d = DurationField(time.Duration(duration) * time.Second)
|
||
|
case "m":
|
||
|
*d = DurationField(time.Duration(duration) * time.Minute)
|
||
|
case "h":
|
||
|
*d = DurationField(time.Duration(duration) * time.Hour)
|
||
|
case "d":
|
||
|
*d = DurationField(time.Duration(duration) * 24 * time.Hour)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type OptionalEnvString string
|
||
|
|
||
|
func (f *OptionalEnvString) UnmarshalYAML(node *yaml.Node) error {
|
||
|
var value string
|
||
|
|
||
|
err := node.Decode(&value)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
matches := EnvFieldPattern.FindStringSubmatch(value)
|
||
|
|
||
|
if len(matches) != 2 {
|
||
|
*f = OptionalEnvString(value)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
value, found := os.LookupEnv(matches[1])
|
||
|
|
||
|
if !found {
|
||
|
return fmt.Errorf("environment variable %s not found", matches[1])
|
||
|
}
|
||
|
|
||
|
*f = OptionalEnvString(value)
|
||
|
|
||
|
return nil
|
||
|
}
|