Repository: Orange-OpenSource/hurl
File: packages/hurl/src/runner/predicate.rs (Rust)

Code before:
                expected: expected_display,
                type_mismatch: false,
            })
        }
        _ => Ok(AssertResult {
            success: false,
            actual: actual.repr(),
            expected: expected_display,
            type_mismatch: true,
        }),
    }
}

/// Evaluates if an `actual` value is a number.
fn eval_is_number(actual: &Value) -> Result<AssertResult, RunnerError> {
    Ok(AssertResult {
        success: actual.is_number(),
        actual: actual.repr(),
        expected: "number".to_string(),
        type_mismatch: false,
    })
}

/// Evaluates if an `actual` value is an IPv4 address.
fn eval_is_ipv4(actual: &Value) -> Result<AssertResult, RunnerError> {
    let ip = actual.to_string();
    let parsed = match IpAddr::from_str(&ip) {
        Ok(ip) => ip,
        Err(_) => {
            return Ok(AssertResult {
                success: false,
                actual: actual.repr(),
                expected: "ipv4".to_string(),
                type_mismatch: true,
            })
        }
    };

    Ok(AssertResult {
        success: parsed.is_ipv4(),
        actual: actual.repr(),
        expected: "ipv4".to_string(),
        type_mismatch: false,
    })
}

/// Evaluates if an `actual` value is an IPv6 address.
fn eval_is_ipv6(actual: &Value) -> Result<AssertResult, RunnerError> {
    let ip = actual.to_string();
    let parsed = match IpAddr::from_str(&ip) {
        Ok(ip) => ip,

Review comment (bug):
We need to change some things. You can take inspiration from `predicate::eval_is_iso_date`. The aim of this code is to be able to provide good error message. In particular, if a `isIpv4` predicate fails, we want to be able to indicate to the user that the predicate is failing because of bad type between the query and the predicate, or because of bad value. This is the difference between:

```
GET https://foo.com
HTTP 200
[Asserts]
status isIpv4
```

=> error because a number (`status`) can't be compared to an IPv4 address.

And

```
GET https://foo.com
HTTP 200
[Asserts]
status toString isIpv4
```

=> error because the string "200" does not match an IPv4 address.

So, we can do :

```rust
fn eval_is_ipv4(actual: &Value) -> Result<AssertResult, RunnerError> {
    match actual.is_ipv4() {
        Ok(success) => Ok(AssertResult {
            success,
            actual: actual.to_string(),
            expected: "string with format IPv4".to_string(),
            type_mismatch: false,
        }),
        _ => Ok(AssertResult {
            success: false,
            actual: actual.repr(),
            expected: "string".to_string(),
            type_mismatch: true,
        }),
    }
}
```

`is_ipv4` is a method on `Value` implemented in `value_impl.rs`, that could be something like (I don't know if this code snippet is compiling):


```rust
    pub fn is_ipv4(&self) -> Result<bool, EvalError> {
        match self {
            Value::String(value) => {
            	let is_ipv4 = IpAddr::from_str(&value).map_or(false, |ip| ip.is_ipv4())
            	Ok(is_ipv4)
            },
            _ => Err(EvalError::Type),
        }
    }
```

It can seem a little complicated but the aim is to provide good error message.

Code after:
                expected: expected_display,
                type_mismatch: false,
            })
        }
        _ => Ok(AssertResult {
            success: false,
            actual: actual.repr(),
            expected: expected_display,
            type_mismatch: true,
        }),
    }
}

/// Evaluates if an `actual` value is a number.
fn eval_is_number(actual: &Value) -> Result<AssertResult, RunnerError> {
    Ok(AssertResult {
        success: actual.is_number(),
        actual: actual.repr(),
        expected: "number".to_string(),
        type_mismatch: false,
    })
}

/// Evaluates if an `actual` value is an IPv4 address.
fn eval_is_ipv4(actual: &Value) -> Result<AssertResult, RunnerError> {
    match actual.is_ipv4() {
        Ok(success) => Ok(AssertResult {
            success,
            actual: actual.to_string(),
            expected: "string in IPv4 format".to_string(),
            type_mismatch: false,
        }),
        _ => Ok(AssertResult {
            success: false,
            actual: actual.repr(),
            expected: "string".to_string(),
            type_mismatch: true,
        }),
    }
}

/// Evaluates if an `actual` value is an IPv6 address.
fn eval_is_ipv6(actual: &Value) -> Result<AssertResult, RunnerError> {
    match actual.is_ipv6() {
        Ok(success) => Ok(AssertResult {
            success,
            actual: actual.to_string(),
            expected: "string in IPv6 format".to_string(),
            type_mismatch: false,
        }),
        _ => Ok(AssertResult {