Skip to main content

quantrs2_circuit/formatter/
mod.rs

1//! Quantum circuit formatter with `SciRS2` code analysis for consistent code style
2//!
3//! This module provides comprehensive code formatting for quantum circuits,
4//! including automatic layout optimization, style enforcement, code organization,
5//! and intelligent formatting using `SciRS2`'s graph analysis and pattern recognition.
6
7pub mod config;
8pub mod types;
9
10#[cfg(test)]
11mod tests;
12
13// Re-export main types
14pub use config::*;
15pub use types::*;
16
17use crate::builder::Circuit;
18use crate::scirs2_integration::SciRS2CircuitAnalyzer;
19use quantrs2_core::error::QuantRS2Result;
20use std::collections::HashMap;
21use std::sync::{Arc, RwLock};
22use std::time::Instant;
23
24/// Comprehensive quantum circuit formatter with `SciRS2` integration
25pub struct QuantumFormatter<const N: usize> {
26    /// Circuit to format
27    circuit: Circuit<N>,
28    /// Formatter configuration
29    pub config: FormatterConfig,
30    /// `SciRS2` analyzer for intelligent formatting
31    analyzer: SciRS2CircuitAnalyzer,
32    /// Layout optimizer
33    layout_optimizer: Arc<RwLock<LayoutOptimizer<N>>>,
34    /// Style enforcer
35    style_enforcer: Arc<RwLock<StyleEnforcer<N>>>,
36    /// Code organizer
37    code_organizer: Arc<RwLock<CodeOrganizer<N>>>,
38    /// Comment formatter
39    comment_formatter: Arc<RwLock<CommentFormatter<N>>>,
40    /// Whitespace manager
41    whitespace_manager: Arc<RwLock<WhitespaceManager<N>>>,
42    /// Alignment engine
43    alignment_engine: Arc<RwLock<AlignmentEngine<N>>>,
44}
45
46impl<const N: usize> QuantumFormatter<N> {
47    /// Create a new quantum formatter
48    #[must_use]
49    pub fn new(circuit: Circuit<N>) -> Self {
50        Self {
51            circuit,
52            config: FormatterConfig::default(),
53            analyzer: SciRS2CircuitAnalyzer::new(),
54            layout_optimizer: Arc::new(RwLock::new(LayoutOptimizer::new())),
55            style_enforcer: Arc::new(RwLock::new(StyleEnforcer::new())),
56            code_organizer: Arc::new(RwLock::new(CodeOrganizer::new())),
57            comment_formatter: Arc::new(RwLock::new(CommentFormatter::new())),
58            whitespace_manager: Arc::new(RwLock::new(WhitespaceManager::new())),
59            alignment_engine: Arc::new(RwLock::new(AlignmentEngine::new())),
60        }
61    }
62
63    /// Format the circuit
64    pub fn format_circuit(&mut self) -> QuantRS2Result<FormattingResult> {
65        let start_time = Instant::now();
66        let mut changes = Vec::new();
67
68        // Analyze code structure
69        let code_structure = self.analyze_code_structure()?;
70
71        // Apply layout optimization
72        let layout_changes = self.optimize_layout(&code_structure)?;
73        changes.extend(layout_changes);
74
75        // Apply style enforcement
76        let style_changes = self.enforce_style(&code_structure)?;
77        changes.extend(style_changes);
78
79        // Organize code
80        let org_changes = self.organize_code(&code_structure)?;
81        changes.extend(org_changes);
82
83        // Format comments
84        let comment_changes = self.format_comments(&code_structure)?;
85        changes.extend(comment_changes);
86
87        // Manage whitespace
88        let ws_changes = self.manage_whitespace(&code_structure)?;
89        changes.extend(ws_changes);
90
91        // Apply alignment
92        let align_changes = self.apply_alignment(&code_structure)?;
93        changes.extend(align_changes);
94
95        // Build formatted code
96        let formatted_code = self.build_formatted_code(&code_structure, &changes)?;
97
98        // Calculate statistics
99        let statistics = FormattingStatistics {
100            total_lines: formatted_code.line_count,
101            lines_modified: changes.len(),
102            characters_added: 0,
103            characters_removed: 0,
104            formatting_time: start_time.elapsed(),
105        };
106
107        // Style information
108        let style_info = self.collect_style_information(&changes)?;
109
110        // Quality metrics
111        let quality = self.calculate_quality_metrics(&formatted_code)?;
112
113        Ok(FormattingResult {
114            formatted_circuit: formatted_code,
115            statistics,
116            changes,
117            style_information: style_info,
118            quality_metrics: quality,
119            duration: start_time.elapsed(),
120        })
121    }
122
123    /// Assess style compliance
124    pub fn assess_style_compliance(
125        &self,
126        style_info: &StyleInformation,
127    ) -> QuantRS2Result<StyleCompliance> {
128        let level = if style_info.compliance_score >= 0.9 {
129            ComplianceLevel::Excellent
130        } else if style_info.compliance_score >= 0.7 {
131            ComplianceLevel::Good
132        } else if style_info.compliance_score >= 0.5 {
133            ComplianceLevel::Fair
134        } else {
135            ComplianceLevel::Poor
136        };
137
138        Ok(StyleCompliance {
139            compliance_level: level,
140            issues: Vec::new(),
141            score: style_info.compliance_score,
142        })
143    }
144
145    fn analyze_code_structure(&self) -> QuantRS2Result<CodeStructure> {
146        Ok(CodeStructure::default())
147    }
148
149    fn optimize_layout(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
150        let optimizer = self.layout_optimizer.read().map_err(|_| {
151            quantrs2_core::error::QuantRS2Error::InvalidOperation(
152                "Failed to acquire layout optimizer lock".to_string(),
153            )
154        })?;
155        optimizer.optimize_layout(code, &self.config)
156    }
157
158    fn enforce_style(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
159        let enforcer = self.style_enforcer.read().map_err(|_| {
160            quantrs2_core::error::QuantRS2Error::InvalidOperation(
161                "Failed to acquire style enforcer lock".to_string(),
162            )
163        })?;
164        enforcer.enforce_style(code, &self.config)
165    }
166
167    fn organize_code(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
168        let organizer = self.code_organizer.read().map_err(|_| {
169            quantrs2_core::error::QuantRS2Error::InvalidOperation(
170                "Failed to acquire code organizer lock".to_string(),
171            )
172        })?;
173        organizer.organize_code(code, &self.config)
174    }
175
176    fn format_comments(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
177        let formatter = self.comment_formatter.read().map_err(|_| {
178            quantrs2_core::error::QuantRS2Error::InvalidOperation(
179                "Failed to acquire comment formatter lock".to_string(),
180            )
181        })?;
182        formatter.format_comments(code, &self.config)
183    }
184
185    fn manage_whitespace(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
186        let manager = self.whitespace_manager.read().map_err(|_| {
187            quantrs2_core::error::QuantRS2Error::InvalidOperation(
188                "Failed to acquire whitespace manager lock".to_string(),
189            )
190        })?;
191        manager.manage_whitespace(code, &self.config)
192    }
193
194    fn apply_alignment(&self, code: &CodeStructure) -> QuantRS2Result<Vec<FormattingChange>> {
195        let engine = self.alignment_engine.read().map_err(|_| {
196            quantrs2_core::error::QuantRS2Error::InvalidOperation(
197                "Failed to acquire alignment engine lock".to_string(),
198            )
199        })?;
200        engine.apply_alignment(code, &self.config)
201    }
202
203    fn build_formatted_code(
204        &self,
205        code: &CodeStructure,
206        _changes: &[FormattingChange],
207    ) -> QuantRS2Result<FormattedCircuit> {
208        let code = format!("// Formatted circuit with {N} qubits\n");
209        let line_count = code.lines().count();
210        let char_count = code.chars().count();
211
212        Ok(FormattedCircuit {
213            code,
214            line_count,
215            char_count,
216        })
217    }
218
219    const fn collect_style_information(
220        &self,
221        _changes: &[FormattingChange],
222    ) -> QuantRS2Result<StyleInformation> {
223        Ok(StyleInformation {
224            applied_rules: Vec::new(),
225            violations_fixed: Vec::new(),
226            compliance_score: 0.95,
227            consistency_metrics: ConsistencyMetrics {
228                naming_consistency: 0.95,
229                indentation_consistency: 0.95,
230                spacing_consistency: 0.95,
231                comment_consistency: 0.95,
232                overall_consistency: 0.95,
233            },
234        })
235    }
236
237    const fn calculate_quality_metrics(
238        &self,
239        code: &FormattedCircuit,
240    ) -> QuantRS2Result<QualityMetrics> {
241        Ok(QualityMetrics {
242            readability_score: 0.9,
243            maintainability_score: 0.9,
244            complexity_score: 0.8,
245            overall_quality: 0.87,
246        })
247    }
248}
249
250/// Layout optimizer
251pub struct LayoutOptimizer<const N: usize> {
252    // Internal state
253}
254
255impl<const N: usize> LayoutOptimizer<N> {
256    #[must_use]
257    pub const fn new() -> Self {
258        Self {}
259    }
260
261    pub fn optimize_layout(
262        &self,
263        code: &CodeStructure,
264        config: &FormatterConfig,
265    ) -> QuantRS2Result<Vec<FormattingChange>> {
266        // Emit a layout change whenever a section's reported span exceeds
267        // `max_line_length` — we cannot move gates around without the underlying
268        // AST, so we surface the breach as a `LineBreak` change positioned at
269        // the start of the offending section.
270        let max_len = config.max_line_length;
271        let mut changes = Vec::new();
272        for section in &code.sections {
273            if section.content.chars().count() > max_len {
274                changes.push(FormattingChange {
275                    change_type: ChangeType::LineBreak,
276                    start: Position {
277                        line: section.start_line,
278                        column: 0,
279                    },
280                    end: Position {
281                        line: section.end_line,
282                        column: max_len,
283                    },
284                    old_text: section.content.clone(),
285                    new_text: format!("// section '{}' exceeds {} cols\n", section.name, max_len),
286                });
287            }
288        }
289        Ok(changes)
290    }
291}
292
293impl<const N: usize> Default for LayoutOptimizer<N> {
294    fn default() -> Self {
295        Self::new()
296    }
297}
298
299/// Style enforcer
300pub struct StyleEnforcer<const N: usize> {
301    // Internal state
302}
303
304impl<const N: usize> StyleEnforcer<N> {
305    #[must_use]
306    pub const fn new() -> Self {
307        Self {}
308    }
309
310    pub fn enforce_style(
311        &self,
312        code: &CodeStructure,
313        config: &FormatterConfig,
314    ) -> QuantRS2Result<Vec<FormattingChange>> {
315        // Emit a `Spacing` change for sections that omit a space after commas
316        // when the spacing config requires it — this is a cheap heuristic that
317        // does not require any tokeniser yet still surfaces real violations
318        // when a `CodeStructure` is materialised.
319        let mut changes = Vec::new();
320        if !config.spacing.after_commas {
321            return Ok(changes);
322        }
323        for section in &code.sections {
324            if section.content.contains(",") && !section.content.contains(", ") {
325                changes.push(FormattingChange {
326                    change_type: ChangeType::Spacing,
327                    start: Position {
328                        line: section.start_line,
329                        column: 0,
330                    },
331                    end: Position {
332                        line: section.end_line,
333                        column: 0,
334                    },
335                    old_text: section.content.clone(),
336                    new_text: section.content.replace(",", ", "),
337                });
338            }
339        }
340        Ok(changes)
341    }
342}
343
344impl<const N: usize> Default for StyleEnforcer<N> {
345    fn default() -> Self {
346        Self::new()
347    }
348}
349
350/// Code organizer
351pub struct CodeOrganizer<const N: usize> {
352    // Internal state
353}
354
355impl<const N: usize> CodeOrganizer<N> {
356    #[must_use]
357    pub const fn new() -> Self {
358        Self {}
359    }
360
361    pub fn organize_code(
362        &self,
363        code: &CodeStructure,
364        config: &FormatterConfig,
365    ) -> QuantRS2Result<Vec<FormattingChange>> {
366        // If `section_ordering` is configured, emit one `Organization` change
367        // per section that is currently out-of-order relative to the desired
368        // ordering. This lets the caller materialise the moves later without
369        // mutating the structure here.
370        let order = &config.organization.section_ordering;
371        if order.is_empty() {
372            return Ok(Vec::new());
373        }
374        let rank = |name: &str| -> Option<usize> {
375            order.iter().position(|n| n.eq_ignore_ascii_case(name))
376        };
377        let mut changes = Vec::new();
378        let mut last_rank: Option<usize> = None;
379        for section in &code.sections {
380            let r = rank(&section.name);
381            if let (Some(prev), Some(cur)) = (last_rank, r) {
382                if cur < prev {
383                    changes.push(FormattingChange {
384                        change_type: ChangeType::Organization,
385                        start: Position {
386                            line: section.start_line,
387                            column: 0,
388                        },
389                        end: Position {
390                            line: section.end_line,
391                            column: 0,
392                        },
393                        old_text: section.name.clone(),
394                        new_text: format!(
395                            "move '{}' to position {} per section_ordering",
396                            section.name, cur
397                        ),
398                    });
399                }
400            }
401            if r.is_some() {
402                last_rank = r;
403            }
404        }
405        Ok(changes)
406    }
407}
408
409impl<const N: usize> Default for CodeOrganizer<N> {
410    fn default() -> Self {
411        Self::new()
412    }
413}
414
415/// Comment formatter
416pub struct CommentFormatter<const N: usize> {
417    state: CommentFormatterState,
418}
419
420impl<const N: usize> CommentFormatter<N> {
421    #[must_use]
422    pub fn new() -> Self {
423        Self {
424            state: CommentFormatterState {
425                rules: Vec::new(),
426                templates: HashMap::new(),
427                quality_threshold: 0.8,
428            },
429        }
430    }
431
432    pub fn format_comments(
433        &self,
434        code: &CodeStructure,
435        config: &FormatterConfig,
436    ) -> QuantRS2Result<Vec<FormattingChange>> {
437        // Estimate comment density per section and emit a `Comment` change
438        // when it falls below `target_comment_density`. This mirrors the
439        // existing comment configuration without rewriting any source.
440        let target = config.comments.target_comment_density;
441        let mut changes = Vec::new();
442        for section in &code.sections {
443            let total_lines = section.content.lines().count().max(1);
444            let comment_lines = section
445                .content
446                .lines()
447                .filter(|line| {
448                    let trimmed = line.trim_start();
449                    trimmed.starts_with("//") || trimmed.starts_with('#')
450                })
451                .count();
452            let density = comment_lines as f64 / total_lines as f64;
453            if density + f64::EPSILON < target {
454                changes.push(FormattingChange {
455                    change_type: ChangeType::Comment,
456                    start: Position {
457                        line: section.start_line,
458                        column: 0,
459                    },
460                    end: Position {
461                        line: section.end_line,
462                        column: 0,
463                    },
464                    old_text: section.content.clone(),
465                    new_text: format!(
466                        "// section '{}' has {:.2} comment density (< {:.2})\n",
467                        section.name, density, target
468                    ),
469                });
470            }
471        }
472        Ok(changes)
473    }
474}
475
476impl<const N: usize> Default for CommentFormatter<N> {
477    fn default() -> Self {
478        Self::new()
479    }
480}
481
482/// Whitespace manager
483pub struct WhitespaceManager<const N: usize> {
484    rules: Vec<WhitespaceRule>,
485    current_state: WhitespaceState,
486    optimization: WhitespaceOptimization,
487}
488
489#[derive(Debug, Clone)]
490struct WhitespaceRule {
491    name: String,
492    pattern: String,
493}
494
495impl<const N: usize> WhitespaceManager<N> {
496    #[must_use]
497    pub const fn new() -> Self {
498        Self {
499            rules: Vec::new(),
500            current_state: WhitespaceState {
501                indentation_level: 0,
502                line_length: 0,
503                pending_changes: Vec::new(),
504                statistics: WhitespaceStatistics {
505                    total_whitespace: 0,
506                    indentation_chars: 0,
507                    spacing_chars: 0,
508                    line_breaks: 0,
509                    consistency_score: 1.0,
510                },
511            },
512            optimization: WhitespaceOptimization {
513                remove_trailing: true,
514                normalize_indentation: true,
515                optimize_line_breaks: true,
516                compress_empty_lines: true,
517                target_compression: 0.1,
518            },
519        }
520    }
521
522    pub fn manage_whitespace(
523        &self,
524        code: &CodeStructure,
525        _config: &FormatterConfig,
526    ) -> QuantRS2Result<Vec<FormattingChange>> {
527        // Detect trailing whitespace and emit `Spacing` changes that strip it.
528        // We honour `WhitespaceOptimization::remove_trailing` from our own
529        // optimisation state because the caller-side `FormatterConfig` does
530        // not expose a per-section toggle.
531        let mut changes = Vec::new();
532        if !self.optimization.remove_trailing {
533            return Ok(changes);
534        }
535        for section in &code.sections {
536            for (offset, line) in section.content.lines().enumerate() {
537                let stripped = line.trim_end();
538                if stripped.len() != line.len() {
539                    let line_no = section.start_line + offset;
540                    changes.push(FormattingChange {
541                        change_type: ChangeType::Spacing,
542                        start: Position {
543                            line: line_no,
544                            column: stripped.len(),
545                        },
546                        end: Position {
547                            line: line_no,
548                            column: line.len(),
549                        },
550                        old_text: line.to_string(),
551                        new_text: stripped.to_string(),
552                    });
553                }
554            }
555        }
556        Ok(changes)
557    }
558}
559
560impl<const N: usize> Default for WhitespaceManager<N> {
561    fn default() -> Self {
562        Self::new()
563    }
564}
565
566/// Alignment engine
567pub struct AlignmentEngine<const N: usize> {
568    rules: Vec<AlignmentRule>,
569    current_state: AlignmentState,
570    optimization: AlignmentOptimization,
571}
572
573#[derive(Debug, Clone)]
574struct AlignmentRule {
575    name: String,
576    column: usize,
577}
578
579impl<const N: usize> AlignmentEngine<N> {
580    #[must_use]
581    pub const fn new() -> Self {
582        Self {
583            rules: Vec::new(),
584            current_state: AlignmentState {
585                active_alignments: Vec::new(),
586                alignment_columns: Vec::new(),
587                statistics: AlignmentStatistics {
588                    total_alignments: 0,
589                    successful_alignments: 0,
590                    average_quality: 0.0,
591                    consistency_score: 1.0,
592                },
593            },
594            optimization: AlignmentOptimization {
595                auto_detect: true,
596                quality_threshold: 0.8,
597                max_distance: 10,
598                prefer_compact: true,
599            },
600        }
601    }
602
603    pub fn apply_alignment(
604        &self,
605        code: &CodeStructure,
606        config: &FormatterConfig,
607    ) -> QuantRS2Result<Vec<FormattingChange>> {
608        // Emit one `Alignment` change per section whose lines differ in their
609        // indentation prefix length — a strong signal that an alignment pass
610        // would be useful. The threshold and column count come from the
611        // configured `AlignmentConfig`.
612        let threshold = config.alignment.column_alignment_threshold.max(1);
613        let mut changes = Vec::new();
614        for section in &code.sections {
615            let mut indents: Vec<usize> = section
616                .content
617                .lines()
618                .filter(|line| !line.trim().is_empty())
619                .map(|line| line.len() - line.trim_start().len())
620                .collect();
621            if indents.len() < threshold {
622                continue;
623            }
624            indents.sort_unstable();
625            let min = indents.first().copied().unwrap_or(0);
626            let max = indents.last().copied().unwrap_or(0);
627            if max > min {
628                changes.push(FormattingChange {
629                    change_type: ChangeType::Alignment,
630                    start: Position {
631                        line: section.start_line,
632                        column: min,
633                    },
634                    end: Position {
635                        line: section.end_line,
636                        column: max,
637                    },
638                    old_text: section.content.clone(),
639                    new_text: format!("// align to column {min} for section '{}'\n", section.name),
640                });
641            }
642        }
643        Ok(changes)
644    }
645}
646
647impl<const N: usize> Default for AlignmentEngine<N> {
648    fn default() -> Self {
649        Self::new()
650    }
651}