rulemorph 0.3.2

YAML-based declarative data transformation engine for CSV/JSON to JSON
Documentation
use std::io::Cursor;

use zip::{CompressionMethod, ZipWriter, write::FileOptions};

use super::write_zip_file;

mod worksheet;

use self::worksheet::{build_sheet1_xml, build_sheet2_xml};

#[derive(Default)]
pub(crate) struct XlsxFixtureOptions {
    pub(crate) duplicate_header: bool,
    pub(crate) empty_sheet: bool,
    pub(crate) formula_without_cache: bool,
    pub(crate) far_formula_without_cache: bool,
    pub(crate) shared_formula: bool,
    pub(crate) sparse_far_cell: bool,
    pub(crate) macro_enabled: bool,
    pub(crate) external_relationship: bool,
    pub(crate) extra_sheet: bool,
    pub(crate) conflicting_sheet_relationship: bool,
    pub(crate) case_variant_duplicate_sheet: bool,
    pub(crate) escaped_sheet_name: bool,
    pub(crate) custom_relationship_prefix: bool,
    pub(crate) unqualified_sheet_relationship_only: bool,
    pub(crate) wrong_relationship_namespace: bool,
    pub(crate) duplicate_qualified_sheet_relationships: bool,
    pub(crate) conflicting_wrong_literal_relationship: bool,
}

pub(crate) fn build_test_xlsx(options: XlsxFixtureOptions) -> Vec<u8> {
    let mut zip = ZipWriter::new(Cursor::new(Vec::new()));
    let file_options = FileOptions::default().compression_method(CompressionMethod::Deflated);
    let workbook_content_type = if options.macro_enabled {
        "application/vnd.ms-excel.sheet.macroEnabled.main+xml"
    } else {
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"
    };
    let include_second_sheet = options.extra_sheet || options.conflicting_sheet_relationship;
    let sheet2_content_type = if include_second_sheet {
        r#"<Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>"#
    } else {
        ""
    };
    let content_types = format!(
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  <Default Extension="xml" ContentType="application/xml"/>
  <Override PartName="/xl/workbook.xml" ContentType="{workbook_content_type}"/>
  <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
  {sheet2_content_type}
  <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
  <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
</Types>"#
    );
    write_zip_file(
        &mut zip,
        "[Content_Types].xml",
        &content_types,
        file_options,
    );
    write_zip_file(
        &mut zip,
        "_rels/.rels",
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>"#,
        file_options,
    );
    let relationship_prefix =
        if options.custom_relationship_prefix || options.wrong_relationship_namespace {
            "rel"
        } else {
            "r"
        };
    let users_relationship_attrs = if options.conflicting_wrong_literal_relationship {
        r#"r:id="rId4" rel:id="rId1""#.to_string()
    } else if options.duplicate_qualified_sheet_relationships {
        r#"r:id="rId4" relationships:id="rId1""#.to_string()
    } else if options.unqualified_sheet_relationship_only {
        r#"id="rId1""#.to_string()
    } else if options.conflicting_sheet_relationship {
        format!(r#"{relationship_prefix}:id="rId4" id="rId1""#)
    } else {
        format!(r#"{relationship_prefix}:id="rId1""#)
    };
    let relationship_namespace = if options.wrong_relationship_namespace {
        "urn:wrong"
    } else {
        "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
    };
    let workbook_namespace_attrs = if options.conflicting_wrong_literal_relationship {
        format!(r#"xmlns:r="urn:wrong" xmlns:rel="{relationship_namespace}""#)
    } else if options.duplicate_qualified_sheet_relationships {
        format!(
            r#"xmlns:r="{relationship_namespace}" xmlns:relationships="{relationship_namespace}""#
        )
    } else {
        format!(r#"xmlns:{relationship_prefix}="{relationship_namespace}""#)
    };
    let sheet_name = if options.escaped_sheet_name {
        "Users &amp; Billing"
    } else {
        "Users"
    };
    let sheet2_workbook_entry = if options.extra_sheet {
        format!(r#"<sheet name="Archive" sheetId="2" {relationship_prefix}:id="rId4"/>"#)
    } else {
        String::new()
    };
    let workbook = format!(
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" {workbook_namespace_attrs}>
  <sheets><sheet name="{sheet_name}" sheetId="1" {users_relationship_attrs}/>{sheet2_workbook_entry}</sheets>
</workbook>"#
    );
    write_zip_file(&mut zip, "xl/workbook.xml", &workbook, file_options);
    let external_relationship = if options.external_relationship {
        r#"<Relationship Id="rId99" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="https://example.test/" TargetMode="External"/>"#
    } else {
        ""
    };
    let sheet2_relationship = if include_second_sheet {
        r#"<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/>"#
    } else {
        ""
    };
    let workbook_rels = format!(
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
  <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
  <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
  {sheet2_relationship}
  {external_relationship}
</Relationships>"#
    );
    write_zip_file(
        &mut zip,
        "xl/_rels/workbook.xml.rels",
        &workbook_rels,
        file_options,
    );
    write_zip_file(
        &mut zip,
        "xl/sharedStrings.xml",
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="4" uniqueCount="4">
  <si><t>id</t></si><si><t>name</t></si><si><t>Alice</t></si><si><t>Bob</t></si>
</sst>"#,
        file_options,
    );
    write_zip_file(
        &mut zip,
        "xl/styles.xml",
        r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs></styleSheet>"#,
        file_options,
    );
    let sheet = build_sheet1_xml(&options);
    write_zip_file(&mut zip, "xl/worksheets/sheet1.xml", &sheet, file_options);
    if options.case_variant_duplicate_sheet {
        write_zip_file(
            &mut zip,
            "xl/worksheets/SHEET1.XML",
            r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <sheetData>
    <row r="1"><c r="A1"><v>1</v></c></row>
    <row r="1048576"><c r="XFD1048576"><v>1</v></c></row>
  </sheetData>
</worksheet>"#,
            file_options,
        );
    }
    if include_second_sheet {
        let sheet2 = build_sheet2_xml(&options);
        write_zip_file(&mut zip, "xl/worksheets/sheet2.xml", sheet2, file_options);
    }
    if options.macro_enabled {
        write_zip_file(
            &mut zip,
            "xl/vbaProject.bin",
            "not-a-real-vba-project",
            file_options,
        );
    }
    zip.finish().expect("finish xlsx").into_inner()
}