Skip to main content

transcribe_cpp/
result.rs

1//! Owned result types.
2//!
3//! Every result is fully materialized into these types — all text is copied at
4//! the FFI boundary (`CStr -> String`), so no borrowed session pointer ever
5//! escapes to user code (the "copy at the FFI boundary" rule). Holding a
6//! [`Transcript`] across the next `run`/`stream` call is therefore always safe.
7
8use std::ffi::CStr;
9use std::os::raw::c_char;
10
11use transcribe_cpp_sys as sys;
12
13use crate::types::TimestampKind;
14
15/// A fully-materialized transcription result.
16#[derive(Debug, Clone, Default, PartialEq)]
17pub struct Transcript {
18    /// The full transcript text.
19    pub text: String,
20    /// The model-detected language (ISO code), if it predicted one. `None`
21    /// when a language hint was given, the model lacks LID, or none applies.
22    pub language: Option<String>,
23    /// The finest timestamp granularity actually populated below.
24    pub timestamp_kind: TimestampKind,
25    /// Segment rows (empty unless segment-or-finer timestamps were produced).
26    pub segments: Vec<Segment>,
27    /// Word rows (empty unless word-or-finer timestamps were produced).
28    pub words: Vec<Word>,
29    /// Token rows (empty unless token timestamps were produced).
30    pub tokens: Vec<Token>,
31    /// Stage timings for the run.
32    pub timings: Timings,
33}
34
35/// One segment row. Times are milliseconds relative to the original audio.
36#[derive(Debug, Clone, Default, PartialEq)]
37pub struct Segment {
38    pub t0_ms: i64,
39    pub t1_ms: i64,
40    pub first_word: i32,
41    pub n_words: i32,
42    pub first_token: i32,
43    pub n_tokens: i32,
44    pub text: String,
45}
46
47/// One word row.
48#[derive(Debug, Clone, Default, PartialEq)]
49pub struct Word {
50    pub t0_ms: i64,
51    pub t1_ms: i64,
52    pub seg_index: i32,
53    pub first_token: i32,
54    pub n_tokens: i32,
55    pub text: String,
56}
57
58/// One token row. `p` is a per-token confidence hint (NaN when the family
59/// produces none); its exact meaning is family-specific.
60#[derive(Debug, Clone, Default, PartialEq)]
61pub struct Token {
62    pub id: i32,
63    pub p: f32,
64    pub t0_ms: i64,
65    pub t1_ms: i64,
66    pub seg_index: i32,
67    pub word_index: i32,
68    pub text: String,
69}
70
71/// Per-run stage timings, in milliseconds. Zero means "unknown / not measured".
72#[derive(Debug, Clone, Copy, Default, PartialEq)]
73pub struct Timings {
74    pub load_ms: f32,
75    pub mel_ms: f32,
76    pub encode_ms: f32,
77    pub decode_ms: f32,
78}
79
80/// Copy a borrowed C string into an owned `String` (empty for NULL).
81pub(crate) fn owned_str(ptr: *const c_char) -> String {
82    if ptr.is_null() {
83        return String::new();
84    }
85    unsafe { CStr::from_ptr(ptr) }
86        .to_string_lossy()
87        .into_owned()
88}
89
90/// Copy a borrowed C string into `Some(String)`, or `None` for NULL/empty.
91pub(crate) fn owned_opt_str(ptr: *const c_char) -> Option<String> {
92    if ptr.is_null() {
93        return None;
94    }
95    let s = unsafe { CStr::from_ptr(ptr) }
96        .to_string_lossy()
97        .into_owned();
98    if s.is_empty() {
99        None
100    } else {
101        Some(s)
102    }
103}
104
105impl Segment {
106    pub(crate) fn from_raw(raw: &sys::transcribe_segment) -> Self {
107        Segment {
108            t0_ms: raw.t0_ms,
109            t1_ms: raw.t1_ms,
110            first_word: raw.first_word,
111            n_words: raw.n_words,
112            first_token: raw.first_token,
113            n_tokens: raw.n_tokens,
114            text: owned_str(raw.text),
115        }
116    }
117}
118
119impl Word {
120    pub(crate) fn from_raw(raw: &sys::transcribe_word) -> Self {
121        Word {
122            t0_ms: raw.t0_ms,
123            t1_ms: raw.t1_ms,
124            seg_index: raw.seg_index,
125            first_token: raw.first_token,
126            n_tokens: raw.n_tokens,
127            text: owned_str(raw.text),
128        }
129    }
130}
131
132impl Token {
133    pub(crate) fn from_raw(raw: &sys::transcribe_token) -> Self {
134        Token {
135            id: raw.id,
136            p: raw.p,
137            t0_ms: raw.t0_ms,
138            t1_ms: raw.t1_ms,
139            seg_index: raw.seg_index,
140            word_index: raw.word_index,
141            text: owned_str(raw.text),
142        }
143    }
144}
145
146impl Timings {
147    pub(crate) fn from_raw(raw: &sys::transcribe_timings) -> Self {
148        Timings {
149            load_ms: raw.load_ms,
150            mel_ms: raw.mel_ms,
151            encode_ms: raw.encode_ms,
152            decode_ms: raw.decode_ms,
153        }
154    }
155}