@@ -166,6 +166,9 @@ fn parse_channel(
166166 feed. bozo_exception = Some ( MALFORMED_ATTRIBUTES_ERROR . to_string ( ) ) ;
167167 }
168168
169+ // Extract xml:lang before matching to avoid borrow issues
170+ let item_lang = extract_xml_lang ( & e, limits. max_attribute_length ) ;
171+
169172 // Use full qualified name to distinguish standard RSS tags from namespaced tags
170173 match tag. as_slice ( ) {
171174 b"title" | b"link" | b"description" | b"language" | b"pubDate"
@@ -186,48 +189,21 @@ fn parse_channel(
186189 }
187190 }
188191 b"item" => {
189- let item_lang = extract_xml_lang ( & e, limits. max_attribute_length ) ;
190-
191- if !feed. check_entry_limit ( reader, & mut buf, limits, depth) ? {
192- continue ;
193- }
194-
195- let effective_lang = item_lang. as_deref ( ) . or ( channel_lang) ;
196-
197- match parse_item ( reader, & mut buf, limits, depth, base_ctx, effective_lang)
198- {
199- Ok ( ( entry, has_attr_errors) ) => {
200- if has_attr_errors {
201- feed. bozo = true ;
202- feed. bozo_exception =
203- Some ( MALFORMED_ATTRIBUTES_ERROR . to_string ( ) ) ;
204- }
205- feed. entries . push ( entry) ;
206- }
207- Err ( e) => {
208- feed. bozo = true ;
209- feed. bozo_exception = Some ( e. to_string ( ) ) ;
210- }
211- }
192+ parse_channel_item (
193+ item_lang. as_deref ( ) ,
194+ reader,
195+ & mut buf,
196+ feed,
197+ limits,
198+ depth,
199+ base_ctx,
200+ channel_lang,
201+ ) ?;
212202 }
213203 _ => {
214- let mut handled = parse_channel_itunes (
204+ parse_channel_extension (
215205 reader, & mut buf, & tag, & attrs, feed, limits, depth,
216206 ) ?;
217- if !handled {
218- handled = parse_channel_podcast (
219- reader, & mut buf, & tag, & attrs, feed, limits,
220- ) ?;
221- }
222- if !handled {
223- handled = parse_channel_namespace (
224- reader, & mut buf, & tag, feed, limits, * depth,
225- ) ?;
226- }
227-
228- if !handled {
229- skip_element ( reader, & mut buf, limits, * depth) ?;
230- }
231207 }
232208 }
233209 * depth = depth. saturating_sub ( 1 ) ;
@@ -245,6 +221,71 @@ fn parse_channel(
245221 Ok ( ( ) )
246222}
247223
224+ /// Parse <item> element within channel
225+ ///
226+ /// Note: Uses 8 parameters instead of a context struct due to borrow checker constraints
227+ /// with multiple simultaneous `&mut` references during parsing.
228+ #[ inline]
229+ #[ allow( clippy:: too_many_arguments) ]
230+ fn parse_channel_item (
231+ item_lang : Option < & str > ,
232+ reader : & mut Reader < & [ u8 ] > ,
233+ buf : & mut Vec < u8 > ,
234+ feed : & mut ParsedFeed ,
235+ limits : & ParserLimits ,
236+ depth : & mut usize ,
237+ base_ctx : & BaseUrlContext ,
238+ channel_lang : Option < & str > ,
239+ ) -> Result < ( ) > {
240+ if !feed. check_entry_limit ( reader, buf, limits, depth) ? {
241+ return Ok ( ( ) ) ;
242+ }
243+
244+ let effective_lang = item_lang. or ( channel_lang) ;
245+
246+ match parse_item ( reader, buf, limits, depth, base_ctx, effective_lang) {
247+ Ok ( ( entry, has_attr_errors) ) => {
248+ if has_attr_errors {
249+ feed. bozo = true ;
250+ feed. bozo_exception = Some ( MALFORMED_ATTRIBUTES_ERROR . to_string ( ) ) ;
251+ }
252+ feed. entries . push ( entry) ;
253+ }
254+ Err ( e) => {
255+ feed. bozo = true ;
256+ feed. bozo_exception = Some ( e. to_string ( ) ) ;
257+ }
258+ }
259+
260+ Ok ( ( ) )
261+ }
262+
263+ /// Parse channel extension elements (iTunes, Podcast, namespaces)
264+ #[ inline]
265+ fn parse_channel_extension (
266+ reader : & mut Reader < & [ u8 ] > ,
267+ buf : & mut Vec < u8 > ,
268+ tag : & [ u8 ] ,
269+ attrs : & [ ( Vec < u8 > , String ) ] ,
270+ feed : & mut ParsedFeed ,
271+ limits : & ParserLimits ,
272+ depth : & mut usize ,
273+ ) -> Result < ( ) > {
274+ let mut handled = parse_channel_itunes ( reader, buf, tag, attrs, feed, limits, depth) ?;
275+ if !handled {
276+ handled = parse_channel_podcast ( reader, buf, tag, attrs, feed, limits) ?;
277+ }
278+ if !handled {
279+ handled = parse_channel_namespace ( reader, buf, tag, feed, limits, * depth) ?;
280+ }
281+
282+ if !handled {
283+ skip_element ( reader, buf, limits, * depth) ?;
284+ }
285+
286+ Ok ( ( ) )
287+ }
288+
248289/// Parse enclosure element from attributes
249290#[ inline]
250291fn parse_enclosure ( attrs : & [ ( Vec < u8 > , String ) ] , limits : & ParserLimits ) -> Option < Enclosure > {
@@ -621,8 +662,9 @@ fn parse_item(
621662 }
622663 }
623664 _ => {
624- let mut handled =
625- parse_item_itunes ( reader, buf, & tag, & attrs, & mut entry, limits) ?;
665+ let mut handled = parse_item_itunes (
666+ reader, buf, & tag, & attrs, & mut entry, limits, is_empty, * depth,
667+ ) ?;
626668 if !handled {
627669 handled = parse_item_podcast (
628670 reader, buf, & tag, & attrs, & mut entry, limits, is_empty, * depth,
@@ -729,14 +771,20 @@ fn parse_item_standard(
729771/// Parse iTunes namespace tags at item level
730772///
731773/// Returns `Ok(true)` if the tag was recognized and handled, `Ok(false)` if not recognized.
774+ ///
775+ /// Note: Uses 8 parameters instead of a context struct due to borrow checker constraints
776+ /// with multiple simultaneous `&mut` references during parsing.
732777#[ inline]
778+ #[ allow( clippy:: too_many_arguments) ]
733779fn parse_item_itunes (
734780 reader : & mut Reader < & [ u8 ] > ,
735781 buf : & mut Vec < u8 > ,
736782 tag : & [ u8 ] ,
737783 attrs : & [ ( Vec < u8 > , String ) ] ,
738784 entry : & mut Entry ,
739785 limits : & ParserLimits ,
786+ is_empty : bool ,
787+ depth : usize ,
740788) -> Result < bool > {
741789 if is_itunes_tag ( tag, b"title" ) {
742790 let text = read_text ( reader, buf, limits) ?;
@@ -763,6 +811,9 @@ fn parse_item_itunes(
763811 let itunes = entry. itunes . get_or_insert_with ( ItunesEntryMeta :: default) ;
764812 itunes. image = Some ( truncate_to_length ( value, limits. max_attribute_length ) ) ;
765813 }
814+ if !is_empty {
815+ skip_element ( reader, buf, limits, depth) ?;
816+ }
766817 Ok ( true )
767818 } else if is_itunes_tag ( tag, b"episode" ) {
768819 let text = read_text ( reader, buf, limits) ?;
0 commit comments