Skip to main content

stout_install/
receipt.rs

1//! INSTALL_RECEIPT.json handling
2
3use crate::error::Result;
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7/// INSTALL_RECEIPT.json structure (Homebrew compatible)
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct InstallReceipt {
10    pub homebrew_version: String,
11    pub installed_as_dependency: bool,
12    pub installed_on_request: bool,
13    pub install_time: u64,
14    pub source: ReceiptSource,
15    #[serde(default)]
16    pub runtime_dependencies: Vec<RuntimeDependency>,
17    #[serde(default)]
18    pub poured_from_bottle: bool,
19    #[serde(default)]
20    pub changed_files: Vec<String>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ReceiptSource {
25    pub tap: String,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub path: Option<String>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct RuntimeDependency {
32    pub full_name: String,
33    pub version: String,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub revision: Option<u32>,
36}
37
38impl InstallReceipt {
39    /// Create a new receipt for a bottle installation
40    pub fn new_bottle(
41        tap: &str,
42        on_request: bool,
43        dependencies: Vec<RuntimeDependency>,
44    ) -> Self {
45        let now = std::time::SystemTime::now()
46            .duration_since(std::time::UNIX_EPOCH)
47            .unwrap()
48            .as_secs();
49
50        Self {
51            homebrew_version: "4.0.0".to_string(), // Compatible version
52            installed_as_dependency: !on_request,
53            installed_on_request: on_request,
54            install_time: now,
55            source: ReceiptSource {
56                tap: tap.to_string(),
57                path: None,
58            },
59            runtime_dependencies: dependencies,
60            poured_from_bottle: true,
61            changed_files: Vec::new(),
62        }
63    }
64
65    /// Create a new receipt for a source-built installation
66    pub fn new_source(
67        tap: &str,
68        on_request: bool,
69        dependencies: Vec<RuntimeDependency>,
70    ) -> Self {
71        let now = std::time::SystemTime::now()
72            .duration_since(std::time::UNIX_EPOCH)
73            .unwrap()
74            .as_secs();
75
76        Self {
77            homebrew_version: "4.0.0".to_string(),
78            installed_as_dependency: !on_request,
79            installed_on_request: on_request,
80            install_time: now,
81            source: ReceiptSource {
82                tap: tap.to_string(),
83                path: None,
84            },
85            runtime_dependencies: dependencies,
86            poured_from_bottle: false, // Built from source
87            changed_files: Vec::new(),
88        }
89    }
90}
91
92/// Write an INSTALL_RECEIPT.json file
93pub fn write_receipt(install_path: impl AsRef<Path>, receipt: &InstallReceipt) -> Result<()> {
94    let path = install_path.as_ref().join("INSTALL_RECEIPT.json");
95    let json = serde_json::to_string_pretty(receipt)?;
96    std::fs::write(&path, json)?;
97    Ok(())
98}
99
100/// Read an existing INSTALL_RECEIPT.json
101pub fn read_receipt(install_path: impl AsRef<Path>) -> Result<InstallReceipt> {
102    let path = install_path.as_ref().join("INSTALL_RECEIPT.json");
103    let json = std::fs::read_to_string(&path)?;
104    let receipt: InstallReceipt = serde_json::from_str(&json)?;
105    Ok(receipt)
106}