diff --git a/hosts/ureq/examples/azure_get.rs b/hosts/ureq/examples/azure_get.rs index e1a5397..1670c7a 100644 --- a/hosts/ureq/examples/azure_get.rs +++ b/hosts/ureq/examples/azure_get.rs @@ -3,6 +3,7 @@ use std::env; use std::io::Write; +use borink_object_storage_proto::ListOptions; use borink_object_storage_proto::azure::Container; use borink_object_storage_ureq::azure; @@ -12,6 +13,7 @@ fn main() -> Result<(), Box> { let token = env::var("AZURE_STORAGE_ACCESS_TOKEN")?; let key = env::args().nth(1).ok_or("missing object key")?; let container = Container::new(&endpoint, &container)?; - std::io::stdout().write_all(&azure::get(&container, &token, &key)?)?; + let mut blobs = azure::Blobs::new(&container, &token, &ListOptions::default())?; + std::io::stdout().write_all(&blobs.get(&key)?)?; Ok(()) } diff --git a/hosts/ureq/examples/azure_list.rs b/hosts/ureq/examples/azure_list.rs index e90e759..9260dc4 100644 --- a/hosts/ureq/examples/azure_list.rs +++ b/hosts/ureq/examples/azure_list.rs @@ -2,9 +2,8 @@ use std::env; -use borink_object_storage_proto::azure as proto; -use borink_object_storage_proto::azure::{AzureNamespace, Container}; -use borink_object_storage_proto::{Error, ListEntry, ListItem, ListMarker, ListOptions}; +use borink_object_storage_proto::azure::Container; +use borink_object_storage_proto::{ListEntry, ListItem, ListMarker, ListOptions}; use borink_object_storage_ureq::azure; fn main() -> Result<(), Box> { @@ -14,21 +13,12 @@ fn main() -> Result<(), Box> { let prefix = env::args().nth(1).unwrap_or_default(); let container = Container::new(&endpoint, &container)?; - // Every page shares the template: an empty buffer is refused with the - // size it needs. + // Every page shares the client's list template. let options = ListOptions { max_results: Some(1000), ..ListOptions::default() }; - let prepare = |buf: &mut [u8]| { - proto::prepare_list(&container, &token, AzureNamespace::Unknown, &options, buf) - }; - let size = match prepare(&mut []) { - Err(Error::Capacity(capacity)) => capacity.required, - other => return Err(format!("{other:?}").into()), - }; - let mut bytes = vec![0; size]; - let template = prepare(&mut bytes)?; + let mut blobs = azure::Blobs::new(&container, &token, &options)?; let mut marker: Option = None; let mut body = Vec::new(); @@ -41,7 +31,7 @@ fn main() -> Result<(), Box> { marker: marker.as_deref().map(ListMarker::Text), ..ListItem::new(&prefix) }; - let page = azure::list(&template, &bytes, &item, &mut body, &mut entries)?; + let page = blobs.list(&item, &mut body, &mut entries)?; for entry in &entries[..page.filled] { println!("{}", entry.key); } diff --git a/hosts/ureq/examples/s3_get.rs b/hosts/ureq/examples/s3_get.rs index 06d232f..c0e360e 100644 --- a/hosts/ureq/examples/s3_get.rs +++ b/hosts/ureq/examples/s3_get.rs @@ -5,6 +5,7 @@ use std::env; use std::io::Write; +use borink_object_storage_proto::ListOptions; use borink_object_storage_proto::s3::{Bucket, Service}; use borink_object_storage_proto::sigv4::Credentials; use borink_object_storage_ureq::s3; @@ -27,6 +28,7 @@ fn main() -> Result<(), Box> { if let Some(token) = &session_token { credentials = credentials.with_session_token(token)?; } - std::io::stdout().write_all(&s3::get(&bucket, &credentials, &key)?)?; + let mut objects = s3::Objects::new(&bucket, credentials, &ListOptions::default())?; + std::io::stdout().write_all(&objects.get(&key)?)?; Ok(()) } diff --git a/hosts/ureq/examples/s3_list.rs b/hosts/ureq/examples/s3_list.rs index 39726d5..abfb576 100644 --- a/hosts/ureq/examples/s3_list.rs +++ b/hosts/ureq/examples/s3_list.rs @@ -4,9 +4,9 @@ use std::env; -use borink_object_storage_proto::s3::{self as proto, Bucket, Service}; +use borink_object_storage_proto::s3::{Bucket, Service}; use borink_object_storage_proto::sigv4::Credentials; -use borink_object_storage_proto::{Error, ListEntry, ListItem, ListMarker, ListOptions}; +use borink_object_storage_proto::{ListEntry, ListItem, ListMarker, ListOptions}; use borink_object_storage_ureq::s3; fn main() -> Result<(), Box> { @@ -28,19 +28,12 @@ fn main() -> Result<(), Box> { credentials = credentials.with_session_token(token)?; } - // Every page shares the template: an empty buffer is refused with the - // size it needs. + // Every page shares the client's list template. let options = ListOptions { max_results: Some(1000), ..ListOptions::default() }; - let prepare = |buf: &mut [u8]| proto::prepare_list(&bucket, &credentials, &options, buf); - let size = match prepare(&mut []) { - Err(Error::Capacity(capacity)) => capacity.required, - other => return Err(format!("{other:?}").into()), - }; - let mut bytes = vec![0; size]; - let template = prepare(&mut bytes)?; + let mut objects = s3::Objects::new(&bucket, credentials, &options)?; let mut marker: Option = None; let mut body = Vec::new(); @@ -52,14 +45,7 @@ fn main() -> Result<(), Box> { marker: marker.as_deref().map(ListMarker::Text), ..ListItem::new(&prefix) }; - let page = s3::list( - &template, - &bytes, - &credentials, - &item, - &mut body, - &mut entries, - )?; + let page = objects.list(&item, &mut body, &mut entries)?; for entry in &entries[..page.filled] { println!("{}", entry.key); } diff --git a/hosts/ureq/src/azure.rs b/hosts/ureq/src/azure.rs index ff192c1..4bb74d9 100644 --- a/hosts/ureq/src/azure.rs +++ b/hosts/ureq/src/azure.rs @@ -3,43 +3,177 @@ use borink_object_storage_proto::azure::{self, Template}; use borink_object_storage_proto::azure::{AzureNamespace, Container}; use borink_object_storage_proto::{ - CapacityError, DeleteHeadOutcome, DeleteOptions, GetHeadOutcome, GetItem, GetOptions, - ListEntry, ListHeadOutcome, ListItem, Listing, ObjectItem, Payload, PutHeadOutcome, PutItem, + DeleteHeadOutcome, DeleteOptions, Error, GetHeadOutcome, GetItem, GetOptions, ListEntry, + ListHeadOutcome, ListItem, ListOptions, Listing, ObjectItem, Payload, PutHeadOutcome, PutItem, PutOptions, }; -/// Builds and executes one GET request, returning an owned response body. +use crate::Prepared; + +/// A client of one Azure container, with one bearer token. /// -/// The template is written for this one read. A host that reads many -/// objects writes it once and keeps it. -pub fn get( - container: &Container<'_>, - token: &str, - key: &str, -) -> Result, Box> { - let now = crate::now()?; - let options = GetOptions::new(); - let prepare = |buf: &mut [u8]| { - azure::prepare_get(container, token, AzureNamespace::Unknown, &options, buf) - }; - let (template, bytes) = crate::sized_template(prepare)?; - let item = GetItem::new(key); - let encode = |buf: &mut [u8]| azure::encode_get(&template, &bytes, &item, &now, buf).map(drop); - let mut buf = vec![0; CapacityError::required_by(encode)?]; - let request = azure::encode_get(&template, &bytes, &item, &now, &mut buf)?; - - let (parts, mut body) = crate::send(&request, None)?; - let shape = item.shape(&options); - match azure::accept_get_head(shape, crate::response_head(&parts))? { - GetHeadOutcome::Body { .. } => body.read_to_vec().map_err(Into::into), - GetHeadOutcome::Complete { .. } => Ok(Vec::new()), - // Azure named no error in the head, so the body names it. - GetHeadOutcome::NeedErrorBody(failure) => Err(no_object(azure::accept_get_error_body( - shape, - failure, - &crate::error_body(&mut body), - ))), - outcome => Err(no_object(outcome)), +/// It writes the template of each kind of request once, when it is made, +/// and writes every request into one buffer that it keeps. Once that buffer +/// has grown to fit the longest request, each request is written once. It +/// sends with one `ureq` agent, which keeps its connections. +pub struct Blobs { + get: Prepared