use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Transcript {
pub transcript_content: TranscriptContent,
pub transcript_metadata: TranscriptMetadata,
}
impl Transcript {
pub fn text(&self) -> &str {
self.transcript_content
.transcript
.as_ref()
.map(|t| t.text.as_str())
.unwrap_or("")
}
pub fn quarter(&self) -> &str {
&self.transcript_metadata.fiscal_period
}
pub fn year(&self) -> i32 {
self.transcript_metadata.fiscal_year
}
pub fn speaker_name(&self, speaker_id: i32) -> Option<&str> {
self.transcript_content
.speaker_mapping
.iter()
.find(|s| s.speaker == speaker_id)
.map(|s| s.speaker_data.name.as_str())
}
pub fn paragraphs_with_speakers(&self) -> Vec<(&Paragraph, Option<&str>)> {
self.transcript_content
.transcript
.as_ref()
.map(|t| {
t.paragraphs
.iter()
.map(|p| (p, self.speaker_name(p.speaker)))
.collect()
})
.unwrap_or_default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TranscriptContent {
pub company_id: i64,
pub event_id: i64,
#[serde(default)]
pub version: Option<String>,
#[serde(default)]
pub speaker_mapping: Vec<SpeakerMapping>,
#[serde(default)]
pub transcript: Option<TranscriptData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpeakerMapping {
pub speaker: i32,
pub speaker_data: SpeakerData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpeakerData {
#[serde(default)]
pub company: Option<String>,
#[serde(default)]
pub name: String,
#[serde(default)]
pub role: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TranscriptData {
#[serde(default)]
pub number_of_speakers: i32,
#[serde(default)]
pub text: String,
#[serde(default)]
pub paragraphs: Vec<Paragraph>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Paragraph {
#[serde(default)]
pub speaker: i32,
#[serde(default)]
pub start: f64,
#[serde(default)]
pub end: f64,
#[serde(default)]
pub text: String,
#[serde(default)]
pub sentences: Vec<Sentence>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sentence {
#[serde(default)]
pub start: f64,
#[serde(default)]
pub end: f64,
#[serde(default)]
pub text: String,
#[serde(default)]
pub words: Vec<Word>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Word {
#[serde(default)]
pub word: String,
#[serde(default)]
pub punctuated_word: String,
#[serde(default)]
pub start: f64,
#[serde(default)]
pub end: f64,
#[serde(default)]
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptMetadata {
#[serde(default)]
pub date: i64,
#[serde(default)]
pub event_id: i64,
#[serde(default)]
pub event_type: String,
#[serde(default)]
pub fiscal_period: String,
#[serde(default)]
pub fiscal_year: i32,
#[serde(default)]
pub is_latest: bool,
#[serde(default)]
pub s3_url: String,
#[serde(default)]
pub title: String,
#[serde(default)]
pub transcript_id: i64,
#[serde(default, rename = "type")]
pub transcript_type: String,
#[serde(default)]
pub updated: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TranscriptWithMeta {
pub event_id: String,
pub quarter: Option<String>,
pub year: Option<i32>,
pub title: String,
pub url: String,
pub transcript: Transcript,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_transcript() {
let json = r#"{
"transcriptContent": {
"company_id": 4742,
"event_id": 369370,
"version": "1.0.0",
"speaker_mapping": [
{
"speaker": 0,
"speaker_data": {
"company": "Apple",
"name": "Tim Cook",
"role": "CEO"
}
}
],
"transcript": {
"number_of_speakers": 15,
"text": "Hello everyone...",
"paragraphs": []
}
},
"transcriptMetadata": {
"date": 1761858000,
"eventId": 369370,
"eventType": "Earnings Call",
"fiscalPeriod": "Q4",
"fiscalYear": 2025,
"isLatest": true,
"title": "Q4 2025"
}
}"#;
let transcript: Transcript = serde_json::from_str(json).unwrap();
assert_eq!(transcript.transcript_content.company_id, 4742);
assert_eq!(transcript.quarter(), "Q4");
assert_eq!(transcript.year(), 2025);
assert_eq!(transcript.speaker_name(0), Some("Tim Cook"));
}
}