# ImageKit Custom Error Types: ParseColorError and ParseWatermarkPositionError Explained

> Understand ImageKit's custom error types ParseColorError and ParseWatermarkPositionError. Learn how they provide clear messages for invalid color codes and watermark positions.

- Repository: [hzbd/imagekit](https://github.com/hzbd/imagekit)
- Tags: deep-dive
- Published: 2026-03-03

---

**ImageKit defines two custom error types, `ParseColorError` and `ParseWatermarkPositionError`, in [`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs) to handle invalid hexadecimal color codes and watermark placement strings with precise, user-friendly error messages.**

The `hzbd/imagekit` Rust crate provides CLI tools for image processing that require strict input validation for parameters like overlay colors and watermark positions. To surface clear validation failures without cluttering the business logic, the codebase implements custom error types using the **thiserror** crate, enabling ergonomic error propagation throughout the application.

## Custom Error Types Defined in ImageKit

Located in [[`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs)](https://github.com/hzbd/imagekit/blob/master/src/errors.rs), ImageKit's custom error types are thin wrapper structs around `String` values that leverage procedural macros for automatic trait implementation.

### ParseWatermarkPositionError

The `ParseWatermarkPositionError` type validates watermark placement arguments against a strict whitelist of positional keywords. According to the source code at lines 5‑6, it rejects any string that does not match the valid options: `nw`, `north`, `ne`, `west`, `center`, `east`, `sw`, `south`, or `se`.

```rust
#[derive(Debug, Error)]
#[error("Invalid watermark position: '{0}'. Valid options are: nw, north, ne, west, center, east, sw, south, se")]
pub struct ParseWatermarkPositionError(pub String);

```

### ParseColorError

The `ParseColorError` type enforces hexadecimal color code formatting rules as implemented at lines 9‑10 of [`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs). It validates that color strings contain exactly 6 or 8 hexadecimal digits, optionally prefixed with a hash symbol.

```rust
#[derive(Debug, Error)]
#[error("Invalid hex color code: '{0}'. Must be in RRGGBB or RRGGBBAA format.")]
pub struct ParseColorError(pub String);

```

Both structs derive `Debug` and `Error` via **thiserror**, which automatically implements `std::error::Error` and generates the display format specified in the `#[error(...)]` attribute.

## How ImageKit Implements Input Validation with Custom Errors

The custom error types integrate directly into the CLI argument parsing logic in [[`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs)](https://github.com/hzbd/imagekit/blob/master/src/cli.rs), specifically within `FromStr` trait implementations.

### HexColor Parsing

Lines 56‑62 implement `FromStr` for the `HexColor` struct, returning `ParseColorError` when input strings fail length validation or hexadecimal conversion:

```rust
// Conceptual implementation based on src/cli.rs lines 56-62
impl FromStr for HexColor {
    type Err = ParseColorError;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // Validation logic that returns ParseColorError on failure
        // ...
    }
}

```

### WatermarkPosition Parsing

Similarly, lines 84‑89 handle `WatermarkPosition` parsing, mapping unknown keywords to `ParseWatermarkPositionError`:

```rust
// Conceptual implementation based on src/cli.rs lines 84-89
impl FromStr for WatermarkPosition {
    type Err = ParseWatermarkPositionError;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // Match against valid positions or return ParseWatermarkPositionError
        // ...
    }
}

```

This architecture isolates validation concerns into dedicated types, allowing functions to propagate `Result<_, Parse*Error>` without manual error message construction.

## Practical Examples of Handling ImageKit Custom Errors

When building tools atop the imagekit crate, you can leverage these error types to provide precise feedback to end-users.

### Validating Hex Color Input

This example demonstrates catching `ParseColorError` when parsing CLI arguments:

```rust
use imagekit::cli::HexColor;
use std::str::FromStr;

fn parse_user_color(input: &str) {
    match HexColor::from_str(input) {
        Ok(color) => println!("Parsed RGBA: {:?}", color.0),
        Err(e) => eprintln!("Error: {}", e), 
        // Output: "Invalid hex color code: 'zzzzzz'. Must be in RRGGBB or RRGGBBAA format."
    }
}

// Usage
parse_user_color("#ff00ff");  // Success
parse_user_color("12345");    // Fails - too short
parse_user_color("gggggg");   // Fails - invalid hex

```

### Validating Watermark Position

The following pattern handles invalid placement strings via `ParseWatermarkPositionError`:

```rust
use imagekit::cli::WatermarkPosition;
use std::str::FromStr;

fn set_watermark(position_arg: &str) {
    match WatermarkPosition::from_str(position_arg) {
        Ok(pos) => println!("Placing watermark at: {}", pos),
        Err(e) => eprintln!("Configuration error: {}", e),
        // Output: "Invalid watermark position: 'middle'. Valid options are: nw, north, ne..."
    }
}

// Usage
set_watermark("se");      // Success - southeast
set_watermark("center");  // Success
set_watermark("middle");  // Fails - invalid keyword

```

## Summary

ImageKit's custom error types provide a robust mechanism for input validation in its CLI tooling:

- **`ParseColorError`** in [`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs) validates hexadecimal color strings against RRGGBB or RRGGBBAA formats
- **`ParseWatermarkPositionError`** enforces strict watermark placement keywords across nine valid positions
- Both types use the **thiserror** crate to derive `std::error::Error` with formatted error messages
- The errors integrate with `FromStr` implementations in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs) (lines 56‑62 and 84‑89) for ergonomic parsing
- This pattern enables precise error propagation without scattering validation logic throughout the codebase

## Frequently Asked Questions

### How does ImageKit validate hexadecimal color codes?

ImageKit validates hex colors through the `ParseColorError` type defined in [`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs). The validation requires strings to contain exactly 6 or 8 hexadecimal digits, optionally prefixed with `#`. When `HexColor::from_str()` encounters malformed input in [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs) (lines 56‑62), it returns this error type with a message specifying the invalid input and required format.

### What watermark positions are valid in ImageKit?

According to the `ParseWatermarkPositionError` definition at lines 5‑6 of [`src/errors.rs`](https://github.com/hzbd/imagekit/blob/main/src/errors.rs), ImageKit accepts nine specific keywords for watermark placement: `nw`, `north`, `ne`, `west`, `center`, `east`, `sw`, `south`, and `se`. Any other string passed to `WatermarkPosition::from_str()` (implemented at lines 84‑89 of [`src/cli.rs`](https://github.com/hzbd/imagekit/blob/main/src/cli.rs)) triggers the custom error with a complete list of valid options.

### Why does ImageKit use the thiserror crate for custom errors?

The **thiserror** crate reduces boilerplate by automatically deriving the `std::error::Error` trait and implementing `Display` based on the `#[error(...)]` attribute. For ImageKit's `ParseColorError` and `ParseWatermarkPositionError`, this eliminates manual error message formatting code while preserving the ability to wrap the original invalid input string for debugging purposes.

### Can I use ImageKit's error types in my own Rust application?

Yes, if you depend on the `imagekit` crate, you can import `ParseColorError` and `ParseWatermarkPositionError` from the library to handle validation in your own CLI tools or image processing pipelines. These types implement `std::error::Error`, making them compatible with Rust's ecosystem error handling patterns like the `?` operator and error conversion traits.