Skip to main content

sakurs_core/api/
output.rs

1//! Output types for unified API
2
3use std::time::Duration;
4
5/// Processing output with rich metadata
6#[derive(Debug, Clone)]
7pub struct Output {
8    /// Sentence boundaries found
9    pub boundaries: Vec<Boundary>,
10    /// Processing metadata
11    pub metadata: ProcessingMetadata,
12}
13
14/// A sentence boundary with detailed information
15#[derive(Debug, Clone)]
16pub struct Boundary {
17    /// Byte offset in the original text
18    pub offset: usize,
19    /// Character offset in the original text
20    pub char_offset: usize,
21}
22
23/// Metadata about the processing
24#[derive(Debug, Clone)]
25pub struct ProcessingMetadata {
26    /// Total processing duration
27    pub duration: Duration,
28    /// Strategy used for processing
29    pub strategy_used: String,
30    /// Number of chunks processed
31    pub chunks_processed: usize,
32    /// Additional statistics
33    pub stats: ProcessingStats,
34}
35
36/// Additional processing statistics
37#[derive(Debug, Clone)]
38pub struct ProcessingStats {
39    /// Total bytes processed
40    pub bytes_processed: usize,
41    /// Total characters processed
42    pub chars_processed: usize,
43    /// Number of sentences found
44    pub sentence_count: usize,
45    /// Average sentence length in characters
46    pub avg_sentence_length: f32,
47}
48
49impl Output {
50    /// Create output from delta stack processing result
51    pub(crate) fn from_delta_stack_result(
52        result: crate::application::DeltaStackResult,
53        text: &str,
54        duration: Duration,
55    ) -> Self {
56        // Calculate character offsets for each byte boundary (and the total
57        // character count, avoiding a second full pass over the text)
58        let (char_boundaries, total_chars) = Self::calculate_char_offsets(text, &result.boundaries);
59
60        let boundaries = result
61            .boundaries
62            .into_iter()
63            .zip(char_boundaries)
64            .map(|(offset, char_offset)| Boundary {
65                offset,
66                char_offset,
67            })
68            .collect::<Vec<_>>();
69
70        let sentence_count = boundaries.len();
71        let avg_sentence_length = if sentence_count > 0 {
72            total_chars as f32 / sentence_count as f32
73        } else {
74            0.0
75        };
76
77        // Determine strategy used based on thread count
78        let strategy_used = if result.thread_count > 1 {
79            format!("parallel ({} threads)", result.thread_count)
80        } else {
81            "sequential".to_string()
82        };
83
84        Self {
85            boundaries,
86            metadata: ProcessingMetadata {
87                duration,
88                strategy_used,
89                chunks_processed: result.chunk_count,
90                stats: ProcessingStats {
91                    bytes_processed: text.len(),
92                    chars_processed: total_chars,
93                    sentence_count,
94                    avg_sentence_length,
95                },
96            },
97        }
98    }
99
100    /// Calculate character offsets from byte offsets.
101    ///
102    /// `byte_offsets` must be sorted ascending and lie on character
103    /// boundaries (guaranteed by the boundary merge step). Counting each
104    /// inter-boundary segment with the standard library's optimized
105    /// word-at-a-time counter is much faster than a char-by-char walk.
106    /// Returns the character offset for each byte offset plus the total
107    /// character count.
108    fn calculate_char_offsets(text: &str, byte_offsets: &[usize]) -> (Vec<usize>, usize) {
109        let mut char_offsets = Vec::with_capacity(byte_offsets.len());
110        let mut chars = 0usize;
111        let mut prev = 0usize;
112        for &off in byte_offsets {
113            chars += text[prev..off].chars().count();
114            char_offsets.push(chars);
115            prev = off;
116        }
117        let total = chars + text[prev..].chars().count();
118        (char_offsets, total)
119    }
120}