feedparser_rs/types/
generics.rs

1//! Generic type abstractions for DRY principle
2//!
3//! This module provides reusable generic types that eliminate code duplication
4//! across feed types and parsing logic.
5
6use std::fmt::Debug;
7
8/// A field with an optional detailed representation
9///
10/// Many feed fields follow the pattern of having a simple value (like a string)
11/// and an optional detailed struct with additional metadata. This generic wrapper
12/// captures that pattern.
13///
14/// # Type Parameters
15///
16/// * `V` - The simple value type (usually `String`)
17/// * `D` - The detailed representation type (e.g., `TextConstruct`, `Person`)
18///
19/// # Examples
20///
21/// ```
22/// use feedparser_rs::types::generics::DetailedField;
23///
24/// // Simple value only
25/// let title: DetailedField<String, ()> = DetailedField::from_value("My Title".to_string());
26/// assert_eq!(title.value(), "My Title");
27/// assert!(title.detail().is_none());
28///
29/// // With detail
30/// let title = DetailedField::with_detail("My Title".to_string(), "extra info");
31/// assert_eq!(title.detail(), Some(&"extra info"));
32/// ```
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[allow(dead_code)]
35pub struct DetailedField<V, D> {
36    value: V,
37    detail: Option<D>,
38}
39
40impl<V, D> DetailedField<V, D> {
41    /// Create a field with only a simple value
42    #[inline]
43    #[must_use]
44    pub const fn from_value(value: V) -> Self {
45        Self {
46            value,
47            detail: None,
48        }
49    }
50
51    /// Create a field with both value and detail
52    #[inline]
53    #[must_use]
54    pub const fn with_detail(value: V, detail: D) -> Self {
55        Self {
56            value,
57            detail: Some(detail),
58        }
59    }
60
61    /// Get reference to the simple value
62    #[inline]
63    #[must_use]
64    pub const fn value(&self) -> &V {
65        &self.value
66    }
67
68    /// Get mutable reference to the simple value
69    #[inline]
70    pub const fn value_mut(&mut self) -> &mut V {
71        &mut self.value
72    }
73
74    /// Get reference to the detail if present
75    #[inline]
76    #[must_use]
77    pub const fn detail(&self) -> Option<&D> {
78        self.detail.as_ref()
79    }
80
81    /// Get mutable reference to the detail if present
82    #[inline]
83    pub const fn detail_mut(&mut self) -> Option<&mut D> {
84        self.detail.as_mut()
85    }
86
87    /// Set the detail
88    #[inline]
89    pub fn set_detail(&mut self, detail: D) {
90        self.detail = Some(detail);
91    }
92
93    /// Take the detail, leaving None in its place
94    #[inline]
95    pub const fn take_detail(&mut self) -> Option<D> {
96        self.detail.take()
97    }
98
99    /// Convert into a tuple of (value, `Option<detail>`)
100    #[inline]
101    #[must_use]
102    pub fn into_parts(self) -> (V, Option<D>) {
103        (self.value, self.detail)
104    }
105}
106
107impl<V: Default, D> Default for DetailedField<V, D> {
108    fn default() -> Self {
109        Self {
110            value: V::default(),
111            detail: None,
112        }
113    }
114}
115
116impl<V, D> From<V> for DetailedField<V, D> {
117    fn from(value: V) -> Self {
118        Self::from_value(value)
119    }
120}
121
122impl<V, D> From<(V, D)> for DetailedField<V, D> {
123    fn from((value, detail): (V, D)) -> Self {
124        Self::with_detail(value, detail)
125    }
126}
127
128/// Extension trait for collections with size limits
129///
130/// Provides methods for safely adding items to collections while respecting
131/// configured limits, which is essential for `DoS` protection.
132///
133/// # Examples
134///
135/// ```
136/// use feedparser_rs::types::LimitedCollectionExt;
137///
138/// let mut vec = Vec::new();
139/// assert!(vec.try_push_limited("first", 2));
140/// assert!(vec.try_push_limited("second", 2));
141/// assert!(!vec.try_push_limited("third", 2)); // Exceeds limit
142/// assert_eq!(vec.len(), 2);
143/// ```
144pub trait LimitedCollectionExt<T> {
145    /// Try to push an item if the collection is below the limit
146    ///
147    /// Returns `true` if the item was added, `false` if limit was reached.
148    fn try_push_limited(&mut self, item: T, limit: usize) -> bool;
149
150    /// Check if the collection has reached its limit
151    fn is_at_limit(&self, limit: usize) -> bool;
152
153    /// Get remaining capacity before reaching the limit
154    fn remaining_capacity(&self, limit: usize) -> usize;
155}
156
157impl<T> LimitedCollectionExt<T> for Vec<T> {
158    #[inline]
159    fn try_push_limited(&mut self, item: T, limit: usize) -> bool {
160        if self.len() < limit {
161            self.push(item);
162            true
163        } else {
164            false
165        }
166    }
167
168    #[inline]
169    fn is_at_limit(&self, limit: usize) -> bool {
170        self.len() >= limit
171    }
172
173    #[inline]
174    #[allow(dead_code)]
175    fn remaining_capacity(&self, limit: usize) -> usize {
176        limit.saturating_sub(self.len())
177    }
178}
179
180/// Trait for types that can be built from XML attributes
181///
182/// Implement this trait for structs that are parsed from XML element attributes,
183/// providing a consistent interface for attribute extraction with limit validation.
184pub trait FromAttributes: Sized {
185    /// Parse from XML attributes with limit validation
186    ///
187    /// # Arguments
188    ///
189    /// * `attrs` - Iterator over XML attributes
190    /// * `max_attr_length` - Maximum allowed attribute value length
191    ///
192    /// # Returns
193    ///
194    /// * `Some(Self)` - Successfully parsed struct
195    /// * `None` - Required attributes missing or validation failed
196    fn from_attributes<'a, I>(attrs: I, max_attr_length: usize) -> Option<Self>
197    where
198        I: Iterator<Item = quick_xml::events::attributes::Attribute<'a>>;
199}
200
201/// Generic trait for parsing types from various sources using GAT
202///
203/// This trait provides a unified interface for constructing types from
204/// different data sources (JSON values, XML elements, etc.) using
205/// Generic Associated Types (GAT) for flexible lifetime handling.
206///
207/// # Examples
208///
209/// ```
210/// use feedparser_rs::types::generics::ParseFrom;
211/// use feedparser_rs::types::Person;
212/// use serde_json::json;
213///
214/// let json = json!({"name": "John Doe", "url": "https://example.com"});
215/// let person = Person::parse_from(&json);
216/// assert!(person.is_some());
217/// assert_eq!(person.unwrap().name.as_deref(), Some("John Doe"));
218/// ```
219pub trait ParseFrom<Source>: Sized {
220    /// Parse from the given source
221    ///
222    /// # Arguments
223    ///
224    /// * `source` - The source data to parse from
225    ///
226    /// # Returns
227    ///
228    /// * `Some(Self)` - Successfully parsed instance
229    /// * `None` - Failed to parse (missing required fields, wrong type, etc.)
230    fn parse_from(source: Source) -> Option<Self>;
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_detailed_field_from_value() {
239        let field: DetailedField<String, i32> = DetailedField::from_value("test".to_string());
240        assert_eq!(field.value(), "test");
241        assert!(field.detail().is_none());
242    }
243
244    #[test]
245    fn test_detailed_field_with_detail() {
246        let field = DetailedField::with_detail("test".to_string(), 42);
247        assert_eq!(field.value(), "test");
248        assert_eq!(field.detail(), Some(&42));
249    }
250
251    #[test]
252    fn test_detailed_field_from_tuple() {
253        let field: DetailedField<String, i32> = ("test".to_string(), 42).into();
254        assert_eq!(field.value(), "test");
255        assert_eq!(field.detail(), Some(&42));
256    }
257
258    #[test]
259    fn test_detailed_field_into_parts() {
260        let field = DetailedField::with_detail("test".to_string(), 42);
261        let (value, detail) = field.into_parts();
262        assert_eq!(value, "test");
263        assert_eq!(detail, Some(42));
264    }
265
266    #[test]
267    fn test_limited_collection_try_push() {
268        let mut vec: Vec<i32> = Vec::new();
269        assert!(vec.try_push_limited(1, 3));
270        assert!(vec.try_push_limited(2, 3));
271        assert!(vec.try_push_limited(3, 3));
272        assert!(!vec.try_push_limited(4, 3));
273        assert_eq!(vec.len(), 3);
274    }
275
276    #[test]
277    fn test_limited_collection_is_at_limit() {
278        let mut vec: Vec<i32> = Vec::new();
279        assert!(!vec.is_at_limit(2));
280        vec.push(1);
281        assert!(!vec.is_at_limit(2));
282        vec.push(2);
283        assert!(vec.is_at_limit(2));
284    }
285
286    #[test]
287    fn test_limited_collection_remaining_capacity() {
288        let mut vec: Vec<i32> = Vec::new();
289        assert_eq!(vec.remaining_capacity(5), 5);
290        vec.push(1);
291        vec.push(2);
292        assert_eq!(vec.remaining_capacity(5), 3);
293    }
294
295    #[test]
296    fn test_detailed_field_default() {
297        let field: DetailedField<String, i32> = DetailedField::default();
298        assert_eq!(field.value(), "");
299        assert!(field.detail().is_none());
300    }
301
302    #[test]
303    fn test_detailed_field_mutability() {
304        let mut field = DetailedField::from_value("original".to_string());
305        *field.value_mut() = "modified".to_string();
306        assert_eq!(field.value(), "modified");
307
308        field.set_detail(100);
309        assert_eq!(field.detail(), Some(&100));
310
311        let taken = field.take_detail();
312        assert_eq!(taken, Some(100));
313        assert!(field.detail().is_none());
314    }
315}