use crate::application::ports::DictSource; use crate::core::entities::{DictEntry, DictEntryId}; use serde::Deserialize; use std::fs::File; use std::io::BufReader; use std::path::Path; // The "Wire Format". // It exists ONLY here to map external JSON names to internal Entity names. #[derive(Deserialize)] struct JsonEntry { id: u32, word: String, // "word" in JSON, "text" in Entity // If JSON has extra fields we don't care about, Serde ignores them. } pub struct JsonFileDictSource { // Helper iterator from Serde iter: serde_json::StreamDeserializer<'static, serde_json::de::IoRead>, JsonEntry>, } impl JsonFileDictSource { pub fn new>(path: P) -> anyhow::Result { let file = File::open(path)?; let reader = BufReader::new(file); let iter = serde_json::Deserializer::from_reader(reader).into_iter::(); Ok(Self { iter }) } } impl DictSource for JsonFileDictSource { fn next_entry(&mut self) -> Option> { self.iter.next().map(|res| { match res { Ok(json) => { // MAPPING HAPPENS HERE. // This is type-safe. If DictEntry::new signature changes, this breaks. Ok(DictEntry::new(json.id as DictEntryId, json.word)) } Err(e) => Err(anyhow::Error::new(e)), } }) } }