use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct QueryRequest {
pub query_name: String,
pub query_id: String,
pub payload: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct QueryResponse {
pub ok: bool,
pub message: Option<String>,
pub payload: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QueryRequestValidationError {
EmptyQueryName,
EmptyQueryId,
InvalidQueryName,
InvalidQueryId,
PayloadTooLarge,
}
impl QueryRequest {
pub fn validate(&self, max_payload_bytes: usize) -> Result<(), QueryRequestValidationError> {
if self.query_name.trim().is_empty() {
return Err(QueryRequestValidationError::EmptyQueryName);
}
if self.query_id.trim().is_empty() {
return Err(QueryRequestValidationError::EmptyQueryId);
}
if self.query_name.len() > 128 || !is_valid_token(&self.query_name) {
return Err(QueryRequestValidationError::InvalidQueryName);
}
if self.query_id.len() > 128 || !is_valid_token(&self.query_id) {
return Err(QueryRequestValidationError::InvalidQueryId);
}
if self.payload_bytes().len() > max_payload_bytes {
return Err(QueryRequestValidationError::PayloadTooLarge);
}
Ok(())
}
pub fn payload_bytes(&self) -> Vec<u8> {
serde_json::to_vec(&self.payload).unwrap_or_default()
}
}
impl QueryResponse {
pub fn ok(payload: serde_json::Value) -> Self {
Self {
ok: true,
message: None,
payload,
}
}
pub fn rejected(message: impl Into<String>) -> Self {
Self {
ok: false,
message: Some(message.into()),
payload: serde_json::Value::Object(serde_json::Map::new()),
}
}
}
fn is_valid_token(value: &str) -> bool {
value
.bytes()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'.' | b'_' | b':' | b'-'))
}
#[cfg(test)]
#[path = "query_contract_tests.rs"]
mod tests;