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