Something went wrong. Try again.
A small Rust tool for viewing SMILES and SDF in modern terminals
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384use base64::Engine;use base64::engine::general_purpose::STANDARD;use molview::{render_sdf_png, render_smiles_png};use std::env;use std::error::Error;use std::ffi::OsStr;use std::fs;use std::io::{self, Write};use std::path::Path;use std::process::ExitCode;
const USAGE: &str = "usage: molview <SMILES|FILE.sdf>";
fn write_kitty_image(png: &[u8], mut output: impl Write) -> io::Result<()> { let mut chunks = png.chunks(3 * 4096 / 4).peekable(); let mut first = true;
while let Some(chunk) = chunks.next() { let metadata = if first { "a=T,f=100," } else { "" }; first = false; let more = u8::from(chunks.peek().is_some());
write!( output, "\x1b_G{metadata}m={more};{}\x1b\\", STANDARD.encode(chunk) )?; }
Ok(())}
fn run() -> Result<(), Box<dyn Error>> { let mut args = env::args_os().skip(1); let input = args .next() .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, USAGE))?; if args.next().is_some() { return Err(io::Error::new(io::ErrorKind::InvalidInput, USAGE).into()); }
let path = Path::new(&input); let is_sdf = path.is_file() || path .extension() .is_some_and(|extension| extension.eq_ignore_ascii_case(OsStr::new("sdf"))); let png = if is_sdf { render_sdf_png(&fs::read(path)?)? } else { render_smiles_png(input.to_str().ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidInput, "SMILES must be valid UTF-8") })?)? };
write_kitty_image(&png, io::stdout().lock())?; Ok(())}
fn main() -> ExitCode { match run() { Ok(()) => ExitCode::SUCCESS, Err(error) => { eprintln!("molview: {error}"); ExitCode::FAILURE } }}
#[cfg(test)]mod tests { use super::write_kitty_image;
#[test] fn chunks_kitty_images() { let mut output = Vec::new(); write_kitty_image(&vec![0; 3073], &mut output).unwrap(); let output = String::from_utf8(output).unwrap();
assert!(output.starts_with("\x1b_Ga=T,f=100,m=1;")); assert!(output.contains("\x1b\\\x1b_Gm=0;")); assert!(output.ends_with("\x1b\\")); }}