summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs312
1 files changed, 312 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..25e7a5c
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,312 @@
+//! Conjecturally Secure Pseudo-Random Spreadsheet Generator
+
+// Copyright (c) 2026 Doan Luu Duc Tai
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// SPDX-License-Identifier: MIT OR Apache-2.0
+
+use rand::Rng;
+
+const REGIONS: &[&str] = &[
+ "North America",
+ "EMEA",
+ "APAC",
+ "LATAM",
+ "DACH",
+ "Nordics",
+ "Benelux",
+ "ASEAN",
+ "Remote",
+ "Global HQ",
+ "East Coast",
+ "West Coast",
+];
+
+const PREFIXES: &[&str] = &[
+ "Global", "Alpha", "Vertex", "Titan", "Cloud", "Nexus", "Delta", "Core", "Omni", "Horizon",
+ "Prime", "Spectrum", "Summit", "Apex", "Zenith", "Quantum", "Aether", "Stellar", "Iron",
+ "Velocity", "Unity", "Beacon", "Pinnacle", "Axiom", "Atlas",
+];
+
+const MIDDLES: &[&str] = &[
+ "Systems",
+ "Logic",
+ "Solutions",
+ "Media",
+ "Dynamics",
+ "Labs",
+ "Tech",
+ "Ventures",
+ "Strategies",
+ "Industries",
+ "Consulting",
+ "Analytics",
+ "Networks",
+ "Services",
+ "Resources",
+ "Capital",
+ "Holdings",
+ "Innovations",
+ "Design",
+ "Security",
+ "Intelligence",
+ "Logistics",
+ "Bio",
+ "Fin",
+ "Automotive",
+];
+
+const SUFFIXES: &[&str] = &[
+ "Corp",
+ "Inc",
+ "Group",
+ "Ltd",
+ "Partners",
+ "Co",
+ "GmbH",
+ "SA",
+ "LLC",
+ "Associates",
+ "International",
+ "Enterprises",
+ "Consortium",
+ "Worldwide",
+ "Agency",
+];
+
+const CATEGORIES: &[&str] = &[
+ "Infrastructure",
+ "Software",
+ "Personnel",
+ "Marketing",
+ "R&D",
+ "Logistics",
+ "Operations",
+ "Compliance",
+ "Cybersecurity",
+ "Procurement",
+ "Legal",
+ "Finance",
+ "Sales",
+ "Customer Success",
+ "Training",
+ "Sustainability",
+ "Facilities",
+ "Internal Audit",
+ "Public Relations",
+ "Strategic Planning",
+ "E-commerce",
+];
+
+const STATUSES: &[&str] = &[
+ "Approved",
+ "Pending",
+ "Review",
+ "Closed",
+ "Active",
+ "Archived",
+ "Draft",
+ "Void",
+ "On Hold",
+ "Deferred",
+ "In Progress",
+ "Disputed",
+ "Flagged",
+ "Under Audit",
+ "Escalated",
+ "Completed",
+ "Canceled",
+ "Scheduled",
+];
+
+#[derive(Clone)]
+enum DataType {
+ Identifier,
+ Date,
+ EntityName,
+ Category,
+ Status,
+ Currency,
+ Priority,
+ Region,
+}
+
+fn main() {
+ let mut rng = rand::thread_rng();
+ let mut wb = spreadsheet_ods::WorkBook::new(icu_locale_core::locale!("en-US"));
+
+ let num_sheets: u32 = rng.r#gen_range(1..=50);
+
+ let v_ref = spreadsheet_ods::format::ValueFormatRef::from("");
+ let mut alt_row = spreadsheet_ods::style::CellStyle::new("alt_row", &v_ref);
+ alt_row.set_background_color(spreadsheet_ods::color::Rgb::new(245, 247, 248));
+ let alt_ref = wb.add_cellstyle(alt_row);
+
+ for s_idx in 0..num_sheets {
+ let sheet = build_sheet(s_idx, &mut rng, &mut wb, &alt_ref, &v_ref);
+ wb.push_sheet(sheet);
+ }
+
+ let output_path = "spreadsheet.ods";
+ match spreadsheet_ods::write_ods(&mut wb, output_path) {
+ Ok(()) => println!("Successfully generated {num_sheets} sheets in: {output_path}"),
+ Err(e) => eprintln!("Error: {e:?}"),
+ }
+}
+
+fn build_sheet(
+ s_idx: u32,
+ rng: &mut impl Rng,
+ wb: &mut spreadsheet_ods::WorkBook,
+ alt_ref: &spreadsheet_ods::style::CellStyleRef,
+ v_ref: &spreadsheet_ods::format::ValueFormatRef,
+) -> spreadsheet_ods::Sheet {
+ let display_idx = s_idx.saturating_add(1);
+ let sheet_name = format!("Report_{display_idx}");
+ let mut sheet = spreadsheet_ods::Sheet::new(sheet_name);
+
+ let num_cols = rng.gen_range(5..=50);
+ let mut schema = Vec::new();
+
+ let possible_types = [
+ DataType::Identifier,
+ DataType::Date,
+ DataType::EntityName,
+ DataType::Category,
+ DataType::Status,
+ DataType::Currency,
+ DataType::Priority,
+ DataType::Region,
+ ];
+
+ let header_color = spreadsheet_ods::color::Rgb::new(
+ rng.gen_range(0..=150),
+ rng.gen_range(0..=150),
+ rng.gen_range(0..=150),
+ );
+ let mut h_style = spreadsheet_ods::style::CellStyle::new(format!("header_{s_idx}"), v_ref);
+ h_style.set_background_color(header_color);
+ h_style.set_color(spreadsheet_ods::color::Rgb::new(255, 255, 255));
+ h_style.set_font_bold();
+ let h_ref = wb.add_cellstyle(h_style);
+
+ for c in 0..num_cols {
+ let d_type = possible_types
+ .get(rng.gen_range(0..possible_types.len()))
+ .cloned()
+ .unwrap_or(DataType::Identifier);
+
+ let title = match d_type {
+ DataType::Identifier => "Ref ID",
+ DataType::Date => "Timestamp",
+ DataType::EntityName => "Counterparty",
+ DataType::Category => "Sector",
+ DataType::Status => "Current Status",
+ DataType::Currency => "Total (USD)",
+ DataType::Priority => "Urgency",
+ DataType::Region => "Geographic Region",
+ };
+ schema.push(d_type);
+ sheet.set_styled_value(0, c, title.to_string(), &h_ref);
+ }
+
+ let num_rows = rng.gen_range(50..1500);
+ for row in 1..=num_rows {
+ let style = if row % 2 == 0 { Some(alt_ref) } else { None };
+
+ for (col, d_type) in schema.iter().enumerate() {
+ let col_u32 = u32::try_from(col).unwrap_or(u32::MAX);
+ populate_cell(&mut sheet, row, col_u32, d_type, rng, style);
+ }
+ }
+
+ sheet
+}
+
+fn populate_cell(
+ sheet: &mut spreadsheet_ods::Sheet,
+ row: u32,
+ col: u32,
+ d_type: &DataType,
+ rng: &mut impl Rng,
+ style: Option<&spreadsheet_ods::style::CellStyleRef>,
+) {
+ match d_type {
+ DataType::Identifier => {
+ let letter = char::from(b'A'.saturating_add(rng.gen_range(0..26)));
+ let id = format!("{letter}-{:04}", rng.gen_range(1000..9999));
+ set_cell(sheet, row, col, id, style);
+ }
+ DataType::Date => {
+ let year = rng.gen_range(1970..=2025);
+ let month = rng.gen_range(1..=12);
+ let day = rng.gen_range(1..=28);
+ let hour = rng.gen_range(0..24);
+ let min = rng.gen_range(0..60);
+ let second = rng.gen_range(0..60);
+ let date = format!("{year}-{month:02}-{day:02} {hour:02}:{min:02}:{second:02} UTC");
+ set_cell(sheet, row, col, date, style);
+ }
+ DataType::EntityName => {
+ let name = format!(
+ "{} {} {}",
+ PREFIXES
+ .get(rng.gen_range(0..PREFIXES.len()))
+ .unwrap_or(&"Core"),
+ MIDDLES
+ .get(rng.gen_range(0..MIDDLES.len()))
+ .unwrap_or(&"Tech"),
+ SUFFIXES
+ .get(rng.gen_range(0..SUFFIXES.len()))
+ .unwrap_or(&"Corp")
+ );
+ set_cell(sheet, row, col, name, style);
+ }
+ DataType::Category => {
+ let cat = CATEGORIES
+ .get(rng.gen_range(0..CATEGORIES.len()))
+ .unwrap_or(&"Misc");
+ set_cell(sheet, row, col, (*cat).to_string(), style);
+ }
+ DataType::Status => {
+ let stat = STATUSES
+ .get(rng.gen_range(0..STATUSES.len()))
+ .unwrap_or(&"Pending");
+ set_cell(sheet, row, col, (*stat).to_string(), style);
+ }
+ DataType::Currency => {
+ let val: f64 = rng.gen_range(10.0..5000.0);
+ let amount = (val * 100.0).round() / 100.0;
+ set_cell(sheet, row, col, amount, style);
+ }
+ DataType::Priority => {
+ let p = if rng.gen_bool(0.2) { "High" } else { "Low" };
+ set_cell(sheet, row, col, p, style);
+ }
+ DataType::Region => {
+ let reg = REGIONS
+ .get(rng.gen_range(0..REGIONS.len()))
+ .unwrap_or(&"Global");
+ set_cell(sheet, row, col, (*reg).to_string(), style);
+ }
+ }
+}
+
+fn set_cell<V: Into<spreadsheet_ods::Value>>(
+ sheet: &mut spreadsheet_ods::Sheet,
+ row: u32,
+ col: u32,
+ val: V,
+ style: Option<&spreadsheet_ods::style::CellStyleRef>,
+) {
+ if let Some(s) = style {
+ sheet.set_styled_value(row, col, val, s);
+ } else {
+ sheet.set_value(row, col, val);
+ }
+}