mod program_scope;
pub use program_scope::*;
use leo_errors::Result;
use leo_span::Symbol;
use crate::{Module, ProgramId, Stub};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Program {
#[serde(with = "module_map")]
pub modules: IndexMap<Vec<Symbol>, Module>,
pub imports: IndexMap<Symbol, ProgramId>,
pub stubs: IndexMap<Symbol, Stub>,
pub program_scopes: IndexMap<Symbol, ProgramScope>,
}
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (_, stub) in self.stubs.iter() {
writeln!(f, "{stub}")?;
}
for (_, module) in self.modules.iter() {
writeln!(f, "{module}")?;
}
for (_, import_id) in self.imports.iter() {
writeln!(f, "import {import_id};")?;
}
for (_, program_scope) in self.program_scopes.iter() {
writeln!(f, "{program_scope}")?;
}
Ok(())
}
}
impl Default for Program {
fn default() -> Self {
Self {
modules: IndexMap::new(),
imports: IndexMap::new(),
stubs: IndexMap::new(),
program_scopes: IndexMap::new(),
}
}
}
impl Program {
pub fn to_json_string(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(&self).map_err(|e| crate::errors::failed_to_convert_ast_to_json_string(&e))?)
}
pub fn to_json_value(&self) -> Result<serde_json::Value> {
Ok(serde_json::to_value(self).map_err(|e| crate::errors::failed_to_convert_ast_to_json_value(&e))?)
}
pub fn to_json_file(&self, path: std::path::PathBuf, file_name: &str) -> Result<()> {
write_ast_json(self, path, file_name)
}
pub fn to_json_file_without_keys(
&self,
path: std::path::PathBuf,
file_name: &str,
excluded_keys: &[&str],
) -> Result<()> {
write_ast_json_filtered(self, path, file_name, excluded_keys)
}
pub fn from_json_string(json: &str) -> Result<Self> {
let ast: Program =
serde_json::from_str(json).map_err(|e| crate::errors::failed_to_read_json_string_to_ast(&e))?;
Ok(ast)
}
pub fn from_json_file(path: std::path::PathBuf) -> Result<Self> {
let data = std::fs::read_to_string(&path).map_err(|e| crate::errors::failed_to_read_json_file(&path, &e))?;
Self::from_json_string(&data)
}
}
pub fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Value {
match value {
serde_json::Value::Object(map) => serde_json::Value::Object(
map.into_iter().filter(|(k, _)| k != key).map(|(k, v)| (k, remove_key_from_json(v, key))).collect(),
),
serde_json::Value::Array(values) => {
serde_json::Value::Array(values.into_iter().map(|v| remove_key_from_json(v, key)).collect())
}
_ => value,
}
}
pub fn normalize_json_value(value: serde_json::Value) -> serde_json::Value {
match value {
serde_json::Value::Array(vec) => {
let orig_length = vec.len();
let mut new_vec: Vec<serde_json::Value> = vec
.into_iter()
.filter(|v| !matches!(v, serde_json::Value::Object(map) if map.is_empty()))
.map(normalize_json_value)
.collect();
if orig_length == 2 && new_vec.len() == 1 {
new_vec.pop().unwrap()
} else {
serde_json::Value::Array(new_vec)
}
}
serde_json::Value::Object(map) => {
serde_json::Value::Object(map.into_iter().map(|(k, v)| (k, normalize_json_value(v))).collect())
}
_ => value,
}
}
pub fn write_ast_json<T: Serialize>(value: &T, mut path: std::path::PathBuf, file_name: &str) -> Result<()> {
path.push(file_name);
let file = std::fs::File::create(&path).map_err(|e| crate::errors::failed_to_create_ast_json_file(&path, &e))?;
let writer = std::io::BufWriter::new(file);
Ok(serde_json::to_writer_pretty(writer, value)
.map_err(|e| crate::errors::failed_to_write_ast_to_json_file(&path, &e))?)
}
pub fn write_ast_json_filtered<T: Serialize>(
value: &T,
mut path: std::path::PathBuf,
file_name: &str,
excluded_keys: &[&str],
) -> Result<()> {
path.push(file_name);
let file = std::fs::File::create(&path).map_err(|e| crate::errors::failed_to_create_ast_json_file(&path, &e))?;
let writer = std::io::BufWriter::new(file);
let mut value = serde_json::to_value(value).map_err(|e| crate::errors::failed_to_convert_ast_to_json_value(&e))?;
for key in excluded_keys {
value = remove_key_from_json(value, key);
}
value = normalize_json_value(value);
Ok(serde_json::to_writer_pretty(writer, &value)
.map_err(|e| crate::errors::failed_to_write_ast_to_json_file(&path, &e))?)
}
pub(crate) mod module_map {
use leo_span::{Symbol, with_session_globals};
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S, V>(map: &IndexMap<Vec<Symbol>, V>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
V: Serialize,
{
let joined: IndexMap<String, &V> = with_session_globals(|globals| {
map.iter()
.map(|(path, value)| {
let key = path.iter().map(|sym| sym.as_str(globals, str::to_owned)).collect::<Vec<_>>().join("::");
(key, value)
})
.collect()
});
joined.serialize(serializer)
}
pub fn deserialize<'de, D, V>(deserializer: D) -> Result<IndexMap<Vec<Symbol>, V>, D::Error>
where
D: Deserializer<'de>,
V: Deserialize<'de>,
{
Ok(IndexMap::<String, V>::deserialize(deserializer)?
.into_iter()
.map(|(path, value)| (path.split("::").map(Symbol::intern).collect(), value))
.collect())
}
#[cfg(test)]
mod tests {
use super::*;
use leo_span::create_session_if_not_set_then;
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Wrapper(#[serde(with = "super")] IndexMap<Vec<Symbol>, u32>);
#[test]
fn round_trips_single_and_multi_segment_keys() {
create_session_if_not_set_then(|_| {
let mut map = IndexMap::new();
map.insert(vec![Symbol::intern("utils")], 1);
map.insert(vec![Symbol::intern("utils"), Symbol::intern("math")], 2);
let wrapper = Wrapper(map);
let json = serde_json::to_value(&wrapper).unwrap();
assert_eq!(json, serde_json::json!({ "utils": 1, "utils::math": 2 }));
let restored: Wrapper = serde_json::from_value(json).unwrap();
assert_eq!(restored, wrapper);
});
}
}
}