C# Mapper

Date: 2018-11-19

Code

window.addEventListener("DOMContentLoaded", () => {
    const publicProps = /\bpublic\b.*?\s+?([_a-zA-Z0-9<>\[\]\?]+)\s+([_a-zA-Z0-9]+)[^\w]*/gm;
    //const publicProps = /\bpublic\b.*?\s+?([_a-zA-Z0-9<>\[\]\?]+)\s+([_a-zA-Z0-9]+?)\s*[;{=]/gm;
    const fallbackProps = /\b([_a-zA-Z0-9<>\[\]\?]+)\s+([_a-zA-Z0-9]+?)\s*[;{=]/gm;
    const reClass = /\b(class|interface|struct)\s*?([^\s]+)/gi;
    const reSingleLineComment = /(\/\/.*)/g;
    const reMultiLineComment = /\/\*(.|[\r\n])*?\*\//gm;
    const reEmptyOrBlankLines = /\n+\s*\n/gm;

    let className = "Unknown";
    let targetClass = "";
    const tab = "\t";
    const line = "\r\n";
    const getMatches = (re, str) => {
        let match, matches = [];
        while (match = re.exec(str)) {
            matches.push(match);
        }
        return matches;
    };
    const filterProps = (prop) => {
        const reserved = ["void", "return", "class", "struct", "enum", "using", "var"];
        return reserved.indexOf(prop.type) === -1;
    };
	const convertGetSet = (prop) => {
        const p = prop.name;
        return `public ${prop.type} ${p} { get; set; }`;
    };
    const convertInitializer = (prop, prefix) => {
        const p = prop.name;
        let c = prop.last ? "" : ",";
        return `${p} = ${prefix}${p}${c}`; // ${prop.type}
    };
    const convertAssignment = (prop, prefix, prefix2) => {
        const p = prop.name;
        return `${prefix}${p} = ${prefix2}${p};`; // ${prop.type}
    };
    const mapTabs = (arr, tabCount) => {
        const tabs = tab.repeat(tabCount);
        return Array.from(arr).map(a => tabs + a);
    }
    const mapProps = (match, i, arr) => {
        return { type: match[1], name: match[2], last: arr.length - 1 === i };
    };
    const convertAll = (str) => {
        str = String(str).replace(reSingleLineComment, "");
        str = String(str).replace(reMultiLineComment, "");
        str = String(str).replace(reEmptyOrBlankLines, "\n");
        console.log(str);

        let props = getMatches(publicProps, str).map(mapProps).filter(filterProps);
        if (props.length < 1) {
            props = getMatches(fallbackProps, str).map(mapProps).filter(filterProps);
        }
        console.table(props);
        console.log(JSON.stringify(props.reduce((x, p) => { x[p.name] = p.type; return x; }, {}), null, 4));

		const props1 = mapTabs(props.map(p => convertGetSet(p)), 1);
        const props2 = mapTabs(props.map(p => convertAssignment(p, "dest.", "src.")), 1);
        const props3 = mapTabs(props.map(p => convertInitializer(p, "src.")), 2);
        const props4 = mapTabs(props.map(p => convertAssignment(p, "", "src.")), 1);
        const mapFrom = mapTabs([
            `public static ${targetClass} FromDomain(${className} src, ${targetClass} dest = null) => FromDomainExt(src, dest);`,
            `public static T FromDomainExt<T>(${className} src, T dest = null) where T: ${targetClass}, new()`,
            `{`,
            tab + `if (src == null) return null;`,
			tab + `if (dest == null) dest = new T();`,
            ...props2,
            tab + `return dest;`,
            `}`
        ], 1);
        const mapTo = mapTabs([
            `public static ${className} ToDomain(${targetClass} src, ${className} dest = null) => ToDomainExt(src, dest);`,
            `public static T ToDomainExt<T>(${targetClass} src, T dest = null) where T: ${className}, new()`,
            `{`,
            tab + `if (src == null) return null;`,
			tab + `if (dest == null) dest = new T();`,
            ...props2,
            tab + `return dest;`,
            `}`
        ], 1);
        // const mapCopyFrom = mapTabs([
        //     `public void CopyFrom(${className} src)`,
        //     `{`,
        //     ...props4,
        //     `}`
        // ], 1);
        // const mapCopyTo = mapTabs([
        //     `public static ${className} CopyTo(this ${className} src, ${targetClass} dest)`,
        //     `{`,
        //     ...props2,
        //     `}`
        // ], 1);

        const classMapper = mapTabs([
            `public class ${targetClass}`,
            `{`,
			...props1,
			"",
            ...mapFrom,
            ...mapTo,
            // ...mapCopyFrom,
            // ...mapCopyTo,
            `}`
        ], 0);
        const mapperClassSrc = classMapper.join(line);

        const toDomainMapper = mapTabs([
            `public static ${className} ToDomain(this ${targetClass} src)
            {`,
            `\tif (src == null) return null;`,
            `\treturn new ${className}() {`,
            ...props3,
            `\t};`,
            `}`
            ], 1);
        const fromDomainMapper = mapTabs([
                `public static ${targetClass} FromDomain(this ${className} src)
                {`,
                `\tif (src == null) return null;`,
                `\treturn new ${targetClass}() {`,
                ...props3,
                `\t};`,
                `}`
                ], 1);

        const extensionMapperSrc = mapTabs([
            `public static class ${targetClass}Extensions`,
            `{`,
			...toDomainMapper,
			"",
            ...fromDomainMapper,
            `}`
        ], 0).join(line);

        const entityConfigure = mapTabs([
            `public void Configure(EntityTypeBuilder<${targetClass}> entityTypeBuilder)`,
            `{`,
                `\tentityTypeBuilder.ToTable("${className}");`,
                `\tentityTypeBuilder.HasKey(x => x.${props[0].name});`,
                ...props.map(p => `\tentityTypeBuilder.Property(x => x.${p.name});`),
            `}`
            ], 1);

        const entityMapperSrc = mapTabs([
            `public class ${targetClass}Map : IEntityTypeConfiguration<${targetClass}>`,
            `{`,
            ...entityConfigure,
            `}`
        ], 0).join(line);

        return [mapperClassSrc, extensionMapperSrc, entityMapperSrc].join("\n");
    };
    const run = () => {
        const input = document.querySelector("#input").value;
        className = (reClass.exec(input) || [])[2] || null;
        if (className) {
            targetClass = `Dm${className}`;
            const output = convertAll(input);
            document.querySelector("#output").value = output;
        }
    };
    document.querySelector("#btnRun").addEventListener("click", run);
});

15900cookie-checkC# Mapper