melodium-lang 0.10.0

Mélodium language parsing and semantic analyser
Documentation
use super::{model::Model, treatment::Treatment};
use crate::Path;
use convert_case::{Case, Casing};
use itertools::Itertools;
use melodium_common::descriptor::{
    Collection, Entry, Identifier, ModelBuildMode, TreatmentBuildMode,
};
use melodium_engine::descriptor::{Model as ModelDescriptor, Treatment as TreatmentDescriptor};
use std::{collections::BTreeMap, sync::Arc};

pub struct Area {
    path: Path,
    collection: Arc<Collection>,
    owned_ids: Vec<Identifier>,
    uses_names: BTreeMap<Identifier, String>,
    models: Vec<Model>,
    treatments: Vec<Treatment>,
}

impl Area {
    pub fn new(path: Path, collection: Arc<Collection>) -> Self {
        let mut owned_ids = collection
            .identifiers()
            .into_iter()
            .filter(|id| Self::is_owned(&path, &collection, id))
            .collect::<Vec<_>>();
        owned_ids.sort();

        let mut models = Vec::new();
        let mut treatments = Vec::new();

        for id in &owned_ids {
            match collection.get(&id.into()).cloned().unwrap() {
                Entry::Model(model) => {
                    let design = match model.clone().downcast_arc::<ModelDescriptor>() {
                        Ok(designed) => {
                            if let Some(designer) =
                                designed.designer(collection.clone(), None).success()
                            {
                                if let Some(design) = designer
                                    .read()
                                    .unwrap()
                                    .unvalidated_design()
                                    .success()
                                    .cloned()
                                {
                                    design
                                } else {
                                    if let Some(design) = designed.design().success() {
                                        (**design).clone()
                                    } else {
                                        continue;
                                    }
                                }
                            } else {
                                continue;
                            }
                        }
                        Err(_) => continue,
                    };
                    models.push(Model::new(design));
                }
                Entry::Treatment(treatment) => {
                    let design = match treatment.clone().downcast_arc::<TreatmentDescriptor>() {
                        Ok(designed) => {
                            if let Some(designer) =
                                designed.designer(collection.clone(), None).success()
                            {
                                if let Some(design) = designer
                                    .read()
                                    .unwrap()
                                    .unvalidated_design()
                                    .success()
                                    .cloned()
                                {
                                    design
                                } else {
                                    if let Some(design) = designed.design().success() {
                                        (**design).clone()
                                    } else {
                                        continue;
                                    }
                                }
                            } else {
                                continue;
                            }
                        }
                        Err(_) => continue,
                    };
                    treatments.push(Treatment::new(design));
                }
                _ => {}
            }
        }

        let mut needs = Vec::new();

        models.iter().for_each(|m| needs.extend(m.uses().clone()));
        treatments
            .iter()
            .for_each(|t| needs.extend(t.uses().clone()));
        needs.append(&mut owned_ids.clone());

        needs.sort();
        needs.dedup();

        let mut names: BTreeMap<Identifier, String> = needs
            .iter()
            .map(|id| (id.clone(), id.name().to_string()))
            .collect();
        loop {
            let conflicts = names
                .iter()
                .map(|(_, name)| name)
                .duplicates()
                .collect::<Vec<_>>();
            if conflicts.is_empty() {
                break;
            }

            let conflicts = names
                .iter()
                .filter_map(|(id, name)| {
                    if conflicts.contains(&name) {
                        Some((id.clone(), name.clone()))
                    } else {
                        None
                    }
                })
                .collect::<Vec<_>>();

            for (id, name) in conflicts {
                if !owned_ids.contains(&id) {
                    let new_name = Self::append_step(&id, &name);
                    names.insert(id, new_name);
                }
            }
        }

        Self {
            path,
            collection,
            owned_ids,
            uses_names: names,
            models,
            treatments,
        }
    }

    pub fn path(&self) -> &Path {
        &self.path
    }

    pub fn collection(&self) -> &Arc<Collection> {
        &self.collection
    }

    pub fn owned_ids(&self) -> &Vec<Identifier> {
        &self.owned_ids
    }

    pub fn uses_names(&self) -> &BTreeMap<Identifier, String> {
        &self.uses_names
    }

    pub fn models(&self) -> &Vec<Model> {
        &self.models
    }

    pub fn treatments(&self) -> &Vec<Treatment> {
        &self.treatments
    }

    pub fn implementation_needs(&self, needs: Vec<Identifier>) -> String {
        let mut result = String::new();
        for (id, name) in &self.uses_names {
            if needs.contains(id) {
                result.push_str("use ");
                result.push_str(if id.root() == &self.path.root() {
                    "root"
                } else {
                    id.root()
                });
                if id.path().len() > 1 {
                    result.push_str("/");
                }
                result.push_str(
                    &id.path()
                        .iter()
                        .skip(1)
                        .cloned()
                        .collect::<Vec<_>>()
                        .join("/"),
                );
                result.push_str("::");
                result.push_str(id.name());
                if name != id.name() {
                    result.push_str(" as ");
                    result.push_str(name);
                }
                result.push_str("\n");
            }
        }

        result.push_str("\n");

        result
    }

    pub fn implementation(&self) -> String {
        let mut result = self.implementation_needs(
            self.uses_names
                .keys()
                .filter_map(|id| {
                    if self.path.path() != id.path() {
                        Some(id.clone())
                    } else {
                        None
                    }
                })
                .collect_vec(),
        );

        for model in &self.models {
            result.push_str(&model.implementation(&self.uses_names));
        }

        for treatment in &self.treatments {
            result.push_str(&treatment.implementation(&self.uses_names));
        }

        result
    }

    fn append_step(id: &Identifier, name: &str) -> String {
        let stripped_name = name.strip_prefix(&['@', '|']).unwrap_or(name);
        let prefix = name.strip_suffix(stripped_name).unwrap_or("");
        let mut full = stripped_name.to_string();
        for step in id.path().iter().rev() {
            full = format!(
                "{}{}",
                step.from_case(Case::Snake).to_case(Case::UpperCamel),
                full,
            );

            if !name.ends_with(&full) && name != full {
                break;
            }
        }

        format!("{prefix}{full}")
    }

    fn is_owned(path: &Path, collection: &Arc<Collection>, id: &Identifier) -> bool {
        if id.path() == path.path() {
            match collection.get(&id.into()) {
                Some(Entry::Context(_)) => false,
                Some(Entry::Function(_)) => false,
                Some(Entry::Model(model)) => match model.build_mode() {
                    ModelBuildMode::Compiled(_) => false,
                    ModelBuildMode::Designed() => true,
                },
                Some(Entry::Data(_)) => false,
                Some(Entry::Treatment(treatment)) => match treatment.build_mode() {
                    TreatmentBuildMode::Compiled(_, _) => false,
                    TreatmentBuildMode::Source(_) => false,
                    TreatmentBuildMode::Designed() => true,
                },
                None => false,
            }
        } else {
            false
        }
    }
}