use std::error::Error;
use std::fmt::{self, Display, Formatter};
use lodestone_types::{
ProfileBatchV1, ProfileFunctionV1, ProfileInstrumentationScopeV1, ProfileLabelV1, ProfileLineV1, ProfileLocationV1,
ProfileMappingV1, ProfileNumberV1, ProfilePrivacyActionV1, ProfilePrivacyFieldV1, ProfilePrivacyPolicyV1,
ProfilePrivacyRuleV1, ProfileSampleTypeV1, ProfileSampleV1, ProfileStackV1,
};
use crate::{
I64, MetricNumber, ProfileBatch, ProfileLabel, ProfilePrivacyAction, ProfilePrivacyField, ProfilePrivacyPolicy,
ProfilePrivacyRule, U64,
};
pub fn profile_batch_from_wire(value: &ProfileBatch) -> Result<ProfileBatchV1, NativeProfileWireError> {
Ok(ProfileBatchV1 {
format_version: u16::try_from(value.format_version).map_err(|_| NativeProfileWireError::Integer)?,
profile_id: value.profile_id.clone(),
batch_id: value.batch_id.clone(),
start_unix_nanos: parse_u64(&value.start_unix_nanos)?,
end_unix_nanos: parse_u64(&value.end_unix_nanos)?,
duration_nanos: parse_u64(&value.duration_nanos)?,
period_nanos: parse_u64(&value.period_nanos)?,
sample_types: value
.sample_types
.iter()
.map(|item| ProfileSampleTypeV1 {
sample_type: item.sample_type.clone(),
unit: item.unit.clone(),
})
.collect(),
resource_attributes: labels_from_wire(&value.resource_attributes),
instrumentation_scope: ProfileInstrumentationScopeV1 {
name: value.instrumentation_scope.name.clone(),
version: value.instrumentation_scope.version.clone(),
attributes: labels_from_wire(&value.instrumentation_scope.attributes),
},
mappings: value
.mappings
.iter()
.map(|item| ProfileMappingV1 {
mapping_id: item.mapping_id.clone(),
build_id: item.build_id.clone(),
module_id: item.module_id.clone(),
filename: item.filename.clone(),
})
.collect(),
functions: value
.functions
.iter()
.map(|item| {
Ok(ProfileFunctionV1 {
function_id: item.function_id.clone(),
name: item.name.clone(),
system_name: item.system_name.clone(),
filename: item.filename.clone(),
start_line: item.start_line.as_ref().map(parse_u64).transpose()?,
symbol_observation_id: item.symbol_observation_id.clone(),
})
})
.collect::<Result<Vec<_>, NativeProfileWireError>>()?,
locations: value
.locations
.iter()
.map(|item| {
Ok(ProfileLocationV1 {
location_id: item.location_id.clone(),
mapping_id: item.mapping_id.clone(),
address: parse_u64(&item.address)?,
lines: item
.lines
.iter()
.map(|line| {
Ok(ProfileLineV1 {
function_id: line.function_id.clone(),
line: parse_u64(&line.line)?,
})
})
.collect::<Result<Vec<_>, NativeProfileWireError>>()?,
})
})
.collect::<Result<Vec<_>, NativeProfileWireError>>()?,
stacks: value
.stacks
.iter()
.map(|item| ProfileStackV1 {
stack_id: item.stack_id.clone(),
parent_stack_id: item.parent_stack_id.clone(),
location_id: item.location_id.clone(),
})
.collect(),
samples: value
.samples
.iter()
.map(|item| {
Ok(ProfileSampleV1 {
sample_id: item.sample_id.clone(),
stack_id: item.stack_id.clone(),
values: item
.values
.iter()
.map(profile_number_from_wire)
.collect::<Result<Vec<_>, _>>()?,
labels: labels_from_wire(&item.labels),
trace_id: item.trace_id.clone(),
span_id: item.span_id.clone(),
})
})
.collect::<Result<Vec<_>, NativeProfileWireError>>()?,
})
}
pub fn profile_number_from_wire(value: &MetricNumber) -> Result<ProfileNumberV1, NativeProfileWireError> {
match value {
MetricNumber::I64 { value } => Ok(ProfileNumberV1::I64 {
value: parse_i64(value)?,
}),
MetricNumber::F64Bits { value } => Ok(ProfileNumberV1::F64Bits {
value: parse_u64(value)?,
}),
}
}
#[must_use]
pub fn profile_number_to_wire(value: ProfileNumberV1) -> MetricNumber {
match value {
ProfileNumberV1::I64 { value } => MetricNumber::I64 {
value: I64 {
value: value.to_string(),
},
},
ProfileNumberV1::F64Bits { value } => MetricNumber::F64Bits {
value: U64 {
value: value.to_string(),
},
},
}
}
pub fn profile_privacy_policy_from_wire(
value: &ProfilePrivacyPolicy,
) -> Result<ProfilePrivacyPolicyV1, NativeProfileWireError> {
Ok(ProfilePrivacyPolicyV1 {
format_version: u16::try_from(value.format_version).map_err(|_| NativeProfileWireError::Integer)?,
rules: value
.rules
.iter()
.map(|rule| ProfilePrivacyRuleV1 {
field: match &rule.field {
ProfilePrivacyField::ResourceAttribute { key } => {
ProfilePrivacyFieldV1::ResourceAttribute { key: key.clone() }
}
ProfilePrivacyField::ScopeAttribute { key } => {
ProfilePrivacyFieldV1::ScopeAttribute { key: key.clone() }
}
ProfilePrivacyField::SampleLabel { key } => ProfilePrivacyFieldV1::SampleLabel { key: key.clone() },
ProfilePrivacyField::FunctionName => ProfilePrivacyFieldV1::FunctionName,
ProfilePrivacyField::FunctionSystemName => ProfilePrivacyFieldV1::FunctionSystemName,
ProfilePrivacyField::FunctionFilename => ProfilePrivacyFieldV1::FunctionFilename,
ProfilePrivacyField::MappingFilename => ProfilePrivacyFieldV1::MappingFilename,
},
action: match rule.action {
ProfilePrivacyAction::Allow => ProfilePrivacyActionV1::Allow,
ProfilePrivacyAction::Drop => ProfilePrivacyActionV1::Drop,
ProfilePrivacyAction::Redact => ProfilePrivacyActionV1::Redact,
ProfilePrivacyAction::Reject => ProfilePrivacyActionV1::Reject,
},
})
.collect(),
})
}
#[must_use]
pub fn profile_privacy_policy_to_wire(value: &ProfilePrivacyPolicyV1) -> ProfilePrivacyPolicy {
ProfilePrivacyPolicy {
format_version: u32::from(value.format_version),
rules: value
.rules
.iter()
.map(|rule| ProfilePrivacyRule {
field: match &rule.field {
ProfilePrivacyFieldV1::ResourceAttribute { key } => {
ProfilePrivacyField::ResourceAttribute { key: key.clone() }
}
ProfilePrivacyFieldV1::ScopeAttribute { key } => {
ProfilePrivacyField::ScopeAttribute { key: key.clone() }
}
ProfilePrivacyFieldV1::SampleLabel { key } => ProfilePrivacyField::SampleLabel { key: key.clone() },
ProfilePrivacyFieldV1::FunctionName => ProfilePrivacyField::FunctionName,
ProfilePrivacyFieldV1::FunctionSystemName => ProfilePrivacyField::FunctionSystemName,
ProfilePrivacyFieldV1::FunctionFilename => ProfilePrivacyField::FunctionFilename,
ProfilePrivacyFieldV1::MappingFilename => ProfilePrivacyField::MappingFilename,
},
action: match rule.action {
ProfilePrivacyActionV1::Allow => ProfilePrivacyAction::Allow,
ProfilePrivacyActionV1::Drop => ProfilePrivacyAction::Drop,
ProfilePrivacyActionV1::Redact => ProfilePrivacyAction::Redact,
ProfilePrivacyActionV1::Reject => ProfilePrivacyAction::Reject,
},
})
.collect(),
}
}
fn labels_from_wire(values: &[ProfileLabel]) -> Vec<ProfileLabelV1> {
values
.iter()
.map(|item| ProfileLabelV1 {
key: item.key.clone(),
value: item.value.clone(),
})
.collect()
}
fn parse_u64(value: &U64) -> Result<u64, NativeProfileWireError> {
value.value.parse().map_err(|_| NativeProfileWireError::Integer)
}
fn parse_i64(value: &I64) -> Result<i64, NativeProfileWireError> {
value.value.parse().map_err(|_| NativeProfileWireError::Integer)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NativeProfileWireError {
Integer,
}
impl Display for NativeProfileWireError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("native profile wire contains a noncanonical integer")
}
}
impl Error for NativeProfileWireError {}