codex/capabilities/
snapshot.rs1use serde::{de::DeserializeOwned, Serialize};
2use std::fs as std_fs;
3use std::path::Path;
4
5use super::{
6 capability_cache_key, current_fingerprint, fingerprints_match, has_fingerprint_metadata,
7 CapabilityOverrides, CapabilitySnapshotError, CapabilitySnapshotFormat, CodexCapabilities,
8};
9
10pub fn serialize_capabilities_snapshot(
12 snapshot: &CodexCapabilities,
13 format: CapabilitySnapshotFormat,
14) -> Result<String, CapabilitySnapshotError> {
15 serialize_snapshot(snapshot, format)
16}
17
18pub fn deserialize_capabilities_snapshot(
20 input: &str,
21 format: CapabilitySnapshotFormat,
22) -> Result<CodexCapabilities, CapabilitySnapshotError> {
23 deserialize_snapshot(input, format)
24}
25
26pub fn write_capabilities_snapshot(
28 path: impl AsRef<Path>,
29 snapshot: &CodexCapabilities,
30 format: Option<CapabilitySnapshotFormat>,
31) -> Result<(), CapabilitySnapshotError> {
32 let path = path.as_ref();
33 let resolved_format = resolve_snapshot_format(format, path)?;
34 let contents = serialize_capabilities_snapshot(snapshot, resolved_format)?;
35 std_fs::write(path, contents).map_err(|source| CapabilitySnapshotError::WriteSnapshot {
36 path: path.to_path_buf(),
37 source,
38 })
39}
40
41pub fn read_capabilities_snapshot(
43 path: impl AsRef<Path>,
44 format: Option<CapabilitySnapshotFormat>,
45) -> Result<CodexCapabilities, CapabilitySnapshotError> {
46 let path = path.as_ref();
47 let resolved_format = resolve_snapshot_format(format, path)?;
48 let contents =
49 std_fs::read_to_string(path).map_err(|source| CapabilitySnapshotError::ReadSnapshot {
50 path: path.to_path_buf(),
51 source,
52 })?;
53 deserialize_capabilities_snapshot(&contents, resolved_format)
54}
55
56pub fn serialize_capability_overrides(
58 overrides: &CapabilityOverrides,
59 format: CapabilitySnapshotFormat,
60) -> Result<String, CapabilitySnapshotError> {
61 serialize_snapshot(overrides, format)
62}
63
64pub fn deserialize_capability_overrides(
66 input: &str,
67 format: CapabilitySnapshotFormat,
68) -> Result<CapabilityOverrides, CapabilitySnapshotError> {
69 deserialize_snapshot(input, format)
70}
71
72pub fn write_capability_overrides(
74 path: impl AsRef<Path>,
75 overrides: &CapabilityOverrides,
76 format: Option<CapabilitySnapshotFormat>,
77) -> Result<(), CapabilitySnapshotError> {
78 let path = path.as_ref();
79 let resolved_format = resolve_snapshot_format(format, path)?;
80 let contents = serialize_capability_overrides(overrides, resolved_format)?;
81 std_fs::write(path, contents).map_err(|source| CapabilitySnapshotError::WriteSnapshot {
82 path: path.to_path_buf(),
83 source,
84 })
85}
86
87pub fn read_capability_overrides(
89 path: impl AsRef<Path>,
90 format: Option<CapabilitySnapshotFormat>,
91) -> Result<CapabilityOverrides, CapabilitySnapshotError> {
92 let path = path.as_ref();
93 let resolved_format = resolve_snapshot_format(format, path)?;
94 let contents =
95 std_fs::read_to_string(path).map_err(|source| CapabilitySnapshotError::ReadSnapshot {
96 path: path.to_path_buf(),
97 source,
98 })?;
99 deserialize_capability_overrides(&contents, resolved_format)
100}
101
102pub fn capability_snapshot_matches_binary(snapshot: &CodexCapabilities, binary: &Path) -> bool {
107 let cache_key = capability_cache_key(binary);
108 if snapshot.cache_key != cache_key {
109 return false;
110 }
111 let current = current_fingerprint(&cache_key);
112 has_fingerprint_metadata(&snapshot.fingerprint)
113 && has_fingerprint_metadata(¤t)
114 && fingerprints_match(&snapshot.fingerprint, ¤t)
115}
116
117fn serialize_snapshot<T: Serialize>(
118 value: &T,
119 format: CapabilitySnapshotFormat,
120) -> Result<String, CapabilitySnapshotError> {
121 match format {
122 CapabilitySnapshotFormat::Json => serde_json::to_string_pretty(value)
123 .map_err(|source| CapabilitySnapshotError::JsonEncode { source }),
124 CapabilitySnapshotFormat::Toml => toml::to_string_pretty(value)
125 .map_err(|source| CapabilitySnapshotError::TomlEncode { source }),
126 }
127}
128
129fn deserialize_snapshot<T: DeserializeOwned>(
130 input: &str,
131 format: CapabilitySnapshotFormat,
132) -> Result<T, CapabilitySnapshotError> {
133 match format {
134 CapabilitySnapshotFormat::Json => serde_json::from_str(input)
135 .map_err(|source| CapabilitySnapshotError::JsonDecode { source }),
136 CapabilitySnapshotFormat::Toml => {
137 toml::from_str(input).map_err(|source| CapabilitySnapshotError::TomlDecode { source })
138 }
139 }
140}
141
142fn resolve_snapshot_format(
143 format: Option<CapabilitySnapshotFormat>,
144 path: &Path,
145) -> Result<CapabilitySnapshotFormat, CapabilitySnapshotError> {
146 format
147 .or_else(|| CapabilitySnapshotFormat::from_path(path))
148 .ok_or_else(|| CapabilitySnapshotError::UnsupportedFormat {
149 path: path.to_path_buf(),
150 })
151}