interpolize 5.0.1

a rust program that scrapes discord, learns how your friends talk, and generates new messages in their collective voice. yes, this is what we're doing with our lives.
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Message {
    pub id: String,
    pub channel_id: String,
    pub channel_name: String,
    pub author_id: String,
    pub author_name: String,
    pub content: String,
    pub timestamp: String,
    pub reply_to: String,
}

#[derive(Serialize, Deserialize, Default)]
struct Store {
    schema_version: u32,
    messages: Vec<Message>,
}

pub fn load(path: &str) -> Result<Vec<Message>> {
    if !std::path::Path::new(path).exists() {
        return Ok(vec![]);
    }
    let raw = fs::read_to_string(path)?;
    let store: Store = toml::from_str(&raw)?;
    Ok(store.messages)
}

pub fn save(path: &str, messages: &[Message]) -> Result<()> {
    let store = Store {
        schema_version: 1,
        messages: messages.to_vec(),
    };
    let raw = toml::to_string(&store)?;
    fs::write(path, raw)?;
    Ok(())
}

pub fn merge(existing: &mut Vec<Message>, incoming: Vec<Message>) {
    let known: std::collections::HashSet<String> =
        existing.iter().map(|m| m.id.clone()).collect();
    for msg in incoming {
        if !known.contains(&msg.id) {
            existing.push(msg);
        }
    }
}

pub fn thread(msg_id: &str, messages: &[Message], depth: usize) -> Vec<Message> {
    let mut chain = vec![];
    let mut current_id = msg_id.to_string();
    for _ in 0..depth {
        let found = messages.iter().find(|m| m.id == current_id);
        match found {
            Some(m) => {
                chain.push(m.clone());
                if m.reply_to.is_empty() { break; }
                current_id = m.reply_to.clone();
            }
            None => break,
        }
    }
    chain.reverse();
    chain
}