use serde::{Deserialize, Serialize};
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Request {
Connect {
id: String,
user: String,
password: String,
},
Sql {
id: String,
sql: String,
#[serde(skip_serializing_if = "Option::is_none")]
rows: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
parameters: Option<Vec<serde_json::Value>>,
},
PrepareSql {
id: String,
sql: String,
},
PrepareSqlExecute {
id: String,
sql: String,
#[serde(skip_serializing_if = "Option::is_none")]
parameters: Option<Vec<Vec<serde_json::Value>>>,
#[serde(skip_serializing_if = "Option::is_none")]
rows: Option<u32>,
},
Execute {
id: String,
cont_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
parameters: Option<Vec<serde_json::Value>>,
},
#[serde(rename = "sqlmore")]
SqlMore {
id: String,
cont_id: String,
rows: u32,
},
#[serde(rename = "sqlclose")]
SqlClose {
id: String,
cont_id: String,
},
Cl {
id: String,
cmd: String,
},
#[serde(rename = "getversion")]
GetVersion {
id: String,
},
#[serde(rename = "getdbjob")]
GetDbJob {
id: String,
},
#[serde(rename = "setconfig")]
SetConfig {
id: String,
tracelevel: String,
tracedest: String,
},
#[serde(rename = "gettracedata")]
GetTraceData {
id: String,
},
Dove {
id: String,
sql: String,
},
Ping {
id: String,
},
Exit {
id: String,
},
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn ping_round_trips() {
let r = Request::Ping { id: "1".into() };
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, r#"{"type":"ping","id":"1"}"#);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Ping { id } if id == "1"));
}
#[test]
fn connect_round_trips() {
let r = Request::Connect {
id: "2".into(),
user: "DCURTIS".into(),
password: "hunter2".into(),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(
json,
r#"{"type":"connect","id":"2","user":"DCURTIS","password":"hunter2"}"#
);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Connect { user, .. } if user == "DCURTIS"));
}
#[test]
fn exit_round_trips() {
let r = Request::Exit { id: "3".into() };
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, r#"{"type":"exit","id":"3"}"#);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Exit { id } if id == "3"));
}
#[test]
fn sql_round_trips_with_params() {
let r = Request::Sql {
id: "10".into(),
sql: "SELECT * FROM ORDERS WHERE ID = ?".into(),
rows: Some(100),
parameters: Some(vec![serde_json::json!(42)]),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(
json,
r#"{"type":"sql","id":"10","sql":"SELECT * FROM ORDERS WHERE ID = ?","rows":100,"parameters":[42]}"#
);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Sql { id, .. } if id == "10"));
}
#[test]
fn sql_round_trips_minimal() {
let r = Request::Sql {
id: "11".into(),
sql: "SELECT 1 FROM SYSIBM.SYSDUMMY1".into(),
rows: None,
parameters: None,
};
let json = serde_json::to_string(&r).unwrap();
assert!(!json.contains(r#""rows""#));
assert!(!json.contains(r#""parameters""#));
let _back: Request = serde_json::from_str(&json).unwrap();
}
#[test]
fn prepare_sql_round_trips() {
let r = Request::PrepareSql {
id: "12".into(),
sql: "SELECT * FROM T WHERE A = ?".into(),
};
let json = serde_json::to_string(&r).unwrap();
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::PrepareSql { id, .. } if id == "12"));
}
#[test]
fn prepare_sql_execute_round_trips_batched() {
let r = Request::PrepareSqlExecute {
id: "13".into(),
sql: "INSERT INTO T VALUES(?,?)".into(),
parameters: Some(vec![
vec![serde_json::json!(1), serde_json::json!("a")],
vec![serde_json::json!(2), serde_json::json!("b")],
]),
rows: None,
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(
json,
r#"{"type":"prepare_sql_execute","id":"13","sql":"INSERT INTO T VALUES(?,?)","parameters":[[1,"a"],[2,"b"]]}"#
);
let _back: Request = serde_json::from_str(&json).unwrap();
}
#[test]
fn execute_round_trips() {
let r = Request::Execute {
id: "14".into(),
cont_id: "stmt-7".into(),
parameters: Some(vec![serde_json::json!("hello")]),
};
let json = serde_json::to_string(&r).unwrap();
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Execute { cont_id, .. } if cont_id == "stmt-7"));
}
#[test]
fn sqlmore_round_trips() {
let r = Request::SqlMore {
id: "20".into(),
cont_id: "cur-1".into(),
rows: 100,
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(
json,
r#"{"type":"sqlmore","id":"20","cont_id":"cur-1","rows":100}"#
);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::SqlMore { rows, .. } if rows == 100));
}
#[test]
fn sqlclose_round_trips() {
let r = Request::SqlClose {
id: "21".into(),
cont_id: "cur-1".into(),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, r#"{"type":"sqlclose","id":"21","cont_id":"cur-1"}"#);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::SqlClose { cont_id, .. } if cont_id == "cur-1"));
}
#[test]
fn cl_round_trips() {
let r = Request::Cl {
id: "30".into(),
cmd: "WRKACTJOB".into(),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, r#"{"type":"cl","id":"30","cmd":"WRKACTJOB"}"#);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Cl { cmd, .. } if cmd == "WRKACTJOB"));
}
#[test]
fn metadata_requests_round_trip_with_bare_tags() {
let cases: [(Request, &str); 3] = [
(
Request::GetVersion { id: "40".into() },
r#"{"type":"getversion","id":"40"}"#,
),
(
Request::GetDbJob { id: "41".into() },
r#"{"type":"getdbjob","id":"41"}"#,
),
(
Request::GetTraceData { id: "42".into() },
r#"{"type":"gettracedata","id":"42"}"#,
),
];
for (r, expected) in cases {
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, expected);
let _back: Request = serde_json::from_str(&json).unwrap();
}
}
#[test]
fn setconfig_round_trips() {
let r = Request::SetConfig {
id: "50".into(),
tracelevel: "DATASTREAM".into(),
tracedest: "FILE".into(),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(
json,
r#"{"type":"setconfig","id":"50","tracelevel":"DATASTREAM","tracedest":"FILE"}"#
);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(
matches!(back, Request::SetConfig { tracelevel, .. } if tracelevel == "DATASTREAM")
);
}
#[test]
fn dove_round_trips() {
let r = Request::Dove {
id: "60".into(),
sql: "SELECT * FROM T".into(),
};
let json = serde_json::to_string(&r).unwrap();
assert_eq!(json, r#"{"type":"dove","id":"60","sql":"SELECT * FROM T"}"#);
let back: Request = serde_json::from_str(&json).unwrap();
assert!(matches!(back, Request::Dove { id, .. } if id == "60"));
}
}