use crate::annotations::impl_helpers::impl_namespace;
use cairo_lang_sierra::program::StatementIdx;
use derive_more::{Add, AddAssign, Display, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VersionedCoverageAnnotations {
V1(CoverageAnnotationsV1),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct CoverageAnnotationsV1 {
pub statements_code_locations: HashMap<StatementIdx, Vec<CodeLocation>>,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct CodeLocation(
pub SourceFileFullPath,
pub SourceCodeSpan,
#[serde(default, skip_serializing_if = "Option::is_none")] pub Option<bool>,
);
#[derive(
Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Display, Default,
)]
pub struct SourceFileFullPath(pub String);
impl SourceFileFullPath {
#[must_use]
pub fn remove_virtual_file_markings(&self) -> (&str, Vec<&str>) {
let mut parts = self.0.split('[');
let path = parts
.next()
.unwrap_or_else(|| unreachable!("split always returns at least one element"));
let virtual_file_markings = parts
.map(|virtual_file| {
virtual_file
.strip_suffix(']')
.expect("virtual file marking should end with ']'")
})
.collect();
(path, virtual_file_markings)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct SourceCodeSpan {
pub start: SourceCodeLocation,
pub end: SourceCodeLocation,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct SourceCodeLocation {
pub line: LineNumber,
pub col: ColumnNumber,
}
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
Hash,
Ord,
PartialOrd,
Serialize,
Deserialize,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Display,
Default,
)]
pub struct ColumnNumber(pub usize);
#[derive(
Clone,
Copy,
Debug,
Eq,
PartialEq,
Hash,
Ord,
PartialOrd,
Serialize,
Deserialize,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Display,
Default,
)]
pub struct LineNumber(pub usize);
impl Serialize for VersionedCoverageAnnotations {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
VersionedCoverageAnnotations::V1(v1) => v1.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for VersionedCoverageAnnotations {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
CoverageAnnotationsV1::deserialize(deserializer).map(VersionedCoverageAnnotations::V1)
}
}
impl_namespace!(
"github.com/software-mansion/cairo-coverage",
CoverageAnnotationsV1,
VersionedCoverageAnnotations
);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_remove_virtual_file_markings() {
let path = SourceFileFullPath(
"/path/to/project/lib.cairo[array_inline_macro][assert_macro]".to_string(),
);
let (path, virtual_file_markings) = path.remove_virtual_file_markings();
assert_eq!(path, "/path/to/project/lib.cairo");
assert_eq!(
virtual_file_markings,
vec!["array_inline_macro", "assert_macro"]
);
}
#[test]
fn test_remove_virtual_file_markings_no_markings() {
let path = SourceFileFullPath("/path/to/project/lib.cairo".to_string());
let (path, virtual_file_markings) = path.remove_virtual_file_markings();
assert_eq!(path, "/path/to/project/lib.cairo");
assert_eq!(virtual_file_markings, Vec::<&str>::new());
}
#[test]
fn test_remove_virtual_file_markings_empty() {
let path = SourceFileFullPath(String::new());
let (path, virtual_file_markings) = path.remove_virtual_file_markings();
assert_eq!(path, "");
assert_eq!(virtual_file_markings, Vec::<&str>::new());
}
#[test]
fn test_remove_virtual_file_markings_single_marking() {
let path = SourceFileFullPath("/path/to/project/lib.cairo[array_inline_macro]".to_string());
let (path, virtual_file_markings) = path.remove_virtual_file_markings();
assert_eq!(path, "/path/to/project/lib.cairo");
assert_eq!(virtual_file_markings, vec!["array_inline_macro"]);
}
}