Lorem Ipsum Generator — Rust source
Generate placeholder Lorem Ipsum text - paragraphs and sentences with adjustable counts. Classic Latin word pool, instant copy.
This is the Rust implementation — the same logic the interactive tool runs, in a shareable, citable form.
//! Lorem Ipsum generator — Rust polyglot showcase port.
//!
//! Pure structure generator: random words from the canonical Lorem Ipsum
//! pool assembled into capitalized sentences, paragraphs, and multi-
//! paragraph blocks. Behaviour mirrors the TypeScript reference in
//! `src/lib/lorem.ts` 1:1 (same word pool, same defaults, same edge-case
//! handling).
//!
//! Rust's standard library ships no RNG, so this port implements a tiny
//! xorshift64 PRNG (non-cryptographic — matching the TypeScript source's
//! `Math.random()`) seeded from the wall clock. Production code should
//! prefer the `rand` crate; this stays stdlib-only for the showcase.
//!
//! Rust lacks default parameters, so callers pass counts explicitly. The
//! canonical defaults are a sentence of 8..16 words, a paragraph of 5
//! sentences, and a block of 3 paragraphs; `paragraph` and `lorem` apply
//! them internally.
//!
//! Ported from src/lib/lorem.ts.
//! Display source — part of CosmoDev's polyglot tool pages.
use std::cell::RefCell;
use std::sync::OnceLock;
use std::time::{SystemTime, UNIX_EPOCH};
/// Source string for the canonical Lorem Ipsum word pool — identical across
/// every port so output is drawn from the same vocabulary.
const RAW_WORDS: &str = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor \
incididunt ut labore et dolore magna aliqua enim ad minim veniam quis nostrud exercitation \
ullamco laboris nisi aliquip ex ea commodo consequat duis aute irure in reprehenderit voluptate \
velit esse cillum eu fugiat nulla pariatur excepteur sint occaecat cupidatat non proident sunt \
culpa qui officia deserunt mollit anim id est laborum";
static WORDS_CELL: OnceLock<Vec<&'static str>> = OnceLock::new();
/// Access the word pool, splitting it once and caching the result. The
/// source string is `'static`, so every slice it yields is `'static` too.
pub fn words() -> &'static [&'static str] {
WORDS_CELL
.get_or_init(|| RAW_WORDS.split(' ').collect())
.as_slice()
}
/// A minimal xorshift64 PRNG (Marsaglia). Non-cryptographic; used only to
/// match the TypeScript source's `Math.random()` semantics for placeholder
/// text generation.
struct Xorshift64 {
state: u64,
}
impl Xorshift64 {
/// Seed from wall-clock nanoseconds. A zero state is an absorbing fixed
/// point for xorshift, so the low bit is forced on as a fallback.
fn from_clock() -> Self {
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0x9E37_79B9_7F4A_7C15); // arbitrary nonzero fallback if the clock is unavailable
Self { state: seed | 1 }
}
fn next_u64(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
/// Uniform int in [0, max). Returns 0 for max == 0 (matching
/// `Math.floor(Math.random() * 0)`). Light modulo bias is acceptable
/// for non-crypto placeholder use.
fn below(&mut self, max: usize) -> usize {
if max == 0 {
0
} else {
(self.next_u64() % max as u64) as usize
}
}
}
thread_local! {
// Each thread gets its own RNG state, lazily seeded on first use.
static RNG: RefCell<Xorshift64> = RefCell::new(Xorshift64::from_clock());
}
/// Uniform int in [0, max) drawn from the thread-local RNG.
fn rand_int(max: usize) -> usize {
RNG.with(|cell| cell.borrow_mut().below(max))
}
/// Build a single sentence of `min_words`..`max_words` words (inclusive).
/// When `max_words <= min_words` the range collapses to exactly `min_words`.
pub fn sentence(min_words: usize, max_words: usize) -> String {
// Equivalent of Math.max(1, max - min + 1), computed without unsigned
// underflow (max - min is only evaluated when max > min).
let span = if max_words > min_words {
max_words - min_words + 1
} else {
1
};
let n = min_words + rand_int(span);
let pool = words();
let mut pieces: Vec<&str> = Vec::with_capacity(n);
for _ in 0..n {
pieces.push(pool[rand_int(pool.len())]);
}
let body = pieces.join(" ");
// Capitalize only the first character and terminate with a period.
// (usize bounds guarantee n words, but the empty case is handled too.)
let mut out = String::with_capacity(body.len() + 1);
let mut chars = body.chars();
if let Some(first) = chars.next() {
out.push(first.to_ascii_uppercase());
out.push_str(chars.as_str());
}
out.push('.');
out
}
/// Join `sentence_count` default-length (8..16 word) sentences into a paragraph.
pub fn paragraph(sentence_count: usize) -> String {
(0..sentence_count)
.map(|_| sentence(8, 16))
.collect::<Vec<_>>()
.join(" ")
}
/// Build `paragraph_count` paragraphs of `sentences_per_paragraph`
/// sentences each, separated by a blank line.
pub fn lorem(paragraph_count: usize, sentences_per_paragraph: usize) -> String {
(0..paragraph_count)
.map(|_| paragraph(sentences_per_paragraph))
.collect::<Vec<_>>()
.join("\n\n")
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →