Skip to content

Commit

Permalink
Merge pull request #1266 from nextstrain/feat/cli-read-annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov authored Oct 6, 2023
2 parents ca54ba3 + d8fc90a commit 5fbe04c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 115 deletions.
57 changes: 0 additions & 57 deletions packages_rs/nextclade-cli/src/bin/featuretree.rs

This file was deleted.

58 changes: 0 additions & 58 deletions packages_rs/nextclade-cli/src/bin/genemap.rs

This file was deleted.

1 change: 1 addition & 0 deletions packages_rs/nextclade-cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ pub mod nextclade_dataset_get;
pub mod nextclade_dataset_list;
pub mod nextclade_loop;
pub mod nextclade_ordered_writer;
pub mod nextclade_read_annotation;
pub mod nextclade_seq_sort;
pub mod verbosity;
36 changes: 36 additions & 0 deletions packages_rs/nextclade-cli/src/cli/nextclade_cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::cli::nextclade_dataset_get::nextclade_dataset_get;
use crate::cli::nextclade_dataset_list::nextclade_dataset_list;
use crate::cli::nextclade_loop::nextclade_run;
use crate::cli::nextclade_read_annotation::nextclade_read_annotation;
use crate::cli::nextclade_seq_sort::nextclade_seq_sort;
use crate::cli::verbosity::{Verbosity, WarnLevel};
use crate::io::http_client::ProxyConfig;
Expand Down Expand Up @@ -92,6 +93,11 @@ pub enum NextcladeCommands {
///
/// For short help type: `nextclade -h`, for extended help type: `nextclade --help`. Each subcommand has its own help, for example: `nextclade sort --help`.
Sort(Box<NextcladeSortArgs>),

/// Read genome annotation and present it in Nextclade's internal formats. This is mostly only useful for Nextclade maintainers and the most curious users. Note that these internal formats have no stability guarantees and can be changed at any time without notice.
///
/// For short help type: `nextclade -h`, for extended help type: `nextclade --help`. Each subcommand has its own help, for example: `nextclade sort --help`.
ReadAnnotation(Box<NextcladeReadAnnotationArgs>),
}

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -702,6 +708,35 @@ pub struct NextcladeSortArgs {
pub proxy_config: ProxyConfig,
}

#[allow(clippy::struct_excessive_bools)]
#[derive(Parser, Debug)]
#[clap(verbatim_doc_comment)]
pub struct NextcladeReadAnnotationArgs {
/// Genome annotation file in GFF3 format.
///
/// Learn more about Generic Feature Format Version 3 (GFF3):
/// https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md
///
#[clap(value_hint = ValueHint::FilePath)]
#[clap(display_order = 0)]
pub input_annotation: Option<PathBuf>,

/// Path to output JSON or YAML file.
///
/// The format is chosen based on file extension: ".json" or ".yaml".
#[clap(long, short = 'o')]
#[clap(value_hint = ValueHint::DirPath)]
pub output: Option<PathBuf>,

/// Present features in "feature tree" format. This format is a precursor of genome annotation format - it contains all genetic features, even the ones that Nextclade does not use, but also less information about each feature.
#[clap(long)]
pub feature_tree: bool,

/// Print console output in JSON format, rather than human-readable table.
#[clap(long)]
pub json: bool,
}

fn generate_completions(shell: &str) -> Result<(), Report> {
let mut command = NextcladeArgs::command();

Expand Down Expand Up @@ -989,5 +1024,6 @@ pub fn nextclade_parse_cli_args() -> Result<(), Report> {
NextcladeDatasetCommands::Get(dataset_get_args) => nextclade_dataset_get(&dataset_get_args),
},
NextcladeCommands::Sort(seq_sort_args) => nextclade_seq_sort(&seq_sort_args),
NextcladeCommands::ReadAnnotation(read_annotation_args) => nextclade_read_annotation(&read_annotation_args),
}
}
54 changes: 54 additions & 0 deletions packages_rs/nextclade-cli/src/cli/nextclade_read_annotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::cli::nextclade_cli::NextcladeReadAnnotationArgs;
use eyre::Report;
use nextclade::features::feature_tree::FeatureTree;
use nextclade::gene::gene_map::GeneMap;
use nextclade::gene::gene_map_display::gene_map_to_table_string;
use nextclade::io::file::open_file_or_stdin;
use nextclade::io::json::{json_or_yaml_write, json_stringify, JsonPretty};
use std::io::Read;

pub fn nextclade_read_annotation(args: &NextcladeReadAnnotationArgs) -> Result<(), Report> {
let content = {
let mut content = String::new();
open_file_or_stdin(&args.input_annotation)?.read_to_string(&mut content)?;
content
};

if args.feature_tree {
handle_feature_tree(args, &content)
} else {
handle_genome_annotation(args, &content)
}
}

fn handle_genome_annotation(args: &NextcladeReadAnnotationArgs, content: &str) -> Result<(), Report> {
let data = GeneMap::from_str(content)?;

if args.json {
println!("{}\n", json_stringify(&data, JsonPretty(true))?);
} else {
println!("{}", gene_map_to_table_string(&data)?);
}

if let Some(output) = &args.output {
json_or_yaml_write(output, &data)?;
}

Ok(())
}

fn handle_feature_tree(args: &NextcladeReadAnnotationArgs, content: &str) -> Result<(), Report> {
let data = FeatureTree::from_gff3_str(content)?;

if args.json {
println!("{}\n", json_stringify(&data, JsonPretty(true))?);
} else {
println!("{}", data.to_pretty_string()?);
}

if let Some(output) = &args.output {
json_or_yaml_write(output, &data)?;
}

Ok(())
}
11 changes: 11 additions & 0 deletions packages_rs/nextclade/src/io/json.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::io::file::create_file_or_stdout;
use crate::io::yaml::yaml_write;
use eyre::{Report, WrapErr};
use serde::{Deserialize, Serialize};
use serde_json::{de::Read, Deserializer};
Expand Down Expand Up @@ -61,3 +62,13 @@ pub fn json_write<T: Serialize>(filepath: impl AsRef<Path>, obj: &T, pretty: Jso
let file = create_file_or_stdout(filepath)?;
json_write_impl(file, &obj, pretty).wrap_err("When writing JSON to file: {filepath:#?}")
}

pub fn json_or_yaml_write<T: Serialize>(filepath: impl AsRef<Path>, obj: &T) -> Result<(), Report> {
let filepath = filepath.as_ref();
let filepath_str = filepath.to_string_lossy();
if filepath_str.ends_with("yaml") || filepath_str.ends_with("yml") {
yaml_write(filepath, &obj)
} else {
json_write(filepath, &obj, JsonPretty(true))
}
}

1 comment on commit 5fbe04c

@vercel
Copy link

@vercel vercel bot commented on 5fbe04c Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nextclade – ./

nextclade-git-master-nextstrain.vercel.app
nextclade-nextstrain.vercel.app
nextclade.vercel.app

Please sign in to comment.