AkurAI Build
Menu

cosmic-mobile

public

Latest change 760f88a8b46f92fbb34539f97d56ba639eaeb8c4 - Sort calendar agenda by date without rewriting records by AkurAI Build

use std::{io, path::PathBuf};
pub struct Store {
    pub rows: Vec<(String, String, String, String)>,
    path: PathBuf,
}
impl Store {
    pub fn for_app(&self, app: &str) -> Vec<&(String, String, String, String)> {
        let mut rows: Vec<_> = self.rows.iter().filter(|r| r.0 == app).collect();
        if app == "Calendar" {
            rows.sort_by(|a, b| a.1.cmp(&b.1));
        }
        rows
    }
    pub fn load(path: PathBuf) -> io::Result<Self> {
        let text = match std::fs::read_to_string(&path) {
            Ok(text) => text,
            Err(e) if e.kind() == io::ErrorKind::NotFound => String::new(),
            Err(e) => return Err(e),
        };
        let mut rows = Vec::new();
        for line in text.lines() {
            let fields: Vec<_> = line.split('\t').collect();
            if fields.len() != 4 {
                return Err(io::Error::other("Invalid records; file preserved"));
            }
            rows.push((
                fields[0].into(),
                fields[1].into(),
                fields[2].into(),
                fields[3].into(),
            ));
        }
        Ok(Self { rows, path })
    }
    pub fn add(&mut self, app: &str, first: &str, second: &str) -> io::Result<()> {
        let first = first.trim();
        let second = second.trim();
        if !["Phone", "Contacts", "Calendar", "Messages"].contains(&app)
            || [first, second]
                .iter()
                .any(|v| v.is_empty() || v.len() > 4096 || v.chars().any(char::is_control))
        {
            return Err(io::Error::other(
                "Enter both fields; control characters are not allowed",
            ));
        }
        let number = match app {
            "Contacts" => Some(second),
            "Phone" | "Messages" => Some(first),
            _ => None,
        };
        if let Some(number) = number {
            if number.len() > 64
                || !number.bytes().any(|b| b.is_ascii_digit())
                || !number
                    .bytes()
                    .all(|b| b.is_ascii_digit() || b"+ -()*#".contains(&b))
            {
                return Err(io::Error::other("Enter a valid phone number"));
            }
        }
        if app == "Calendar" {
            let parts: Vec<_> = first.split('-').collect();
            let date: Vec<u32> = parts.iter().filter_map(|v| v.parse().ok()).collect();
            if parts.len() != 3
                || parts.iter().map(|s| s.len()).collect::<Vec<_>>() != [4, 2, 2]
                || date.len() != 3
            {
                return Err(io::Error::other("Use YYYY-MM-DD"));
            }
            let (y, m, d) = (date[0], date[1], date[2]);
            let leap = y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
            let days = [
                31,
                if leap { 29 } else { 28 },
                31,
                30,
                31,
                30,
                31,
                31,
                30,
                31,
                30,
                31,
            ];
            if y == 0 || !(1..=12).contains(&m) || d == 0 || d > days[(m - 1) as usize] {
                return Err(io::Error::other("Invalid calendar date"));
            }
        }
        let status = match app {
            "Phone" => "Simulated — no call placed",
            "Messages" => "Simulated — not sent",
            _ => "Saved locally",
        };
        let mut rows = self.rows.clone();
        rows.push((app.into(), first.into(), second.into(), status.into()));
        let data: String = rows
            .iter()
            .map(|r| format!("{}\t{}\t{}\t{}\n", r.0, r.1, r.2, r.3))
            .collect();
        if let Some(parent) = self.path.parent() {
            std::fs::create_dir_all(parent)?;
        }
        let tmp = self.path.with_extension("tmp");
        std::fs::write(&tmp, data)?;
        std::fs::rename(tmp, &self.path)?;
        self.rows = rows;
        Ok(())
    }
}