use std::ffi::OsStr;
use std::path::PathBuf;
use anyhow::Result;
use clap::Parser;
use rnapkin::draw::{self, colors::ColorTheme, Mirror};
use rnapkin::forest;
use rnapkin::rnamanip::{self, Nucleotide};
use rnapkin::utils::ParsedInput;
const BUBBLE_RADIUS: f64 = 0.5;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
input: Option<String>,
#[arg(short, long)]
output: Option<String>,
#[arg(short, long, default_value = "dark")]
theme: String,
#[arg(short, long, default_value_t = 0.)]
angle: f64,
#[arg(short, long)]
bgopacity: Option<f64>,
#[arg(long, default_value_t = false)]
my: bool,
#[arg(long, default_value_t = false)]
mx: bool,
#[arg(long, default_value_t = 900)]
height: u32,
#[arg(short, long, default_value_t = false)]
points: bool,
#[arg(short, long, default_value_t = false)]
svgprint: bool,
}
fn main() -> Result<()> {
let args = Args::parse();
let pi = match args.input {
Some(input) => ParsedInput::from_file(&input)?,
None => ParsedInput::from_pipe()?, };
let mut filename: PathBuf = args
.output
.unwrap_or_else(|| pi.rna_name.unwrap_or_else(|| "rnaimg.svg".to_owned()))
.into();
if args.svgprint {
filename = PathBuf::from("o.x")
} else {
match filename.extension().and_then(OsStr::to_str) {
Some("png") | Some("svg") => (),
_ => {
filename = PathBuf::from(format!(
"{}.svg",
filename.to_str().expect("filename is not valid utf8?")
));
}
};
}
let mut theme = match args.theme.as_ref() {
"dark" => ColorTheme::dark(),
"white" | "w" => ColorTheme::white(),
"black" | "b" => ColorTheme::black(),
"bright" => ColorTheme::bright(),
_ => {
eprintln!(
"theme: \"{}\" not recognized!\nfalling back to default",
args.theme
);
ColorTheme::default()
}
};
if let Some(bgopacity) = args.bgopacity {
theme.bg.3 = bgopacity;
}
let (pairlist, sequence) = match (pi.secondary_structure, pi.sequence) {
(Some(sst), Some(seq)) => {
let pl = rnamanip::get_pair_list(&sst);
let seq = rnamanip::read_sequence(&seq);
assert_eq!(
pl.len(),
seq.len(),
"sequence and structure have differents lenghts!"
);
(pl, seq)
}
(Some(sst), None) => {
let pairlist = rnamanip::get_pair_list(&sst);
let seq = vec![Nucleotide::X; pairlist.len()]; (pairlist, seq)
}
(None, Some(_)) => unimplemented!(
"Calling external soft e.g. RNAFold to get secondary_structure not yet implemented"
),
(None, None) => panic!("Neither sequence nor secondary structure found in the input file!"),
};
let tree = forest::grow_tree(&pairlist);
let mut bubbles =
draw::gather_bubbles(&tree, &sequence, BUBBLE_RADIUS, args.angle.to_radians());
let mirror = Mirror::new(args.mx, args.my);
if args.points {
bubbles.mirror(mirror);
for bbl in &bubbles.bubbles {
println!("{},{},{},{}", bbl.point.x, bbl.point.y, bbl.nt, bbl.pos);
}
return Ok(());
}
let highlights = match pi.highlight {
Some(hls) => draw::colors::user_input_to_highlight_indices(&hls),
None => vec![None; sequence.len()],
};
let svgout = draw::plot(
&bubbles,
BUBBLE_RADIUS,
&filename,
&theme,
args.height,
mirror,
&highlights,
)?;
match svgout {
Some(svg_string) => println!("{svg_string}"),
None => println!("{}", &filename.to_str().unwrap()),
}
Ok(())
}