1use toml;
2
3use crate::rule::{LintError, LintResult, LintWarning, Rule, RuleCategory, Severity};
4use crate::rule_config_serde::RuleConfig;
5use crate::utils::range_utils::calculate_match_range;
6use std::collections::{HashMap, HashSet};
7
8mod md024_config;
9use md024_config::MD024Config;
10
11#[derive(Clone, Debug, Default)]
12pub struct MD024NoDuplicateHeading {
13 config: MD024Config,
14}
15
16impl MD024NoDuplicateHeading {
17 pub fn new(allow_different_nesting: bool, siblings_only: bool) -> Self {
18 Self {
19 config: MD024Config {
20 allow_different_nesting,
21 siblings_only,
22 },
23 }
24 }
25
26 pub fn from_config_struct(config: MD024Config) -> Self {
27 Self { config }
28 }
29}
30
31impl Rule for MD024NoDuplicateHeading {
32 fn name(&self) -> &'static str {
33 "MD024"
34 }
35
36 fn description(&self) -> &'static str {
37 "Multiple headings with the same content"
38 }
39
40 fn check(&self, ctx: &crate::lint_context::LintContext) -> LintResult {
41 if ctx.lines.is_empty() {
43 return Ok(Vec::new());
44 }
45
46 let mut warnings = Vec::new();
47 let mut seen_headings: HashSet<String> = HashSet::new();
48 let mut seen_headings_per_level: HashMap<u8, HashSet<String>> = HashMap::new();
49
50 let mut current_section_path: Vec<(u8, String)> = Vec::new(); let mut seen_siblings: HashMap<String, HashSet<String>> = HashMap::new(); let is_mkdocs = ctx.flavor == crate::config::MarkdownFlavor::MkDocs;
56 let mut in_snippet_section = false;
57
58 for (line_num, line_info) in ctx.lines.iter().enumerate() {
60 if is_mkdocs {
62 if crate::utils::mkdocs_snippets::is_snippet_section_start(line_info.content(ctx.content)) {
63 in_snippet_section = true;
64 continue; } else if crate::utils::mkdocs_snippets::is_snippet_section_end(line_info.content(ctx.content)) {
66 in_snippet_section = false;
67 continue; }
69 }
70
71 if is_mkdocs && in_snippet_section {
73 continue;
74 }
75
76 if let Some(heading) = &line_info.heading {
77 if heading.text.is_empty() {
79 continue;
80 }
81
82 let heading_key = heading.text.clone();
83 let level = heading.level;
84
85 let text_start_in_line = if let Some(pos) = line_info.content(ctx.content).find(&heading.text) {
87 pos
88 } else {
89 let trimmed = line_info.content(ctx.content).trim_start();
91 let hash_count = trimmed.chars().take_while(|&c| c == '#').count();
92 let after_hashes = &trimmed[hash_count..];
93 let text_start_in_trimmed = after_hashes.find(&heading.text).unwrap_or(0);
94 (line_info.byte_len - trimmed.len()) + hash_count + text_start_in_trimmed
95 };
96
97 let (start_line, start_col, end_line, end_col) = calculate_match_range(
98 line_num + 1,
99 line_info.content(ctx.content),
100 text_start_in_line,
101 heading.text.len(),
102 );
103
104 if self.config.siblings_only {
105 while !current_section_path.is_empty() && current_section_path.last().unwrap().0 >= level {
107 current_section_path.pop();
108 }
109
110 let parent_path = current_section_path
112 .iter()
113 .map(|(_, text)| text.as_str())
114 .collect::<Vec<_>>()
115 .join("/");
116
117 let siblings = seen_siblings.entry(parent_path.clone()).or_default();
119 if siblings.contains(&heading_key) {
120 warnings.push(LintWarning {
121 rule_name: Some(self.name().to_string()),
122 message: format!("Duplicate heading: '{}'.", heading.text),
123 line: start_line,
124 column: start_col,
125 end_line,
126 end_column: end_col,
127 severity: Severity::Warning,
128 fix: None,
129 });
130 } else {
131 siblings.insert(heading_key.clone());
132 }
133
134 current_section_path.push((level, heading_key.clone()));
136 } else if self.config.allow_different_nesting {
137 let seen = seen_headings_per_level.entry(level).or_default();
139 if seen.contains(&heading_key) {
140 warnings.push(LintWarning {
141 rule_name: Some(self.name().to_string()),
142 message: format!("Duplicate heading: '{}'.", heading.text),
143 line: start_line,
144 column: start_col,
145 end_line,
146 end_column: end_col,
147 severity: Severity::Warning,
148 fix: None,
149 });
150 } else {
151 seen.insert(heading_key.clone());
152 }
153 } else {
154 if seen_headings.contains(&heading_key) {
156 warnings.push(LintWarning {
157 rule_name: Some(self.name().to_string()),
158 message: format!("Duplicate heading: '{}'.", heading.text),
159 line: start_line,
160 column: start_col,
161 end_line,
162 end_column: end_col,
163 severity: Severity::Warning,
164 fix: None,
165 });
166 } else {
167 seen_headings.insert(heading_key.clone());
168 }
169 }
170 }
171 }
172
173 Ok(warnings)
174 }
175
176 fn fix(&self, ctx: &crate::lint_context::LintContext) -> Result<String, LintError> {
177 Ok(ctx.content.to_string())
179 }
180
181 fn category(&self) -> RuleCategory {
183 RuleCategory::Heading
184 }
185
186 fn should_skip(&self, ctx: &crate::lint_context::LintContext) -> bool {
188 if !ctx.likely_has_headings() {
190 return true;
191 }
192 ctx.lines.iter().all(|line| line.heading.is_none())
194 }
195
196 fn as_any(&self) -> &dyn std::any::Any {
197 self
198 }
199
200 fn default_config_section(&self) -> Option<(String, toml::Value)> {
201 let default_config = MD024Config::default();
202 let json_value = serde_json::to_value(&default_config).ok()?;
203 let toml_value = crate::rule_config_serde::json_to_toml_value(&json_value)?;
204
205 if let toml::Value::Table(table) = toml_value {
206 if !table.is_empty() {
207 Some((MD024Config::RULE_NAME.to_string(), toml::Value::Table(table)))
208 } else {
209 None
210 }
211 } else {
212 None
213 }
214 }
215
216 fn from_config(config: &crate::config::Config) -> Box<dyn Rule>
217 where
218 Self: Sized,
219 {
220 let rule_config = crate::rule_config_serde::load_rule_config::<MD024Config>(config);
221 Box::new(Self::from_config_struct(rule_config))
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use crate::lint_context::LintContext;
229
230 fn run_test(content: &str, config: MD024Config) -> LintResult {
231 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard, None);
232 let rule = MD024NoDuplicateHeading::from_config_struct(config);
233 rule.check(&ctx)
234 }
235
236 fn run_fix_test(content: &str, config: MD024Config) -> Result<String, LintError> {
237 let ctx = LintContext::new(content, crate::config::MarkdownFlavor::Standard, None);
238 let rule = MD024NoDuplicateHeading::from_config_struct(config);
239 rule.fix(&ctx)
240 }
241
242 #[test]
243 fn test_no_duplicate_headings() {
244 let content = r#"# First Heading
245
246Some content here.
247
248## Second Heading
249
250More content.
251
252### Third Heading
253
254Even more content.
255
256## Fourth Heading
257
258Final content."#;
259
260 let config = MD024Config::default();
261 let result = run_test(content, config);
262 assert!(result.is_ok());
263 let warnings = result.unwrap();
264 assert_eq!(warnings.len(), 0);
265 }
266
267 #[test]
268 fn test_duplicate_headings_same_level() {
269 let content = r#"# First Heading
270
271Some content here.
272
273## Second Heading
274
275More content.
276
277## Second Heading
278
279This is a duplicate."#;
280
281 let config = MD024Config::default();
282 let result = run_test(content, config);
283 assert!(result.is_ok());
284 let warnings = result.unwrap();
285 assert_eq!(warnings.len(), 1);
286 assert_eq!(warnings[0].message, "Duplicate heading: 'Second Heading'.");
287 assert_eq!(warnings[0].line, 9);
288 }
289
290 #[test]
291 fn test_duplicate_headings_different_levels_default() {
292 let content = r#"# Main Title
293
294Some content.
295
296## Main Title
297
298This has the same text but different level."#;
299
300 let config = MD024Config {
301 allow_different_nesting: false,
302 siblings_only: false,
303 };
304 let result = run_test(content, config);
305 assert!(result.is_ok());
306 let warnings = result.unwrap();
307 assert_eq!(warnings.len(), 1);
308 assert_eq!(warnings[0].message, "Duplicate heading: 'Main Title'.");
309 assert_eq!(warnings[0].line, 5);
310 }
311
312 #[test]
313 fn test_duplicate_headings_different_levels_allow_different_nesting() {
314 let content = r#"# Main Title
315
316Some content.
317
318## Main Title
319
320This has the same text but different level."#;
321
322 let config = MD024Config {
323 allow_different_nesting: true,
324 siblings_only: false,
325 };
326 let result = run_test(content, config);
327 assert!(result.is_ok());
328 let warnings = result.unwrap();
329 assert_eq!(warnings.len(), 0);
330 }
331
332 #[test]
333 fn test_case_sensitivity() {
334 let content = r#"# First Heading
335
336Some content.
337
338## first heading
339
340Different case.
341
342### FIRST HEADING
343
344All caps."#;
345
346 let config = MD024Config::default();
347 let result = run_test(content, config);
348 assert!(result.is_ok());
349 let warnings = result.unwrap();
350 assert_eq!(warnings.len(), 0);
352 }
353
354 #[test]
355 fn test_headings_with_trailing_punctuation() {
356 let content = r#"# First Heading!
357
358Some content.
359
360## First Heading!
361
362Same with punctuation.
363
364### First Heading
365
366Without punctuation."#;
367
368 let config = MD024Config {
369 allow_different_nesting: false,
370 siblings_only: false,
371 };
372 let result = run_test(content, config);
373 assert!(result.is_ok());
374 let warnings = result.unwrap();
375 assert_eq!(warnings.len(), 1);
376 assert_eq!(warnings[0].message, "Duplicate heading: 'First Heading!'.");
377 }
378
379 #[test]
380 fn test_headings_with_inline_formatting() {
381 let content = r#"# **Bold Heading**
382
383Some content.
384
385## *Italic Heading*
386
387More content.
388
389### **Bold Heading**
390
391Duplicate with same formatting.
392
393#### `Code Heading`
394
395Code formatted.
396
397##### `Code Heading`
398
399Duplicate code formatted."#;
400
401 let config = MD024Config {
402 allow_different_nesting: false,
403 siblings_only: false,
404 };
405 let result = run_test(content, config);
406 assert!(result.is_ok());
407 let warnings = result.unwrap();
408 assert_eq!(warnings.len(), 2);
409 assert_eq!(warnings[0].message, "Duplicate heading: '**Bold Heading**'.");
410 assert_eq!(warnings[1].message, "Duplicate heading: '`Code Heading`'.");
411 }
412
413 #[test]
414 fn test_headings_in_different_sections() {
415 let content = r#"# Section One
416
417## Subsection
418
419Some content.
420
421# Section Two
422
423## Subsection
424
425Same subsection name in different section."#;
426
427 let config = MD024Config {
428 allow_different_nesting: false,
429 siblings_only: false,
430 };
431 let result = run_test(content, config);
432 assert!(result.is_ok());
433 let warnings = result.unwrap();
434 assert_eq!(warnings.len(), 1);
435 assert_eq!(warnings[0].message, "Duplicate heading: 'Subsection'.");
436 assert_eq!(warnings[0].line, 9);
437 }
438
439 #[test]
440 fn test_multiple_duplicates() {
441 let content = r#"# Title
442
443## Subtitle
444
445### Title
446
447#### Subtitle
448
449## Title
450
451### Subtitle"#;
452
453 let config = MD024Config {
454 allow_different_nesting: false,
455 siblings_only: false,
456 };
457 let result = run_test(content, config);
458 assert!(result.is_ok());
459 let warnings = result.unwrap();
460 assert_eq!(warnings.len(), 4);
461 assert_eq!(warnings[0].message, "Duplicate heading: 'Title'.");
463 assert_eq!(warnings[0].line, 5);
464 assert_eq!(warnings[1].message, "Duplicate heading: 'Subtitle'.");
466 assert_eq!(warnings[1].line, 7);
467 assert_eq!(warnings[2].message, "Duplicate heading: 'Title'.");
469 assert_eq!(warnings[2].line, 9);
470 assert_eq!(warnings[3].message, "Duplicate heading: 'Subtitle'.");
472 assert_eq!(warnings[3].line, 11);
473 }
474
475 #[test]
476 fn test_empty_headings() {
477 let content = r#"#
478
479Some content.
480
481##
482
483More content.
484
485### Non-empty
486
487####
488
489Another empty."#;
490
491 let config = MD024Config::default();
492 let result = run_test(content, config);
493 assert!(result.is_ok());
494 let warnings = result.unwrap();
495 assert_eq!(warnings.len(), 0);
497 }
498
499 #[test]
500 fn test_unicode_and_special_characters() {
501 let content = r#"# 你好世界
502
503Some content.
504
505## Émojis 🎉🎊
506
507More content.
508
509### 你好世界
510
511Duplicate Chinese.
512
513#### Émojis 🎉🎊
514
515Duplicate emojis.
516
517##### Special <chars> & symbols!
518
519###### Special <chars> & symbols!
520
521Duplicate special chars."#;
522
523 let config = MD024Config {
524 allow_different_nesting: false,
525 siblings_only: false,
526 };
527 let result = run_test(content, config);
528 assert!(result.is_ok());
529 let warnings = result.unwrap();
530 assert_eq!(warnings.len(), 3);
531 assert_eq!(warnings[0].message, "Duplicate heading: '你好世界'.");
532 assert_eq!(warnings[1].message, "Duplicate heading: 'Émojis 🎉🎊'.");
533 assert_eq!(warnings[2].message, "Duplicate heading: 'Special <chars> & symbols!'.");
534 }
535
536 #[test]
537 fn test_allow_different_nesting_with_same_level_duplicates() {
538 let content = r#"# Section One
539
540## Title
541
542### Subsection
543
544## Title
545
546This is a duplicate at the same level.
547
548# Section Two
549
550## Title
551
552Different section, but still a duplicate when allow_different_nesting is true."#;
553
554 let config = MD024Config {
555 allow_different_nesting: true,
556 siblings_only: false,
557 };
558 let result = run_test(content, config);
559 assert!(result.is_ok());
560 let warnings = result.unwrap();
561 assert_eq!(warnings.len(), 2);
562 assert_eq!(warnings[0].message, "Duplicate heading: 'Title'.");
563 assert_eq!(warnings[0].line, 7);
564 assert_eq!(warnings[1].message, "Duplicate heading: 'Title'.");
565 assert_eq!(warnings[1].line, 13);
566 }
567
568 #[test]
569 fn test_atx_style_headings_with_closing_hashes() {
570 let content = r#"# Heading One #
571
572Some content.
573
574## Heading Two ##
575
576More content.
577
578### Heading One ###
579
580Duplicate with different style."#;
581
582 let config = MD024Config {
583 allow_different_nesting: false,
584 siblings_only: false,
585 };
586 let result = run_test(content, config);
587 assert!(result.is_ok());
588 let warnings = result.unwrap();
589 assert_eq!(warnings.len(), 1);
591 assert_eq!(warnings[0].message, "Duplicate heading: 'Heading One'.");
592 assert_eq!(warnings[0].line, 9);
593 }
594
595 #[test]
596 fn test_fix_method_returns_unchanged() {
597 let content = r#"# Duplicate
598
599## Duplicate
600
601This has duplicates."#;
602
603 let config = MD024Config::default();
604 let result = run_fix_test(content, config);
605 assert!(result.is_ok());
606 assert_eq!(result.unwrap(), content);
607 }
608
609 #[test]
610 fn test_empty_content() {
611 let content = "";
612 let config = MD024Config::default();
613 let result = run_test(content, config);
614 assert!(result.is_ok());
615 let warnings = result.unwrap();
616 assert_eq!(warnings.len(), 0);
617 }
618
619 #[test]
620 fn test_no_headings() {
621 let content = r#"This is just regular text.
622
623No headings anywhere.
624
625Just paragraphs."#;
626
627 let config = MD024Config::default();
628 let result = run_test(content, config);
629 assert!(result.is_ok());
630 let warnings = result.unwrap();
631 assert_eq!(warnings.len(), 0);
632 }
633
634 #[test]
635 fn test_whitespace_differences() {
636 let content = r#"# Heading with spaces
637
638Some content.
639
640## Heading with spaces
641
642Different amount of spaces.
643
644### Heading with spaces
645
646Exact match."#;
647
648 let config = MD024Config {
649 allow_different_nesting: false,
650 siblings_only: false,
651 };
652 let result = run_test(content, config);
653 assert!(result.is_ok());
654 let warnings = result.unwrap();
655 assert_eq!(warnings.len(), 2);
657 assert_eq!(warnings[0].message, "Duplicate heading: 'Heading with spaces'.");
658 assert_eq!(warnings[0].line, 5);
659 assert_eq!(warnings[1].message, "Duplicate heading: 'Heading with spaces'.");
660 assert_eq!(warnings[1].line, 9);
661 }
662
663 #[test]
664 fn test_column_positions() {
665 let content = r#"# First
666
667## Second
668
669### First"#;
670
671 let config = MD024Config {
672 allow_different_nesting: false,
673 siblings_only: false,
674 };
675 let result = run_test(content, config);
676 assert!(result.is_ok());
677 let warnings = result.unwrap();
678 assert_eq!(warnings.len(), 1);
679 assert_eq!(warnings[0].line, 5);
680 assert_eq!(warnings[0].column, 5); assert_eq!(warnings[0].end_line, 5);
682 assert_eq!(warnings[0].end_column, 10); }
684
685 #[test]
686 fn test_complex_nesting_scenario() {
687 let content = r#"# Main Document
688
689## Introduction
690
691### Overview
692
693## Implementation
694
695### Overview
696
697This Overview is in a different section.
698
699## Conclusion
700
701### Overview
702
703Another Overview in yet another section."#;
704
705 let config = MD024Config {
706 allow_different_nesting: true,
707 siblings_only: false,
708 };
709 let result = run_test(content, config);
710 assert!(result.is_ok());
711 let warnings = result.unwrap();
712 assert_eq!(warnings.len(), 2);
714 assert_eq!(warnings[0].message, "Duplicate heading: 'Overview'.");
715 assert_eq!(warnings[0].line, 9);
716 assert_eq!(warnings[1].message, "Duplicate heading: 'Overview'.");
717 assert_eq!(warnings[1].line, 15);
718 }
719
720 #[test]
721 fn test_setext_style_headings() {
722 let content = r#"Main Title
723==========
724
725Some content.
726
727Second Title
728------------
729
730More content.
731
732Main Title
733==========
734
735Duplicate setext."#;
736
737 let config = MD024Config::default();
738 let result = run_test(content, config);
739 assert!(result.is_ok());
740 let warnings = result.unwrap();
741 assert_eq!(warnings.len(), 1);
742 assert_eq!(warnings[0].message, "Duplicate heading: 'Main Title'.");
743 assert_eq!(warnings[0].line, 11);
744 }
745
746 #[test]
747 fn test_mixed_heading_styles() {
748 let content = r#"# ATX Title
749
750Some content.
751
752ATX Title
753=========
754
755Same text, different style."#;
756
757 let config = MD024Config::default();
758 let result = run_test(content, config);
759 assert!(result.is_ok());
760 let warnings = result.unwrap();
761 assert_eq!(warnings.len(), 1);
762 assert_eq!(warnings[0].message, "Duplicate heading: 'ATX Title'.");
763 assert_eq!(warnings[0].line, 5);
764 }
765
766 #[test]
767 fn test_heading_with_links() {
768 let content = r#"# [Link Text](http://example.com)
769
770Some content.
771
772## [Link Text](http://example.com)
773
774Duplicate heading with link.
775
776### [Different Link](http://example.com)
777
778Not a duplicate."#;
779
780 let config = MD024Config {
781 allow_different_nesting: false,
782 siblings_only: false,
783 };
784 let result = run_test(content, config);
785 assert!(result.is_ok());
786 let warnings = result.unwrap();
787 assert_eq!(warnings.len(), 1);
788 assert_eq!(
789 warnings[0].message,
790 "Duplicate heading: '[Link Text](http://example.com)'."
791 );
792 assert_eq!(warnings[0].line, 5);
793 }
794
795 #[test]
796 fn test_consecutive_duplicates() {
797 let content = r#"# Title
798
799## Title
800
801### Title
802
803Three in a row."#;
804
805 let config = MD024Config {
806 allow_different_nesting: false,
807 siblings_only: false,
808 };
809 let result = run_test(content, config);
810 assert!(result.is_ok());
811 let warnings = result.unwrap();
812 assert_eq!(warnings.len(), 2);
813 assert_eq!(warnings[0].message, "Duplicate heading: 'Title'.");
814 assert_eq!(warnings[0].line, 3);
815 assert_eq!(warnings[1].message, "Duplicate heading: 'Title'.");
816 assert_eq!(warnings[1].line, 5);
817 }
818
819 #[test]
820 fn test_siblings_only_config() {
821 let content = r#"# Section One
822
823## Subsection
824
825### Details
826
827# Section Two
828
829## Subsection
830
831Different parent sections, so not siblings - no warning expected."#;
832
833 let config = MD024Config {
834 allow_different_nesting: false,
835 siblings_only: true,
836 };
837 let result = run_test(content, config);
838 assert!(result.is_ok());
839 let warnings = result.unwrap();
840 assert_eq!(warnings.len(), 0);
842 }
843
844 #[test]
845 fn test_siblings_only_with_actual_siblings() {
846 let content = r#"# Main Section
847
848## First Subsection
849
850### Details
851
852## Second Subsection
853
854### Details
855
856The two 'Details' headings are siblings under different subsections - no warning.
857
858## First Subsection
859
860This 'First Subsection' IS a sibling duplicate."#;
861
862 let config = MD024Config {
863 allow_different_nesting: false,
864 siblings_only: true,
865 };
866 let result = run_test(content, config);
867 assert!(result.is_ok());
868 let warnings = result.unwrap();
869 assert_eq!(warnings.len(), 1);
871 assert_eq!(warnings[0].message, "Duplicate heading: 'First Subsection'.");
872 assert_eq!(warnings[0].line, 13);
873 }
874
875 #[test]
876 fn test_code_spans_in_headings() {
877 let content = r#"# `code` in heading
878
879Some content.
880
881## `code` in heading
882
883Duplicate with code span."#;
884
885 let config = MD024Config {
886 allow_different_nesting: false,
887 siblings_only: false,
888 };
889 let result = run_test(content, config);
890 assert!(result.is_ok());
891 let warnings = result.unwrap();
892 assert_eq!(warnings.len(), 1);
893 assert_eq!(warnings[0].message, "Duplicate heading: '`code` in heading'.");
894 assert_eq!(warnings[0].line, 5);
895 }
896
897 #[test]
898 fn test_very_long_heading() {
899 let long_text = "This is a very long heading that goes on and on and on and contains many words to test how the rule handles long headings";
900 let content = format!("# {long_text}\n\nSome content.\n\n## {long_text}\n\nDuplicate long heading.");
901
902 let config = MD024Config {
903 allow_different_nesting: false,
904 siblings_only: false,
905 };
906 let result = run_test(&content, config);
907 assert!(result.is_ok());
908 let warnings = result.unwrap();
909 assert_eq!(warnings.len(), 1);
910 assert_eq!(warnings[0].message, format!("Duplicate heading: '{long_text}'."));
911 assert_eq!(warnings[0].line, 5);
912 }
913
914 #[test]
915 fn test_heading_with_html_entities() {
916 let content = r#"# Title & More
917
918Some content.
919
920## Title & More
921
922Duplicate with HTML entity."#;
923
924 let config = MD024Config {
925 allow_different_nesting: false,
926 siblings_only: false,
927 };
928 let result = run_test(content, config);
929 assert!(result.is_ok());
930 let warnings = result.unwrap();
931 assert_eq!(warnings.len(), 1);
932 assert_eq!(warnings[0].message, "Duplicate heading: 'Title & More'.");
933 assert_eq!(warnings[0].line, 5);
934 }
935
936 #[test]
937 fn test_three_duplicates_different_nesting() {
938 let content = r#"# Main
939
940## Main
941
942### Main
943
944#### Main
945
946All same text, different levels."#;
947
948 let config = MD024Config {
949 allow_different_nesting: true,
950 siblings_only: false,
951 };
952 let result = run_test(content, config);
953 assert!(result.is_ok());
954 let warnings = result.unwrap();
955 assert_eq!(warnings.len(), 0);
957 }
958}