use std::{collections::BTreeMap, marker::PhantomData};
pub use field_expr::{
BinaryParam, ComponentFieldRefParam, CondParam, ConstParam, ConstValue, FieldExpression,
FieldRefParam, RecordRefParam, StringRepr, UnaryParam,
};
use serde::{Deserialize, Serialize};
use strum::AsRefStr;
mod field_expr;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ConstBagRef<T> {
const_bag_key: usize,
#[serde(skip)]
_phantom_data: PhantomData<T>,
}
impl<T> ConstBagRef<T> {
pub fn get_const_bag_ident(&self) -> String {
format!("__CONST_BAG_VALUE_{}", self.const_bag_key)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum ConstOrEnv<T> {
Const(T),
Env(ConstBagRef<T>),
}
#[derive(Serialize, Deserialize, Debug, Clone, AsRefStr)]
#[serde(tag = "opcode")]
pub enum GrassIR {
CastToBed(CastToBedParam),
Let(LetBinding),
Ref(RefParam),
Open(OpenParam),
WriteFile(WriteFileParam),
Alter(AlterParam),
Filter(FilterParam),
MergeOverlap(MergeOverlapParam),
Intersection(IntersectParam),
Format(FormatParam),
GroupBy(GroupByParam),
AssumeSorted(AssumeSortedParam),
InlineRust(InlineRustParam),
LoadGenomeFile(LoadGenomeFileParam),
SortedRandom(SortedRandomParam),
InternalSort(InternalSortParam),
Invert(InvertParam),
AssignTag(AssignTagParam),
TwoWayMerge(TwoWayMergeParam),
Limit(LimitParam),
Nop(NopParam),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LimitParam {
pub what: Box<GrassIR>,
pub count: ConstOrEnv<f64>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TwoWayMergeParam {
pub expr_1: Box<GrassIR>,
pub expr_2: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum TagValue {
String(String),
Int(i64),
Float(f64),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AssignTagParam {
pub inner: Box<GrassIR>,
pub tag: TagValue,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InvertParam {
pub inner: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InternalSortParam {
pub inner: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NopParam {
pub inner: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SortedRandomParam {
pub count: ConstOrEnv<usize>,
pub min_length: ConstOrEnv<u32>,
pub max_length: ConstOrEnv<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum LoadGenomeFileParam {
File(ConstOrEnv<String>),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct InlineRustParam {
pub env: BTreeMap<String, GrassIR>,
pub src: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RefParam {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GroupByParam {
#[serde(rename = "inner")]
pub expr: Box<GrassIR>,
pub keys: Vec<FieldExpression>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FormatParam {
#[serde(rename = "inner")]
pub expr: Box<GrassIR>,
pub fmt_str: String,
pub values: BTreeMap<String, FieldExpression>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum IntersectFlavor {
#[serde(rename = "inner")]
Inner,
#[serde(rename = "outer")]
Outer,
#[serde(rename = "left-outer")]
LeftOuter,
#[serde(rename = "right-outer")]
RightOuter,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct IntersectParam {
pub flavor: IntersectFlavor,
pub lhs: Box<GrassIR>,
pub rhs: Box<GrassIR>,
pub sorted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MergeOverlapParam {
#[serde(rename = "inner")]
pub input_expr: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FilterParam {
#[serde(rename = "inner")]
pub input_expr: Box<GrassIR>,
pub cond: FieldExpression,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AlterParam {
#[serde(rename = "inner")]
pub original_expr: Box<GrassIR>,
pub field: String,
pub value: FieldExpression,
pub sorted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AssumeSortedParam {
pub inner: Box<GrassIR>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CastToBedParam {
pub inner: Box<GrassIR>,
pub num_of_fields: u32,
pub sorted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum InputFormat {
Bam,
Bed,
Cram,
Vcf,
Fasta,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum OpenTarget {
Path(ConstOrEnv<String>),
FileNo(u32),
CmdArg(u32),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OpenParam {
pub target: OpenTarget,
pub format: InputFormat,
pub num_of_fields: i32,
pub compression: bool,
pub sorted: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum WriteTarget {
Path(ConstOrEnv<String>),
FileNo(i32),
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WriteFileParam {
pub what: Box<GrassIR>,
pub target: WriteTarget,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LetBinding {
pub id: String,
pub value: Box<GrassIR>,
}
#[cfg(test)]
mod test {
use std::{collections::BTreeMap, error::Error};
use serde::{Deserialize, Serialize};
use serde_json::from_str;
use crate::GrassIR;
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
#[serde(untagged)]
enum JsonValue {
String(String),
Number(f64),
Boolean(bool),
List(Vec<JsonValue>),
Object(BTreeMap<String, JsonValue>),
}
fn validate_object<'a, T: Serialize>(input: &str, obj: &'a T) {
let input_dict: JsonValue = serde_json::from_str(input).unwrap();
let obj_str = serde_json::to_string(obj).unwrap();
let obj_dict: JsonValue = serde_json::from_str(&obj_str).unwrap();
assert_eq!(obj_dict, input_dict);
}
macro_rules! parse_test {
($name: ident, $path : expr) => {
#[test]
fn $name() -> Result<(), Box<dyn Error>> {
let input = include_str!($path);
let data: GrassIR = from_str(input)?;
validate_object(input, &data);
Ok(())
}
};
}
parse_test!(parse_bam_to_bed, "../../data/ir/bam-to-bed.py.json");
parse_test!(
parse_expand_interval,
"../../data/ir/expand-interval.py.json"
);
parse_test!(parse_filter, "../../data/ir/filter.py.json");
parse_test!(parse_merge, "../../data/ir/merge.py.json");
parse_test!(parse_slop, "../../data/ir/slop.py.json");
parse_test!(
parse_sorted_intersect_custom_format,
"../../data/ir/sorted-intersect-custom-fmt.py.json"
);
parse_test!(
parse_sorted_intersect_groupby,
"../../data/ir/sorted-intersect-group.py.json"
);
parse_test!(
parse_sorted_intersect_leftouter,
"../../data/ir/sorted-intersect-leftouter.py.json"
);
parse_test!(
parse_sorted_intersect_overlap_filter,
"../../data/ir/sorted-intersect-overlap-filter.py.json"
);
parse_test!(
parse_sorted_intersect,
"../../data/ir/sorted-intersect.py.json"
);
parse_test!(parse_sorted_window, "../../data/ir/window.py.json");
}