diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 312 |
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); + } +} |
