appcore_api/
query_contract.rs1use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct QueryRequest {
18 pub query_name: String,
20 pub query_id: String,
22 pub payload: serde_json::Value,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct QueryResponse {
29 pub ok: bool,
31 pub message: Option<String>,
33 pub payload: serde_json::Value,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum QueryRequestValidationError {
40 EmptyQueryName,
42 EmptyQueryId,
44 InvalidQueryName,
46 InvalidQueryId,
48 PayloadTooLarge,
50}
51
52impl QueryRequest {
53 pub fn validate(&self, max_payload_bytes: usize) -> Result<(), QueryRequestValidationError> {
55 if self.query_name.trim().is_empty() {
56 return Err(QueryRequestValidationError::EmptyQueryName);
57 }
58 if self.query_id.trim().is_empty() {
59 return Err(QueryRequestValidationError::EmptyQueryId);
60 }
61 if self.query_name.len() > 128 || !is_valid_token(&self.query_name) {
62 return Err(QueryRequestValidationError::InvalidQueryName);
63 }
64 if self.query_id.len() > 128 || !is_valid_token(&self.query_id) {
65 return Err(QueryRequestValidationError::InvalidQueryId);
66 }
67 if self.payload_bytes().len() > max_payload_bytes {
68 return Err(QueryRequestValidationError::PayloadTooLarge);
69 }
70 Ok(())
71 }
72
73 pub fn payload_bytes(&self) -> Vec<u8> {
75 serde_json::to_vec(&self.payload).unwrap_or_default()
76 }
77}
78
79impl QueryResponse {
80 pub fn ok(payload: serde_json::Value) -> Self {
82 Self {
83 ok: true,
84 message: None,
85 payload,
86 }
87 }
88
89 pub fn rejected(message: impl Into<String>) -> Self {
91 Self {
92 ok: false,
93 message: Some(message.into()),
94 payload: serde_json::Value::Object(serde_json::Map::new()),
95 }
96 }
97}
98
99fn is_valid_token(value: &str) -> bool {
100 value
101 .bytes()
102 .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'.' | b'_' | b':' | b'-'))
103}
104
105#[cfg(test)]
106#[path = "query_contract_tests.rs"]
107mod tests;