use std::collections::HashMap;
use std::sync::Arc;
#[cfg(feature = "radar_examples")]
use ndarray::Array1;
#[cfg(feature = "radar_examples")]
use num_complex::Complex;
#[cfg(feature = "python")]
use pyo3::PyObject;
#[derive(Clone, Debug)]
pub enum GraphData {
Int(i64),
Float(f64),
String(String),
FloatVec(Arc<Vec<f64>>),
IntVec(Arc<Vec<i64>>),
#[cfg(feature = "radar_examples")]
Complex(Complex<f64>),
#[cfg(feature = "radar_examples")]
FloatArray(Arc<Array1<f64>>),
#[cfg(feature = "radar_examples")]
ComplexArray(Arc<Array1<Complex<f64>>>),
Map(HashMap<String, GraphData>),
#[cfg(feature = "python")]
PyObject(PyObject),
None,
}
impl GraphData {
pub fn int(value: i64) -> Self {
GraphData::Int(value)
}
pub fn float(value: f64) -> Self {
GraphData::Float(value)
}
pub fn string(value: impl Into<String>) -> Self {
GraphData::String(value.into())
}
pub fn float_vec(value: Vec<f64>) -> Self {
GraphData::FloatVec(Arc::new(value))
}
pub fn int_vec(value: Vec<i64>) -> Self {
GraphData::IntVec(Arc::new(value))
}
pub fn map(value: HashMap<String, GraphData>) -> Self {
GraphData::Map(value)
}
pub fn none() -> Self {
GraphData::None
}
#[cfg(feature = "radar_examples")]
pub fn complex(value: Complex<f64>) -> Self {
GraphData::Complex(value)
}
#[cfg(feature = "radar_examples")]
pub fn float_array(value: Array1<f64>) -> Self {
GraphData::FloatArray(Arc::new(value))
}
#[cfg(feature = "radar_examples")]
pub fn complex_array(value: Array1<Complex<f64>>) -> Self {
GraphData::ComplexArray(Arc::new(value))
}
#[cfg(feature = "python")]
pub fn py_object(value: PyObject) -> Self {
GraphData::PyObject(value)
}
pub fn as_int(&self) -> Option<i64> {
match self {
GraphData::Int(v) => Some(*v),
_ => None,
}
}
pub fn as_float(&self) -> Option<f64> {
match self {
GraphData::Float(v) => Some(*v),
GraphData::Int(v) => Some(*v as f64),
_ => None,
}
}
pub fn as_f64_lossy(&self) -> Option<f64> {
match self {
GraphData::Float(v) => Some(*v),
GraphData::Int(v) => Some(*v as f64),
#[cfg(feature = "python")]
GraphData::PyObject(obj) => {
pyo3::Python::with_gil(|py| {
obj.extract::<f64>(py)
.ok()
.or_else(|| obj.extract::<i64>(py).ok().map(|i| i as f64))
})
}
_ => None,
}
}
pub fn as_string(&self) -> Option<&str> {
match self {
GraphData::String(s) => Some(s.as_str()),
_ => None,
}
}
pub fn as_float_vec(&self) -> Option<&Vec<f64>> {
match self {
GraphData::FloatVec(v) => Some(v.as_ref()),
_ => None,
}
}
pub fn as_int_vec(&self) -> Option<&Vec<i64>> {
match self {
GraphData::IntVec(v) => Some(v.as_ref()),
_ => None,
}
}
pub fn as_map(&self) -> Option<&HashMap<String, GraphData>> {
match self {
GraphData::Map(m) => Some(m),
_ => None,
}
}
#[cfg(feature = "radar_examples")]
pub fn as_complex(&self) -> Option<Complex<f64>> {
match self {
GraphData::Complex(c) => Some(*c),
_ => None,
}
}
#[cfg(feature = "radar_examples")]
pub fn as_float_array(&self) -> Option<&Array1<f64>> {
match self {
GraphData::FloatArray(a) => Some(a.as_ref()),
_ => None,
}
}
#[cfg(feature = "radar_examples")]
pub fn as_complex_array(&self) -> Option<&Array1<Complex<f64>>> {
match self {
GraphData::ComplexArray(a) => Some(a.as_ref()),
_ => None,
}
}
#[cfg(feature = "python")]
pub fn as_py_object(&self) -> Option<&PyObject> {
match self {
GraphData::PyObject(obj) => Some(obj),
_ => None,
}
}
pub fn is_none(&self) -> bool {
matches!(self, GraphData::None)
}
pub fn to_string_repr(&self) -> String {
match self {
GraphData::Int(v) => v.to_string(),
GraphData::Float(v) => v.to_string(),
GraphData::String(s) => s.clone(),
GraphData::FloatVec(v) => format!("{:?}", v),
GraphData::IntVec(v) => format!("{:?}", v),
#[cfg(feature = "radar_examples")]
GraphData::Complex(c) => format!("{:?}", c),
#[cfg(feature = "radar_examples")]
GraphData::FloatArray(a) => format!("{:?}", a),
#[cfg(feature = "radar_examples")]
GraphData::ComplexArray(a) => format!("{:?}", a),
GraphData::Map(m) => format!("{:?}", m),
#[cfg(feature = "python")]
GraphData::PyObject(_) => "<PyObject>".to_string(),
GraphData::None => "None".to_string(),
}
}
pub fn from_string(s: &str) -> Self {
if let Ok(v) = s.parse::<i64>() {
return GraphData::Int(v);
}
if let Ok(v) = s.parse::<f64>() {
return GraphData::Float(v);
}
GraphData::String(s.to_string())
}
}
impl Default for GraphData {
fn default() -> Self {
GraphData::None
}
}
impl From<i64> for GraphData {
fn from(v: i64) -> Self {
GraphData::Int(v)
}
}
impl From<f64> for GraphData {
fn from(v: f64) -> Self {
GraphData::Float(v)
}
}
impl From<String> for GraphData {
fn from(v: String) -> Self {
GraphData::String(v)
}
}
impl From<&str> for GraphData {
fn from(v: &str) -> Self {
GraphData::String(v.to_string())
}
}
impl From<Vec<f64>> for GraphData {
fn from(v: Vec<f64>) -> Self {
GraphData::FloatVec(Arc::new(v))
}
}
impl From<Vec<i64>> for GraphData {
fn from(v: Vec<i64>) -> Self {
GraphData::IntVec(Arc::new(v))
}
}
#[cfg(feature = "radar_examples")]
impl From<Complex<f64>> for GraphData {
fn from(v: Complex<f64>) -> Self {
GraphData::Complex(v)
}
}
#[cfg(feature = "radar_examples")]
impl From<Array1<f64>> for GraphData {
fn from(v: Array1<f64>) -> Self {
GraphData::FloatArray(Arc::new(v))
}
}
#[cfg(feature = "radar_examples")]
impl From<Array1<Complex<f64>>> for GraphData {
fn from(v: Array1<Complex<f64>>) -> Self {
GraphData::ComplexArray(Arc::new(v))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_int_construction() {
let data = GraphData::int(42);
assert_eq!(data.as_int(), Some(42));
assert_eq!(data.as_float(), Some(42.0));
}
#[test]
fn test_float_construction() {
let data = GraphData::float(3.14);
assert_eq!(data.as_float(), Some(3.14));
assert!(data.as_int().is_none());
}
#[test]
fn test_string_construction() {
let data = GraphData::string("hello");
assert_eq!(data.as_string(), Some("hello"));
}
#[test]
fn test_float_vec_construction() {
let data = GraphData::float_vec(vec![1.0, 2.0, 3.0]);
assert_eq!(data.as_float_vec(), Some(&vec![1.0, 2.0, 3.0]));
}
#[test]
fn test_int_vec_construction() {
let data = GraphData::int_vec(vec![1, 2, 3]);
assert_eq!(data.as_int_vec(), Some(&vec![1, 2, 3]));
}
#[test]
fn test_map_construction() {
let mut map = HashMap::new();
map.insert("x".to_string(), GraphData::int(10));
map.insert("y".to_string(), GraphData::float(20.5));
let data = GraphData::map(map);
let extracted = data.as_map().unwrap();
assert_eq!(extracted.get("x").and_then(|d| d.as_int()), Some(10));
assert_eq!(extracted.get("y").and_then(|d| d.as_float()), Some(20.5));
}
#[test]
fn test_none_construction() {
let data = GraphData::none();
assert!(data.is_none());
}
#[test]
fn test_from_conversions() {
let d1: GraphData = 42i64.into();
assert_eq!(d1.as_int(), Some(42));
let d2: GraphData = 3.14f64.into();
assert_eq!(d2.as_float(), Some(3.14));
let d3: GraphData = "test".into();
assert_eq!(d3.as_string(), Some("test"));
let d4: GraphData = vec![1.0, 2.0].into();
assert_eq!(d4.as_float_vec(), Some(&vec![1.0, 2.0]));
}
#[test]
fn test_to_string_repr() {
assert_eq!(GraphData::int(42).to_string_repr(), "42");
assert_eq!(GraphData::float(3.14).to_string_repr(), "3.14");
assert_eq!(GraphData::string("test").to_string_repr(), "test");
assert!(GraphData::none().to_string_repr().contains("None"));
}
#[test]
fn test_from_string() {
let d1 = GraphData::from_string("42");
assert_eq!(d1.as_int(), Some(42));
let d2 = GraphData::from_string("3.14");
assert_eq!(d2.as_float(), Some(3.14));
let d3 = GraphData::from_string("not a number");
assert_eq!(d3.as_string(), Some("not a number"));
}
}