use crate::types::serde_flex::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::upper_case_acronyms)]
pub struct CelestrakSATCATRecord {
#[serde(
rename = "OBJECT_NAME",
default,
deserialize_with = "flex_string::deserialize"
)]
pub object_name: Option<String>,
#[serde(
rename = "OBJECT_ID",
default,
deserialize_with = "flex_string::deserialize"
)]
pub object_id: Option<String>,
#[serde(
rename = "NORAD_CAT_ID",
default,
deserialize_with = "flex_u32::deserialize"
)]
pub norad_cat_id: Option<u32>,
#[serde(
rename = "OBJECT_TYPE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub object_type: Option<String>,
#[serde(
rename = "OPS_STATUS_CODE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub ops_status_code: Option<String>,
#[serde(
rename = "OWNER",
default,
deserialize_with = "flex_string::deserialize"
)]
pub owner: Option<String>,
#[serde(
rename = "LAUNCH_DATE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub launch_date: Option<String>,
#[serde(
rename = "LAUNCH_SITE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub launch_site: Option<String>,
#[serde(
rename = "DECAY_DATE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub decay_date: Option<String>,
#[serde(
rename = "PERIOD",
default,
deserialize_with = "flex_string::deserialize"
)]
pub period: Option<String>,
#[serde(
rename = "INCLINATION",
default,
deserialize_with = "flex_string::deserialize"
)]
pub inclination: Option<String>,
#[serde(
rename = "APOGEE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub apogee: Option<String>,
#[serde(
rename = "PERIGEE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub perigee: Option<String>,
#[serde(rename = "RCS", default, deserialize_with = "flex_string::deserialize")]
pub rcs: Option<String>,
#[serde(
rename = "DATA_STATUS_CODE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub data_status_code: Option<String>,
#[serde(
rename = "ORBIT_CENTER",
default,
deserialize_with = "flex_string::deserialize"
)]
pub orbit_center: Option<String>,
#[serde(
rename = "ORBIT_TYPE",
default,
deserialize_with = "flex_string::deserialize"
)]
pub orbit_type: Option<String>,
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn test_celestrak_satcat_record_deserialize_full() {
let json = r#"[{
"OBJECT_NAME": "ISS (ZARYA)",
"OBJECT_ID": "1998-067A",
"NORAD_CAT_ID": "25544",
"OBJECT_TYPE": "PAY",
"OPS_STATUS_CODE": "+",
"OWNER": "ISS",
"LAUNCH_DATE": "1998-11-20",
"LAUNCH_SITE": "TTMTR",
"DECAY_DATE": null,
"PERIOD": "92.87",
"INCLINATION": "51.64",
"APOGEE": "415",
"PERIGEE": "414",
"RCS": "0.0000",
"DATA_STATUS_CODE": "",
"ORBIT_CENTER": "EA",
"ORBIT_TYPE": "ORB"
}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
assert_eq!(records.len(), 1);
let record = &records[0];
assert_eq!(record.object_name.as_deref(), Some("ISS (ZARYA)"));
assert_eq!(record.object_id.as_deref(), Some("1998-067A"));
assert_eq!(record.norad_cat_id, Some(25544));
assert_eq!(record.object_type.as_deref(), Some("PAY"));
assert_eq!(record.ops_status_code.as_deref(), Some("+"));
assert_eq!(record.owner.as_deref(), Some("ISS"));
assert_eq!(record.launch_date.as_deref(), Some("1998-11-20"));
assert_eq!(record.launch_site.as_deref(), Some("TTMTR"));
assert!(record.decay_date.is_none());
assert_eq!(record.period.as_deref(), Some("92.87"));
assert_eq!(record.inclination.as_deref(), Some("51.64"));
assert_eq!(record.apogee.as_deref(), Some("415"));
assert_eq!(record.perigee.as_deref(), Some("414"));
assert_eq!(record.orbit_center.as_deref(), Some("EA"));
assert_eq!(record.orbit_type.as_deref(), Some("ORB"));
}
#[test]
fn test_celestrak_satcat_record_deserialize_minimal() {
let json = r#"[{
"OBJECT_NAME": "ISS (ZARYA)",
"NORAD_CAT_ID": "25544"
}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
assert_eq!(records.len(), 1);
assert_eq!(records[0].object_name.as_deref(), Some("ISS (ZARYA)"));
assert_eq!(records[0].norad_cat_id, Some(25544));
assert!(records[0].period.is_none());
assert!(records[0].decay_date.is_none());
}
#[test]
fn test_celestrak_satcat_record_unknown_fields_ignored() {
let json = r#"[{
"OBJECT_NAME": "ISS (ZARYA)",
"UNKNOWN_FIELD": "some_value",
"ANOTHER_UNKNOWN": 42
}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
assert_eq!(records.len(), 1);
assert_eq!(records[0].object_name.as_deref(), Some("ISS (ZARYA)"));
}
#[test]
fn test_celestrak_satcat_record_empty_array() {
let json = "[]";
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
assert!(records.is_empty());
}
#[test]
fn test_celestrak_satcat_record_clone() {
let json = r#"[{"OBJECT_NAME": "ISS", "NORAD_CAT_ID": "25544"}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
let cloned = records[0].clone();
assert_eq!(cloned.object_name, records[0].object_name);
}
#[test]
fn test_celestrak_satcat_record_debug() {
let json = r#"[{"OBJECT_NAME": "ISS"}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
let debug = format!("{:?}", records[0]);
assert!(debug.contains("ISS"));
}
#[test]
fn test_celestrak_satcat_record_serialize() {
let json = r#"[{"OBJECT_NAME":"ISS","NORAD_CAT_ID":"25544"}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
let serialized = serde_json::to_string(&records[0]).unwrap();
assert!(serialized.contains("OBJECT_NAME"));
assert!(serialized.contains("ISS"));
}
#[test]
fn test_celestrak_satcat_record_numeric_norad_cat_id() {
let json = r#"[{"OBJECT_NAME": "ISS", "NORAD_CAT_ID": 25544}]"#;
let records: Vec<CelestrakSATCATRecord> = serde_json::from_str(json).unwrap();
assert_eq!(records[0].norad_cat_id, Some(25544));
}
}