import { define, object, string, number } from 'decoders';
import type { Decoder } from 'decoders';
interface Color {
r: number;
g: number;
b: number;
}
// Decoder that accepts hex color strings OR RGB objects
const color: Decoder<Color> = define<Color>((blob, ok, err) => {
// Try parsing as hex string
if (typeof blob === 'string') {
const hex = blob.replace(/^#/, '');
if (!/^[0-9a-f]{6}$/i.test(hex)) {
return err('Invalid hex color format');
}
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
return ok({ r, g, b });
}
// Try parsing as RGB object
const rgbDecoder = object({
r: number,
g: number,
b: number,
});
const result = rgbDecoder.decode(blob);
if (!result.ok) {
return err('Must be hex color string or RGB object');
}
const { r, g, b } = result.value;
// Validate range
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
return err('RGB values must be between 0 and 255');
}
return ok({ r, g, b });
});
color.verify('#ff6600'); // { r: 255, g: 102, b: 0 }
color.verify({ r: 255, g: 102, b: 0 }); // { r: 255, g: 102, b: 0 }
color.verify('#xyz'); // Error: Invalid hex color format
color.verify({ r: 300, g: 0, b: 0 }); // Error: RGB values must be between 0 and 255