use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnhandledPragma {
pub comment: String,
pub line: u32,
pub column: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileCoverage {
#[serde(default, deserialize_with = "deserialize_null_as_empty")]
pub path: String,
#[serde(rename = "statementMap")]
pub statement_map: BTreeMap<String, Location>,
#[serde(rename = "fnMap")]
pub fn_map: BTreeMap<String, FnEntry>,
#[serde(rename = "branchMap")]
pub branch_map: BTreeMap<String, BranchEntry>,
#[serde(deserialize_with = "deserialize_null_as_zero_map")]
pub s: BTreeMap<String, u32>,
#[serde(deserialize_with = "deserialize_null_as_zero_map")]
pub f: BTreeMap<String, u32>,
#[serde(deserialize_with = "deserialize_null_as_zero_vec_map")]
pub b: BTreeMap<String, Vec<u32>>,
#[serde(
rename = "bT",
skip_serializing_if = "Option::is_none",
default,
deserialize_with = "deserialize_optional_null_as_zero_vec_map"
)]
pub b_t: Option<BTreeMap<String, Vec<u32>>>,
#[serde(rename = "inputSourceMap", skip_serializing_if = "Option::is_none")]
pub input_source_map: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
pub start: Position,
pub end: Position,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Position {
#[serde(default, deserialize_with = "deserialize_null_as_zero")]
pub line: u32,
#[serde(default, deserialize_with = "deserialize_null_as_zero")]
pub column: u32,
}
impl Serialize for Position {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.line == 0 && self.column == 0 {
return serializer.serialize_struct("Position", 0)?.end();
}
let mut state = serializer.serialize_struct("Position", 2)?;
state.serialize_field("line", &self.line)?;
state.serialize_field("column", &self.column)?;
state.end()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FnEntry {
#[serde(default, deserialize_with = "deserialize_null_as_empty")]
pub name: String,
#[serde(default, deserialize_with = "deserialize_null_as_zero")]
pub line: u32,
pub decl: Location,
pub loc: Location,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchEntry {
pub loc: Location,
#[serde(default, deserialize_with = "deserialize_null_as_zero")]
pub line: u32,
#[serde(rename = "type", default, deserialize_with = "deserialize_null_as_empty")]
pub branch_type: String,
pub locations: Vec<Location>,
}
fn deserialize_null_as_empty<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Option::<String>::deserialize(deserializer)?.unwrap_or_default())
}
fn deserialize_null_as_zero<'de, D>(deserializer: D) -> Result<u32, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Option::<u32>::deserialize(deserializer)?.unwrap_or(0))
}
fn deserialize_null_as_zero_map<'de, D>(deserializer: D) -> Result<BTreeMap<String, u32>, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw: BTreeMap<String, Option<u32>> = Deserialize::deserialize(deserializer)?;
Ok(raw.into_iter().map(|(k, v)| (k, v.unwrap_or(0))).collect())
}
fn deserialize_null_as_zero_vec_map<'de, D>(
deserializer: D,
) -> Result<BTreeMap<String, Vec<u32>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw: BTreeMap<String, Option<Vec<Option<u32>>>> = Deserialize::deserialize(deserializer)?;
Ok(raw
.into_iter()
.map(|(k, v)| {
let vec = v.unwrap_or_default().into_iter().map(|x| x.unwrap_or(0)).collect();
(k, vec)
})
.collect())
}
fn deserialize_optional_null_as_zero_vec_map<'de, D>(
deserializer: D,
) -> Result<Option<BTreeMap<String, Vec<u32>>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let opt: Option<BTreeMap<String, Option<Vec<Option<u32>>>>> =
Deserialize::deserialize(deserializer)?;
Ok(opt.map(|raw| {
raw.into_iter()
.map(|(k, v)| {
let vec = v.unwrap_or_default().into_iter().map(|x| x.unwrap_or(0)).collect();
(k, vec)
})
.collect()
}))
}
impl FileCoverage {
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
pub(crate) fn from_maps(
path: String,
statement_map: BTreeMap<String, Location>,
fn_map: BTreeMap<String, FnEntry>,
mut branch_map: BTreeMap<String, BranchEntry>,
logical_branch_ids: &[usize],
) -> Self {
branch_map.retain(|_, entry| !entry.locations.is_empty());
let s = statement_map.keys().map(|k| (k.clone(), 0)).collect();
let f = fn_map.keys().map(|k| (k.clone(), 0)).collect();
let b = branch_map
.iter()
.map(|(k, entry)| (k.clone(), vec![0; entry.locations.len()]))
.collect();
let b_t = if logical_branch_ids.is_empty() {
None
} else {
Some(
logical_branch_ids
.iter()
.map(|&id| {
let key = id.to_string();
let len = branch_map[&key].locations.len();
(key, vec![0; len])
})
.collect(),
)
};
Self { path, statement_map, fn_map, branch_map, s, f, b, b_t, input_source_map: None }
}
}