1pub mod multi_target;
2
3use crate::ast::*;
4use crate::options::SerializationFormat;
5use crate::transform::TransformCtx;
6use serde::{Deserialize, Deserializer, Serialize};
7use serde_state::{DeserializeState, SerializeState};
8use std::fs::File;
9use std::io::{BufReader, BufWriter, Read};
10use std::path::{Path, PathBuf};
11
12#[derive(SerializeState, DeserializeState)]
15pub struct CrateData {
16 #[serde_state(stateless)]
19 pub charon_version: CharonVersion,
20 pub translated: TranslatedCrate,
21 #[serde_state(stateless)]
22 #[charon::opaque] pub has_errors: bool,
25}
26
27impl Serialize for CrateData {
28 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
29 where
30 S: serde::Serializer,
31 {
32 if self.translated.options.no_dedup_serialized_ast {
33 self.serialize_state(&(), serializer)
34 } else {
35 let state = HashConsDedupSerializer::default();
36 self.serialize_state(&state, serializer)
37 }
38 }
39}
40
41impl<'de> Deserialize<'de> for CrateData {
42 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43 where
44 D: Deserializer<'de>,
45 {
46 let state = HashConsDedupSerializer::default();
48 Self::deserialize_state(&state, deserializer)
49 }
50}
51
52#[derive(Serialize)]
53pub struct CharonVersion(pub String);
54
55impl<'de> Deserialize<'de> for CharonVersion {
56 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
57 where
58 D: Deserializer<'de>,
59 {
60 use serde::de::Error;
61 let version = String::deserialize(deserializer)?;
62 if version != crate::VERSION {
63 return Err(D::Error::custom(format!(
64 "Incompatible version of charon: \
65 this program supports llbc emitted by charon v{} \
66 but attempted to read a file emitted by charon v{}",
67 crate::VERSION,
68 version,
69 )));
70 }
71 Ok(CharonVersion(version))
72 }
73}
74
75impl CrateData {
76 pub fn new(ctx: TransformCtx) -> Self {
77 CrateData {
78 charon_version: CharonVersion(crate::VERSION.to_owned()),
79 has_errors: ctx.has_errors(),
80 translated: ctx.translated,
81 }
82 }
83
84 #[allow(clippy::result_unit_err)]
85 pub fn serialize_to_files(
86 &self,
87 targets: Vec<(PathBuf, SerializationFormat)>,
88 ) -> Result<(), ()> {
89 for (target_filename, format) in targets {
90 self.serialize_to_file(&target_filename, format)?;
91 }
92 Ok(())
93 }
94
95 #[allow(clippy::result_unit_err)]
97 pub fn serialize_to_file(
98 &self,
99 target_filename: &Path,
100 format: SerializationFormat,
101 ) -> Result<(), ()> {
102 let target_dir = target_filename.parent().unwrap();
106 match std::fs::create_dir_all(target_dir) {
107 Ok(()) => (),
108 Err(_) => {
109 error!("Could not create the directory: {:?}", target_dir);
110 return Err(());
111 }
112 };
113
114 let std::io::Result::Ok(outfile) = File::create(target_filename) else {
116 error!("Could not open: {:?}", target_filename);
117 return Err(());
118 };
119 let mut writer = BufWriter::new(outfile);
121 match format {
122 SerializationFormat::Json => {
123 if let Err(err) = serde_json::to_writer(&mut writer, self) {
124 error!("Could not serialize to `{target_filename:?}`: {err:?}");
125 return Err(());
126 }
127 }
128 SerializationFormat::Postcard => {
129 if let Err(err) = postcard::to_io(self, &mut writer) {
130 error!("Could not serialize to `{target_filename:?}`: {err:?}");
131 return Err(());
132 }
133 }
134 }
135
136 let target_filename = std::fs::canonicalize(target_filename).unwrap();
139 if self.has_errors {
140 info!(
141 "Generated the partial (because we encountered errors) file: {}",
142 target_filename.to_str().unwrap()
143 );
144 } else {
145 info!("Generated the file: {}", target_filename.to_str().unwrap());
146 }
147 Ok(())
148 }
149
150 pub fn deserialize_from_file(
151 path: &std::path::Path,
152 format: SerializationFormat,
153 ) -> anyhow::Result<Self> {
154 use crate::export::CrateData;
155 use anyhow::Context;
156 use serde::Deserialize;
157 use std::fs::File;
158 let file = File::open(path)
159 .with_context(|| format!("Failed to read llbc file {}", path.display()))?;
160 match format {
161 SerializationFormat::Json => {
162 let reader = BufReader::new(file);
163 let mut deserializer = serde_json::Deserializer::from_reader(reader);
164 deserializer.disable_recursion_limit();
166 let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
168 Ok(CrateData::deserialize(deserializer)?)
169 }
170 SerializationFormat::Postcard => {
171 let mut reader = BufReader::new(file);
172 let mut bytes = Vec::new();
173 reader.read_to_end(&mut bytes)?;
174 let (crate_data, rem) = postcard::take_from_bytes::<CrateData>(&bytes)
175 .map_err(|err| anyhow::anyhow!("postcard deserialize error: {err:?}"))?;
176 if !rem.is_empty() {
177 anyhow::bail!("postcard deserialize error: trailing bytes left in input");
178 }
179 Ok(crate_data)
180 }
181 }
182 }
183}