radstudio 0.8.0

A utility library for the Embarcadero RAD Studio IDE
use std::{
    fs::File,
    path::{Path, PathBuf},
};

use tempfile::TempDir;
use uuid::Uuid;
use xmltree::{Element, XMLNode};

use crate::{
    Platform, dcc,
    msbuild::{add_text_element, consts::*, xml_write_file},
};

pub fn generator_project_file(
    file: &super::FileInfo,
    platform: &Option<Platform>,
    options: &dcc::Options,
) -> crate::Result<Option<(PathBuf, TempDir)>> {
    let ext = file.extension();
    let project_type = if ext.eq_ignore_ascii_case("dpk") {
        "Package"
    } else {
        return Ok(None);
    };

    let mut root = Element::new("Project");
    root.attributes.insert(
        "xmlns".to_string(),
        "http://schemas.microsoft.com/developer/msbuild/2003".to_string(),
    );

    let mut pg_first = Element::new(PROPERTY_GROUP);
    add_text_element(
        &mut pg_first,
        "ProjectGuid",
        format!("{{{:X}}}", Uuid::new_v4()),
    );
    add_text_element(&mut pg_first, "ProjectVersion", "99.9".to_string());
    add_text_element(
        &mut pg_first,
        "MainSource",
        file.path().display().to_string(),
    );
    add_text_element(&mut pg_first, CONFIG, "Release".to_string());
    if let Some(platform) = platform {
        add_text_element(&mut pg_first, PLATFORM, platform.to_string());
    };

    let mut pg_empty = Element::new(PROPERTY_GROUP);
    pg_empty.attributes.insert(
        CONDITION.to_string(),
        "'$(Config)'=='Base' or '$(Base)'!=''".to_string(),
    );

    let mut pg_base = Element::new(PROPERTY_GROUP);
    pg_base.attributes.insert(
        CONDITION.to_string(),
        PROPERTY_GROUP_BASE_CONDITION.to_string(),
    );

    let curdir = std::env::current_dir()?;

    for arg in options.data() {
        if !arg.msbuild.is_empty() {
            add_text_element(
                &mut pg_base,
                arg.msbuild,
                if arg.is_output_dir() {
                    absolute_dirs(&curdir, &arg.value)
                } else {
                    arg.value
                },
            );
        }
    }

    if let Some(search_dirs) = options.search_dirs() {
        add_text_element(
            &mut pg_base,
            "DCC_UnitSearchPath",
            absolute_dirs(&curdir, &search_dirs),
        );
    }

    if options.cpp {
        add_text_element(&mut pg_base, "DCC_CBuilderOutput", "All".to_string());
    };

    if !options.raw.is_empty() {
        add_text_element(
            &mut pg_base,
            "DCC_AdditionalSwitches",
            options.raw.join(" "),
        );
    }

    let mut pe = Element::new("ProjectExtensions");
    add_text_element(&mut pe, "Borland.ProjectType", project_type.to_string());

    root.children.push(XMLNode::Element(pg_first));
    root.children.push(XMLNode::Element(pg_empty));
    root.children.push(XMLNode::Element(pg_base));
    root.children.push(XMLNode::Element(pe));

    let tempdir = tempfile::tempdir()?;
    let output = tempdir
        .path()
        .join(file.project_name())
        .with_extension(EXT_DPROJ);
    xml_write_file(&root, File::create(&output)?)?;
    Ok(Some((output, tempdir)))
}

fn absolute_dirs(base_dir: impl AsRef<Path>, input: impl AsRef<str>) -> String {
    input
        .as_ref()
        .split(";")
        .map(|s| base_dir.as_ref().join(s).display().to_string())
        .collect::<Vec<_>>()
        .join(";")
}