113 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
	
		
			3.1 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{lib, ...}: let
 | 
						|
  inherit (lib) toInt all min max;
 | 
						|
  inherit (builtins) mapAttrs elemAt isInt isFloat match getAttr hasAttr;
 | 
						|
 | 
						|
  # basic checks
 | 
						|
  range = a: b: v: (v <= max a b) && (v >= min a b);
 | 
						|
  number = v: isInt v || isFloat v;
 | 
						|
  unary = range 0.0 1.0;
 | 
						|
  hue = range 0.0 360.0;
 | 
						|
 | 
						|
  # type checking
 | 
						|
  hex = {
 | 
						|
    string = color: let
 | 
						|
      hexPatternWithHash = match "#[[:xdigit:]]{6}" color;
 | 
						|
      hexPatternNoHash = match "[[:xdigit:]]{6}" color;
 | 
						|
      isValid = hexPatternWithHash != null || hexPatternNoHash != null;
 | 
						|
    in
 | 
						|
      if !isValid
 | 
						|
      then abort "Invalid hex color format: ${color}"
 | 
						|
      else color;
 | 
						|
 | 
						|
    set = color: let
 | 
						|
      hasAttributes = all (k: hasAttr k color) ["r" "g" "b"];
 | 
						|
      validPattern = all (k: let
 | 
						|
        v = getAttr k color;
 | 
						|
      in
 | 
						|
        match "[[:xdigit:]]{2}" v != null) ["r" "g" "b"];
 | 
						|
    in
 | 
						|
      if !(hasAttributes && validPattern)
 | 
						|
      then abort "Invalid Hex values: r=${toString color.r}, g=${toString color.g}, b=${toString color.b}"
 | 
						|
      else color;
 | 
						|
  };
 | 
						|
 | 
						|
  rgb = {
 | 
						|
    string = color: let
 | 
						|
      rgbPattern = match "([0-9]{1,3}),([0-9]{1,3}),([0-9]{1,3})" color;
 | 
						|
      toNum = str: let
 | 
						|
        num = toInt str;
 | 
						|
      in
 | 
						|
        num != null && range 0 255 num;
 | 
						|
      isValid = rgbPattern != null && all toNum rgbPattern;
 | 
						|
    in
 | 
						|
      if !isValid
 | 
						|
      then abort "Invalid RGB string: ${color}"
 | 
						|
      else color;
 | 
						|
 | 
						|
    set = color: let
 | 
						|
      hasAttributes = all (k: hasAttr k color) ["r" "g" "b"];
 | 
						|
      validRanges = all (
 | 
						|
        k: let
 | 
						|
          v = getAttr k color;
 | 
						|
        in
 | 
						|
          number v && range 0 255 v
 | 
						|
      ) ["r" "g" "b"];
 | 
						|
    in
 | 
						|
      if !(hasAttributes && validRanges)
 | 
						|
      then abort "Invalid RGB set: r=${toString color.r}, g=${toString color.g}, b=${toString color.b}"
 | 
						|
      else color;
 | 
						|
  };
 | 
						|
  hsl = {
 | 
						|
    string = color: let
 | 
						|
      hslPattern = match "([0-9]{1,3}),[ ]*([0-9]{1,3})%,[ ]*([0-9]{1,3})%" color;
 | 
						|
      # Convert matched values to numbers and check ranges
 | 
						|
      validateHSL = groups: let
 | 
						|
        h = toInt (elemAt groups 0);
 | 
						|
        s = toInt (elemAt groups 1);
 | 
						|
        l = toInt (elemAt groups 2);
 | 
						|
      in
 | 
						|
        h
 | 
						|
        != null
 | 
						|
        && h >= 0
 | 
						|
        && h <= 360
 | 
						|
        && s != null
 | 
						|
        && s >= 0
 | 
						|
        && s <= 100
 | 
						|
        && l != null
 | 
						|
        && l >= 0
 | 
						|
        && l <= 100;
 | 
						|
      isValid = hslPattern != null && validateHSL hslPattern;
 | 
						|
    in
 | 
						|
      if !isValid
 | 
						|
      then abort "Invalid HSL string: ${color} (expected format: h(0-360),s(0-100%),l(0-100%))"
 | 
						|
      else color;
 | 
						|
 | 
						|
    set = color: let
 | 
						|
      hasAttributes = all (k: hasAttr k color) ["h" "s" "l"];
 | 
						|
      validRanges =
 | 
						|
        color.h
 | 
						|
        >= 0
 | 
						|
        && color.h <= 360
 | 
						|
        && color.s >= 0
 | 
						|
        && color.s <= 1.0
 | 
						|
        && color.l >= 0
 | 
						|
        && color.l <= 1.0;
 | 
						|
    in
 | 
						|
      if !(hasAttributes && validRanges)
 | 
						|
      then
 | 
						|
        abort ''
 | 
						|
          Invalid HSL set: h=${toString color.h}, s=${toString color.s}, l=${toString color.l}
 | 
						|
          Expected:
 | 
						|
            h: 0-360
 | 
						|
            s: 0.0-1.0
 | 
						|
            l: 0.0-1.0
 | 
						|
        ''
 | 
						|
      else color;
 | 
						|
  };
 | 
						|
 | 
						|
  # validate neutral hex values
 | 
						|
  neutrals = {neutrals, ...}:
 | 
						|
    mapAttrs (_: value:
 | 
						|
      hex.string value)
 | 
						|
    neutrals;
 | 
						|
in {inherit neutrals range number unary hue hex rgb hsl;}
 |