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

Code before:
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
use hurl_core::ast::SourceInfo;

use crate::runner::{RunnerError, RunnerErrorKind, Value};

/// Returns the element from a collection `value` at a zero-based index.
pub fn eval_nth(
    value: &Value,
    source_info: SourceInfo,
    assert: bool,
    n: i64,
) -> Result<Option<Value>, RunnerError> {
    match value {
        Value::List(values) => {
            let index: usize = if n >= 0 {
                n as usize
            } else {
                let size = values.len() as i64;
                (size + n) as usize
            };
            match values.get(index) {
                None => {
                    let kind = RunnerErrorKind::FilterInvalidInput(format!(
                        "Out of bound - size is {}",
                        values.len()
                    ));
                    Err(RunnerError::new(source_info, kind, assert))
                }
                Some(value) => Ok(Some(value.clone())),
            }
        }
        v => {
            let kind = RunnerErrorKind::FilterInvalidInput(v.repr());
            Err(RunnerError::new(source_info, kind, assert))
        }
    }
}

#[cfg(test)]
mod tests {
    use hurl_core::ast::{Filter, FilterValue, SourceInfo, Whitespace, I64};
    use hurl_core::reader::Pos;
    use hurl_core::typing::ToSource;


Review comment (suggestion):
Something that I don't like is if n is invalid (-5 for instance for a list of 3), `(size+n) as usize`is not valid. I would prefer something like this where we never compute invalid value:

```rust
    match value {
        Value::List(values) => {
            let len = values.len() as i64;
            let value = if 0 >= n && n < len {
                &values[n as usize]
            } else if len - n.abs() >= 0 {
                &values[(len - n.abs()) as usize]
            } else {
                let kind =
                    RunnerErrorKind::FilterInvalidInput(format!("Out of bound - size is {len}"));
                return Err(RunnerError::new(source_info, kind, assert));
            };
            Ok(Some(value.clone()))
        }
        v => {
            let kind = RunnerErrorKind::FilterInvalidInput(v.repr());
            Err(RunnerError::new(source_info, kind, assert))
        }
    }
```



Code after:
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
use hurl_core::ast::SourceInfo;

use crate::runner::{RunnerError, RunnerErrorKind, Value};

/// Returns the element from a collection `value` at a zero-based index.
pub fn eval_nth(
    value: &Value,
    source_info: SourceInfo,
    assert: bool,
    n: i64,
) -> Result<Option<Value>, RunnerError> {
    match value {
        Value::List(values) => {
            let len = values.len() as i64;
            let value = if n >= 0 && n < len {
                &values[n as usize]
            } else if n < 0 && len - n.abs() >= 0 {
                &values[(len - n.abs()) as usize]
            } else {
                let kind = RunnerErrorKind::FilterInvalidInput(format!(
                    "Out of bound - size is {}",
                    values.len()
                ));
                return Err(RunnerError::new(source_info, kind, assert));
            };
            Ok(Some(value.clone()))
        }
        v => {
            let kind = RunnerErrorKind::FilterInvalidInput(v.repr());
            Err(RunnerError::new(source_info, kind, assert))
        }
    }
}

#[cfg(test)]
mod tests {
    use hurl_core::ast::{Filter, FilterValue, SourceInfo, Whitespace, I64};
    use hurl_core::reader::Pos;
    use hurl_core::typing::ToSource;

    use crate::runner::filter::eval::eval_filter;
    use crate::runner::{Number, RunnerError, RunnerErrorKind, Value, VariableSet};