use serde::{Deserialize, Serialize};
use std::collections::HashMap;
fn default_required() -> bool {
true
}
fn is_default_required(value: &bool) -> bool {
*value
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ModelType {
Analytical,
Ode,
Sde,
}
impl std::fmt::Display for ModelType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Analytical => write!(f, "analytical"),
Self::Ode => write!(f, "ode"),
Self::Sde => write!(f, "sde"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnalyticalFunction {
OneCompartment,
OneCompartmentWithAbsorption,
TwoCompartments,
TwoCompartmentsWithAbsorption,
ThreeCompartments,
ThreeCompartmentsWithAbsorption,
}
impl AnalyticalFunction {
pub fn rust_name(&self) -> &'static str {
match self {
Self::OneCompartment => "one_compartment",
Self::OneCompartmentWithAbsorption => "one_compartment_with_absorption",
Self::TwoCompartments => "two_compartments",
Self::TwoCompartmentsWithAbsorption => "two_compartments_with_absorption",
Self::ThreeCompartments => "three_compartments",
Self::ThreeCompartmentsWithAbsorption => "three_compartments_with_absorption",
}
}
pub fn expected_parameters(&self) -> Vec<&'static str> {
match self {
Self::OneCompartment => vec!["ke"],
Self::OneCompartmentWithAbsorption => vec!["ka", "ke"],
Self::TwoCompartments => vec!["ke", "kcp", "kpc"],
Self::TwoCompartmentsWithAbsorption => vec!["ke", "ka", "kcp", "kpc"],
Self::ThreeCompartments => vec!["k10", "k12", "k13", "k21", "k31"],
Self::ThreeCompartmentsWithAbsorption => {
vec!["ka", "k10", "k12", "k13", "k21", "k31"]
}
}
}
pub fn num_states(&self) -> usize {
match self {
Self::OneCompartment => 1,
Self::OneCompartmentWithAbsorption => 2,
Self::TwoCompartments => 2,
Self::TwoCompartmentsWithAbsorption => 3,
Self::ThreeCompartments => 3,
Self::ThreeCompartmentsWithAbsorption => 4,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Expression(pub String);
impl Expression {
pub fn new(s: impl Into<String>) -> Self {
Self(s.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.trim().is_empty()
}
}
impl From<String> for Expression {
fn from(s: String) -> Self {
Self(s)
}
}
impl From<&str> for Expression {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}
impl AsRef<str> for Expression {
fn as_ref(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ExpressionOrNumber {
Number(f64),
Expression(String),
}
impl ExpressionOrNumber {
pub fn to_rust_expr(&self) -> String {
match self {
Self::Number(n) => format!("{:.6}", n),
Self::Expression(s) => s.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DiffEqSpec {
Object(HashMap<String, String>),
}
impl DiffEqSpec {
pub fn is_empty(&self) -> bool {
match self {
Self::Object(m) => m.is_empty(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum InitSpec {
Object(HashMap<String, ExpressionOrNumber>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OutputDefinition {
pub id: String,
pub equation: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub units: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NamedEquation {
pub id: String,
pub equation: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CovariateDefinition {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<String>,
#[serde(
default = "default_required",
skip_serializing_if = "is_default_required"
)]
pub required: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub reference: Option<f64>,
}
impl CovariateDefinition {
pub fn symbol(&self) -> &str {
&self.id
}
pub fn column_name(&self) -> &str {
self.column.as_deref().unwrap_or(&self.id)
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct ExecutableModel {
pub id: String,
#[serde(rename = "type")]
pub model_type: ModelType,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub compartments: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub states: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub analytical: Option<AnalyticalFunction>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diffeq: Option<DiffEqSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
pub drift: Option<DiffEqSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
pub diffusion: Option<HashMap<String, ExpressionOrNumber>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub calculations: Vec<NamedEquation>,
pub outputs: Vec<OutputDefinition>,
#[serde(skip_serializing_if = "Option::is_none")]
pub init: Option<InitSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lag: Option<HashMap<String, ExpressionOrNumber>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fa: Option<HashMap<String, ExpressionOrNumber>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub neqs: Option<(usize, usize)>,
#[serde(skip_serializing_if = "Option::is_none")]
pub particles: Option<usize>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub covariates: Vec<CovariateDefinition>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Complexity {
Basic,
Intermediate,
Advanced,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Category {
Pk,
Pd,
Pkpd,
Disease,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Position {
pub x: f64,
pub y: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct DisplayInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(
alias = "shortName",
rename = "shortName",
skip_serializing_if = "Option::is_none"
)]
pub short_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<Category>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subcategory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub complexity: Option<Complexity>,
#[serde(skip_serializing_if = "Option::is_none")]
pub icon: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Reference {
#[serde(skip_serializing_if = "Option::is_none")]
pub authors: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub journal: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub year: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub doi: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pmid: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct EquationDocs {
#[serde(skip_serializing_if = "Option::is_none")]
pub differential: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub solution: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Documentation {
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub equations: Option<EquationDocs>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assumptions: Option<Vec<String>>,
#[serde(
alias = "whenToUse",
rename = "whenToUse",
skip_serializing_if = "Option::is_none"
)]
pub when_to_use: Option<Vec<String>>,
#[serde(
alias = "whenNotToUse",
rename = "whenNotToUse",
skip_serializing_if = "Option::is_none"
)]
pub when_not_to_use: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub references: Option<Vec<Reference>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct EditorInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub display: Option<DisplayInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub layout: Option<HashMap<String, Position>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub documentation: Option<Documentation>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Feature {
LagTime,
Bioavailability,
InitialConditions,
}