use std::collections::HashMap;
use super::{visit_urls, SceneVisitorExtension, Visitor};
pub fn merge_scenes(scenes: Vec<crate::Scene>) -> crate::Scene {
let mut new_scene = crate::Scene::new();
for mut scene in scenes {
register_resources(&mut new_scene, &mut scene);
if let Some(redirections) = scene.redirections {
let new_redirections = new_scene.redirections.get_or_insert(HashMap::new());
for (key, value) in redirections {
new_redirections.insert(key, value);
}
}
for product in scene.products {
new_scene.products.push(product);
}
if let Some(representations) = scene.representations {
for representation in representations {
let available_representations = new_scene.representations.get_or_insert(Vec::new());
if !available_representations.contains(&representation) {
available_representations.push(representation);
}
}
}
}
new_scene
}
fn register_resources(final_scene: &mut crate::Scene, scene: &mut crate::Scene) {
let base_path = scene.base_path.clone();
let mut updated_hashes = scene.hashes.clone();
visit_urls(scene, &mut |url| {
let absolute_url = get_absolute_url(base_path.as_deref(), url);
let hash = updated_hashes.remove(url);
if let Some(hash) = hash {
final_scene.hashes.insert(absolute_url.clone(), hash);
}
*url = absolute_url;
});
for (url, hash) in updated_hashes {
final_scene.hashes.insert(url, hash);
}
scene.visit(&mut MergeVisitor { final_scene });
}
struct MergeVisitor<'a> {
final_scene: &'a mut crate::Scene,
}
impl<'a> Visitor for MergeVisitor<'a> {
fn visit_geometry_index(&mut self, geometry_name: &str, url: &mut String) {
let available_geometries = self
.final_scene
.geometry_indexes
.get_or_insert(HashMap::new());
if !available_geometries.contains_key(geometry_name) {
available_geometries.insert(geometry_name.to_string(), url.clone());
}
}
fn visit_material_index(&mut self, material_name: &str, url: &mut String) {
let available_materials = self
.final_scene
.material_indexes
.get_or_insert(HashMap::new());
if !available_materials.contains_key(material_name) {
available_materials.insert(material_name.to_string(), url.clone());
}
}
fn visit_geometry(&mut self, geometry_name: &str, geometry: &mut crate::ig::Geometry) {
let available_geometries = self.final_scene.geometries.get_or_insert(HashMap::new());
if !available_geometries.contains_key(geometry_name) {
available_geometries.insert(
geometry_name.to_string(),
crate::Geometry {
ig: Some(geometry.clone()),
cns: None,
},
);
}
}
fn visit_material(&mut self, material_name: &str, material: &mut crate::ig::Material) {
let available_materials = self.final_scene.materials.get_or_insert(HashMap::new());
if !available_materials.contains_key(material_name) {
available_materials.insert(
material_name.to_string(),
crate::Material {
ig: Some(material.clone()),
},
);
}
}
fn visit_script(&mut self, script: &mut crate::Script) {
let scripts = self.final_scene.scripts.get_or_insert(Vec::new());
for added_script in scripts.iter_mut() {
if added_script.name == script.name {
if is_script_version_higher(&added_script.version, &script.version) {
*added_script = script.clone();
}
return;
}
}
scripts.push(script.clone());
}
}
fn get_absolute_url(base_url: Option<&str>, url: &str) -> String {
if url.starts_with("http://") || url.starts_with("https://") {
url.to_string()
} else if let Some(base_url) = base_url {
let url = url.trim_start_matches('/');
let base_url = base_url.trim_end_matches('/');
format!("{}/{}", base_url, url)
} else {
url.to_string()
}
}
fn is_script_version_higher(current_version: &str, compare_version: &str) -> bool {
let current_version_parts = current_version
.split(".")
.map(|value| i32::from_str_radix(value, 10).unwrap())
.collect::<Vec<_>>();
let compare_version_parts = compare_version
.split(".")
.map(|value| i32::from_str_radix(value, 10).unwrap())
.collect::<Vec<_>>();
for (current_part, compare_part) in current_version_parts
.iter()
.zip(compare_version_parts.iter())
{
match current_part.cmp(compare_part) {
std::cmp::Ordering::Less => return true,
std::cmp::Ordering::Greater => return false,
_ => {}
}
}
false
}
#[cfg(test)]
mod tests {
use crate::support::merge::get_absolute_url;
#[test]
fn get_absolute_url_handles_absolute_url() {
let base_url = Some("http://example.com");
let url = "http://example2.com/image.jpg";
let result = get_absolute_url(base_url, url);
assert_eq!(result, "http://example2.com/image.jpg");
}
#[test]
fn get_absolute_url_handles_trailing_slash() {
let base_url = Some("http://example.com/");
let url = "image.jpg";
let result = get_absolute_url(base_url, url);
assert_eq!(result, "http://example.com/image.jpg");
}
#[test]
fn get_absolute_url_handles_leading_slash() {
let base_url = Some("http://example.com");
let url = "/image.jpg";
let result = get_absolute_url(base_url, url);
assert_eq!(result, "http://example.com/image.jpg");
}
#[test]
fn get_absolute_url_leading_and_trailing_slash() {
let base_url = Some("http://example.com/");
let url = "/image.jpg";
let result = get_absolute_url(base_url, url);
assert_eq!(result, "http://example.com/image.jpg");
}
#[test]
fn get_absolute_url_handles_no_base_url() {
let base_url = None;
let url = "image.jpg";
let result = get_absolute_url(base_url, url);
assert_eq!(result, "image.jpg");
}
#[test]
fn version_3_2_1_100_is_newer_than_3_2_1_99() {
let a = "3.2.1.99";
let b = "3.2.1.100";
assert!(super::is_script_version_higher(a, b));
}
#[test]
fn version_3_2_1_99_is_newer_than_3_1_1_100() {
let a = "3.1.1.100";
let b = "3.2.1.99";
assert!(super::is_script_version_higher(a, b));
}
#[test]
fn version_3_2_1_99_is_not_newer_than_3_2_1_99() {
let a = "3.2.1.99";
let b = "3.2.1.99";
assert!(!super::is_script_version_higher(a, b));
}
#[test]
fn version_3_0_0_100_is_newer_than_2_6_0_100() {
let a = "2.6.0.100";
let b = "3.0.0.100";
assert!(super::is_script_version_higher(a, b));
}
}