use std::sync::Arc;
use base64::Engine;
use serde_json::{json, Map, Value};
use sha2::{Digest, Sha256};
use crate::error::Error;
use crate::http::{HttpTransport, QueryParams};
use crate::models::documents::*;
use crate::validate::assert_sha256_hex;
#[derive(Clone)]
pub struct DocumentsResource {
t: Arc<HttpTransport>,
}
impl DocumentsResource {
pub(crate) fn new(t: Arc<HttpTransport>) -> Self {
Self { t }
}
pub async fn anchor(
&self,
params: AnchorDocumentParams,
) -> Result<AnchorDocumentResponse, Error> {
assert_sha256_hex("document_hash", ¶ms.document_hash)?;
let mut body = Map::new();
body.insert("document_hash".into(), json!(params.document_hash));
if let Some(v) = ¶ms.document_ref {
body.insert("document_ref".into(), json!(v));
}
if let Some(v) = ¶ms.event_type {
body.insert("event_type".into(), json!(v));
}
if let Some(v) = ¶ms.original_bytes_b64 {
body.insert("original_bytes_b64".into(), json!(v));
}
if let Some(v) = params.metadata {
body.insert("metadata".into(), Value::Object(v));
}
if let Some(v) = ¶ms.trace_id {
body.insert("trace_id".into(), json!(v));
}
self.t
.post(
"/document/anchor",
Some(&Value::Object(body)),
params.idempotency_key.as_deref(),
)
.await
}
pub async fn anchor_file(
&self,
params: AnchorFileParams,
) -> Result<AnchorDocumentResponse, Error> {
let (content, default_ref): (Vec<u8>, Option<String>) = match ¶ms.file {
FileSource::Path(p) => {
let bytes = std::fs::read(p).map_err(|e| {
Error::validation(format!("failed to read file {}: {e}", p.display()))
})?;
let base = p.file_name().and_then(|s| s.to_str()).map(str::to_string);
(bytes, base)
}
FileSource::Bytes(b) => (b.clone(), None),
};
let mut hasher = Sha256::new();
hasher.update(&content);
let document_hash = hex::encode(hasher.finalize());
let document_ref = params.document_ref.or(default_ref);
let original_bytes_b64 = if params.skip_original {
None
} else {
Some(base64::engine::general_purpose::STANDARD.encode(&content))
};
self.anchor(AnchorDocumentParams {
document_hash,
document_ref,
event_type: params.event_type,
original_bytes_b64,
metadata: params.metadata,
idempotency_key: params.idempotency_key,
trace_id: params.trace_id,
})
.await
}
pub async fn list(&self, params: ListDocumentsParams) -> Result<ListDocumentsResponse, Error> {
let q: QueryParams = vec![
("page", params.page.map(|v| v.to_string())),
("limit", params.limit.map(|v| v.to_string())),
("date_from", params.date_from),
("date_to", params.date_to),
("document_ref", params.document_ref),
];
self.t.get("/document", Some(q)).await
}
pub async fn get(&self, event_id: &str) -> Result<DocumentEvent, Error> {
self.t.get(&format!("/document/{event_id}"), None).await
}
pub async fn get_original(&self, event_id: &str) -> Result<Vec<u8>, Error> {
self.t
.get_bytes(&format!("/document/{event_id}/original"))
.await
}
pub async fn verify(
&self,
event_id: &str,
params: VerifyDocumentParams,
) -> Result<VerifyDocumentResponse, Error> {
assert_sha256_hex("document_hash", ¶ms.document_hash)?;
let body = json!({ "document_hash": params.document_hash });
self.t
.post(&format!("/document/{event_id}/verify"), Some(&body), None)
.await
}
}