use args::{Args, ArgsError};
use getopts::Occur;
use saville_encode::{encode_saville, SavilleLayout, SavilleSpaceShape};
use std::{
io::{self, Read},
process::exit,
};
fn main() {
let (layout, space_shape, output_file, symbol_height) = parse_args().unwrap();
let mut input: Vec<u8> = Vec::new();
if let Err(err) = io::stdin().read_to_end(&mut input) {
panic!("Could not read from stdin: {}", err);
}
input.pop();
let input = match String::from_utf8(input) {
Ok(s) => s,
Err(err) => panic!("Input is not valid UTF-8: {}", err),
};
let document = encode_saville(&input, layout, space_shape, symbol_height);
svg::save(output_file, &document).unwrap();
}
fn parse_args() -> Result<(SavilleLayout, SavilleSpaceShape, String, i32), ArgsError> {
let mut args = Args::new(
"saville_encode",
concat!(
"Encode UTF-8 text into Peter Saville's colour code.\n",
"\n",
"saville_encode reads UTF-8 text from standard input (to EOF) and generates an SVG file of the encoded text which is outputted.\n",
"\n",
"saville_encode encodes '0'-'9' and 'a'-'z' (case-insensitive) as a sequence of colourful square-shaped symbols as per the code, and encodes ' ' (space) as a grey octagon.\n",
"Custom two-colour symbols may be entered as \"|73\", where the colour for the left and right sides of the symbol are the (single) colours '7' and '3' encode as, respectively. Accepted characters are '0'-'9' and 'a'-'i'.\n",
"A newline character begins a new line of symbols.\n",
"Any UTF-8 character which is not in any aforementioned range (such as '.' [full stop/period], ',' [comma], '?' [question mark], and '\\'' [apostrophe]) will be encoded as a blank symbol space.\n",
"\n",
"Examples:\n",
"$ echo \"fac|73 ,blue monday and\" | ./saville_encode -o blue_monday.svg\n",
"$ echo \"but, if it wasn't for your misfortune,\n",
" i'd be a heavenly person today\" | ./saville_encode -l RightDown -s Regular -r 500\n",
"$ ./saville_encode < input.txt"
),
);
args.flag("h", "help", "Print this help menu and exit.")
.option("l", "layout",
concat!(
"(Default: DownRight) The layout of the design to be outputted. May be one of:\n",
" - DownRight: mimics the vertical design found on the cover of FAC 73 Blue Monday and The Beach by New Order.\n",
" - RightDown: mimics the horizontal design found in the official lyric video for Blue Monday by New Order.\n",
" - RightDownThin: RightDown but thin (same height as the top trapezoid of a regular octagon).",
),
"LAYOUT", Occur::Optional, Some("DownRight".to_owned())
)
.option("s", "space-shape",
concat!(
"(Default: Fuller) The shape of the ' ' [space] character. May be one of:\n",
" - Fuller: corners are smaller than for Regular.\n",
" - Regular: regular octagon.",
),
"SHAPE", Occur::Optional, Some("Fuller".to_owned())
)
.option("o", "output",
"(Default: out.svg) The file to output the generated SVG to.", "FILE",
Occur::Optional, Some("out.svg".to_owned())
)
.option("r", "resolution",
"(Default: 1000) The height of each symbol in the SVG coordinate system. In other words, the length of a side of a square-shaped symbol (such as for 'a').",
"INTEGER", Occur::Optional, Some("1000".to_owned())
);
args.parse_from_cli()?;
if args.value_of("help")? {
print!("{}", args.full_usage());
exit(0);
}
Ok((
match args.value_of::<String>("layout")?.as_str() {
"RightDown" => SavilleLayout::RightDown,
"DownRight" => SavilleLayout::DownRight,
"RightDownThin" => SavilleLayout::RightDownThin,
a => return Err(ArgsError::new("Invalid argument for option --layout", a)),
},
match args.value_of::<String>("space-shape")?.as_str() {
"Fuller" => SavilleSpaceShape::Fuller,
"Regular" => SavilleSpaceShape::Regular,
a => {
return Err(ArgsError::new(
"Invalid argument for option --space-shape",
a,
))
}
},
args.value_of("output")?,
match args.value_of::<i32>("resolution")? {
a @ 0.. => a,
a => {
return Err(ArgsError::new(
format!(
"Argument for option --resolution is too large (max is {})",
i32::MAX
)
.as_str(),
format!("{}", a).as_str(),
))
}
},
))
}