Skip to main content

gitsheets/hash/
mod.rs

1// git-sheets: Hash module - pure hash computation logic
2// A tool for Excel sufferers who deserve better
3
4use serde::{Deserialize, Serialize};
5use sha2::{Digest, Sha256};
6use std::collections::HashMap;
7
8// Re-export from core module
9pub use crate::core::Table;
10
11/// Hashes for verifying table integrity
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TableHashes {
14    /// Hash of the entire table (quick integrity check)
15    pub table_hash: String,
16    /// Per-header hashes (column-level verification)
17    pub header_hashes: HashMap<String, String>,
18    /// Optional: per-row hashes (fine-grained verification)
19    pub row_hashes: Option<Vec<String>>,
20}
21
22impl TableHashes {
23    /// Compute all hashes for a table
24    pub fn compute(table: &Table) -> Self {
25        let mut header_hashes = HashMap::new();
26
27        // Hash each column
28        for (idx, header) in table.headers.iter().enumerate() {
29            let column_data: Vec<&str> = table
30                .rows
31                .iter()
32                .map(|row| row.get(idx).map(|s| s.as_str()).unwrap_or(""))
33                .collect();
34
35            let hash = Self::hash_column(header, &column_data);
36            header_hashes.insert(header.clone(), hash);
37        }
38
39        // Hash entire table
40        let table_hash = Self::hash_table(&table.headers, &table.rows);
41
42        // Optional: per-row hashes
43        let row_hashes = Some(table.rows.iter().map(|row| Self::hash_row(row)).collect());
44
45        Self {
46            table_hash,
47            header_hashes,
48            row_hashes,
49        }
50    }
51
52    /// Hash a single column
53    fn hash_column(header: &str, data: &[&str]) -> String {
54        let mut hasher = Sha256::new();
55        hasher.update(header.as_bytes());
56        for value in data {
57            hasher.update(value.as_bytes());
58        }
59        format!("{:x}", hasher.finalize())
60    }
61
62    /// Hash a single row
63    fn hash_row(row: &[String]) -> String {
64        let mut hasher = Sha256::new();
65        for cell in row {
66            hasher.update(cell.as_bytes());
67        }
68        format!("{:x}", hasher.finalize())
69    }
70
71    /// Hash the entire table (headers + all rows)
72    fn hash_table(headers: &[String], rows: &[Vec<String>]) -> String {
73        let mut hasher = Sha256::new();
74
75        // Hash headers
76        for h in headers {
77            hasher.update(h.as_bytes());
78        }
79
80        // Hash all row data
81        for row in rows {
82            for cell in row {
83                hasher.update(cell.as_bytes());
84            }
85        }
86
87        format!("{:x}", hasher.finalize())
88    }
89}