lowdash/
nth.rs

1use std::error::Error;
2use std::fmt;
3
4#[derive(Debug, PartialEq)]
5pub struct NthError {
6    index: i64,
7}
8
9impl fmt::Display for NthError {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        write!(f, "nth: {} out of slice bounds", self.index)
12    }
13}
14
15impl Error for NthError {}
16
17/// Returns the nth element from the collection.
18/// Supports both positive and negative indices.
19/// Negative indices count from the end of the collection.
20///
21/// # Arguments
22/// * `collection` - A slice of items
23/// * `nth` - Index of the desired element (can be negative)
24///
25/// # Returns
26/// * `Ok(&T)` - The element at the specified index
27/// * `Err(NthError)` - If the index is out of bounds
28///
29/// # Examples
30/// ```rust
31/// use lowdash::nth;
32///
33/// let numbers = vec![1, 2, 3, 4, 5];
34/// let result = nth(&numbers, 2);
35/// assert_eq!(result.unwrap(), &3);
36///
37/// let result = nth(&numbers, -2);
38/// assert_eq!(result.unwrap(), &4);
39///
40/// let result = nth(&numbers, 10);
41/// assert!(result.is_err());
42/// ```
43pub fn nth<T>(collection: &[T], nth: i64) -> Result<&T, NthError> {
44    let len = collection.len() as i64;
45
46    if nth >= len || -nth > len {
47        return Err(NthError { index: nth });
48    }
49
50    let index = if nth >= 0 { nth } else { len + nth } as usize;
51
52    Ok(&collection[index])
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn test_nth_positive_index() {
61        let collection = vec![1, 2, 3, 4, 5];
62        let result = nth(&collection, 2);
63        assert_eq!(result.unwrap(), &3);
64    }
65
66    #[test]
67    fn test_nth_negative_index() {
68        let collection = vec![1, 2, 3, 4, 5];
69        let result = nth(&collection, -2);
70        assert_eq!(result.unwrap(), &4);
71    }
72
73    #[test]
74    fn test_nth_out_of_bounds_positive() {
75        let collection = vec![1, 2, 3];
76        let result = nth(&collection, 5);
77        assert_eq!(result.unwrap_err(), NthError { index: 5 });
78    }
79
80    #[test]
81    fn test_nth_out_of_bounds_negative() {
82        let collection = vec![1, 2, 3];
83        let result = nth(&collection, -5);
84        assert_eq!(result.unwrap_err(), NthError { index: -5 });
85    }
86
87    #[test]
88    fn test_nth_empty_collection() {
89        let collection: Vec<i32> = vec![];
90        let result = nth(&collection, 0);
91        assert_eq!(result.unwrap_err(), NthError { index: 0 });
92    }
93
94    #[test]
95    fn test_nth_with_structs() {
96        #[derive(Debug, PartialEq)]
97        struct Person {
98            name: String,
99            age: u32,
100        }
101
102        let people = vec![
103            Person {
104                name: "Alice".to_string(),
105                age: 25,
106            },
107            Person {
108                name: "Bob".to_string(),
109                age: 30,
110            },
111            Person {
112                name: "Carol".to_string(),
113                age: 35,
114            },
115        ];
116
117        let result = nth(&people, 1);
118        assert_eq!(
119            result.unwrap(),
120            &Person {
121                name: "Bob".to_string(),
122                age: 30,
123            }
124        );
125
126        let result = nth(&people, -1);
127        assert_eq!(
128            result.unwrap(),
129            &Person {
130                name: "Carol".to_string(),
131                age: 35,
132            }
133        );
134    }
135
136    #[test]
137    fn test_nth_first_and_last() {
138        let collection = vec![1, 2, 3, 4, 5];
139        assert_eq!(nth(&collection, 0).unwrap(), &1);
140        assert_eq!(nth(&collection, -1).unwrap(), &5);
141    }
142
143    #[test]
144    fn test_nth_with_single_element() {
145        let collection = vec![42];
146        assert_eq!(nth(&collection, 0).unwrap(), &42);
147        assert_eq!(nth(&collection, -1).unwrap(), &42);
148        assert!(nth(&collection, 1).is_err());
149        assert!(nth(&collection, -2).is_err());
150    }
151
152    #[test]
153    fn test_nth_error_display() {
154        let error = NthError { index: 5 };
155        assert_eq!(error.to_string(), "nth: 5 out of slice bounds");
156
157        let error = NthError { index: -3 };
158        assert_eq!(error.to_string(), "nth: -3 out of slice bounds");
159    }
160}