use crate::compile::{CompiledAasa, EffectiveRule, Service};
use serde::Serialize;
use std::collections::BTreeMap;
#[derive(Serialize)]
struct NormalizedDocument<'a> {
services: BTreeMap<&'static str, Vec<&'a str>>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
substitution_variables: &'a BTreeMap<String, Vec<String>>,
applinks: Vec<NormalizedDetail<'a>>,
}
#[derive(Serialize)]
struct NormalizedDetail<'a> {
index: usize,
app_ids: &'a [String],
rules: Vec<EffectiveRule>,
}
impl CompiledAasa {
#[must_use]
pub fn to_normalized_json(&self) -> String {
let mut services = BTreeMap::new();
for service in [
Service::AppLinks,
Service::WebCredentials,
Service::AppClips,
Service::ActivityContinuation,
] {
let apps = self.apps_for_service(service);
if !apps.is_empty() || self.declares_service(service) {
let mut apps = apps;
apps.sort_unstable();
apps.dedup();
services.insert(service.key(), apps);
}
}
let applinks = self
.details
.iter()
.map(|detail| NormalizedDetail {
index: detail.index,
app_ids: &detail.app_ids,
rules: detail
.rules
.iter()
.map(crate::compile::CompiledRule::effective_rule)
.collect(),
})
.collect();
let normalized = NormalizedDocument {
services,
substitution_variables: self.substitution_variables(),
applinks,
};
blazingly_json::to_string_pretty(&normalized)
.expect("the normalized model always serializes")
}
fn declares_service(&self, service: Service) -> bool {
match service {
Service::AppLinks => self.has_applinks,
Service::WebCredentials => self.document.webcredentials.is_some(),
Service::AppClips => self.document.appclips.is_some(),
Service::ActivityContinuation => self.document.activitycontinuation.is_some(),
}
}
}