Mercurial > lbo > hg > async-google-apis
changeset 122:779081e4efe2
gcs_example: Enable more features and encode mandatory parameters
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 25 Oct 2020 18:22:02 +0100 |
parents | f14c93b7ab21 |
children | 907bfb45511b |
files | async-google-apis-common/src/http.rs drive_example/README.md gcs_example/README.md gcs_example/src/main.rs gcs_example/src/storage_v1_types.rs generate/generate.py generate/templates.py |
diffstat | 7 files changed, 239 insertions(+), 129 deletions(-) [+] |
line wrap: on
line diff
--- a/async-google-apis-common/src/http.rs Sun Oct 25 15:08:03 2020 +0100 +++ b/async-google-apis-common/src/http.rs Sun Oct 25 18:22:02 2020 +0100 @@ -145,13 +145,16 @@ } } -pub async fn do_download<Req: Serialize + std::fmt::Debug, Resp: DeserializeOwned + std::fmt::Debug>( +pub async fn do_download< + Req: Serialize + std::fmt::Debug, + Resp: DeserializeOwned + std::fmt::Debug, +>( cl: &TlsClient, path: &str, headers: &[(hyper::header::HeaderName, String)], http_method: &str, rq: Option<Req>, - dst: Option<&mut dyn std::io::Write>, + dst: Option<&mut (dyn tokio::io::AsyncWrite + std::marker::Unpin)>, ) -> Result<DownloadResponse<Resp>> { let mut path = path.to_string(); let mut http_response; @@ -222,25 +225,22 @@ serde_json::from_reader(response_body.as_ref()) .map_err(|e| anyhow::Error::from(e).context(body_to_str(response_body))) .map(DownloadResponse::Response) - } + }; } } - let response_body = http_response.unwrap().into_body(); + + use tokio::io::AsyncWriteExt; + let mut response_body = http_response.unwrap().into_body(); if let Some(dst) = dst { - let write_results = response_body - .map(move |chunk| { - dst.write(chunk?.as_ref()) - .map(|_| ()) - .map_err(anyhow::Error::from) - }) - .collect::<Vec<Result<()>>>() - .await; - if let Some(e) = write_results.into_iter().find(|r| r.is_err()) { - return Err(e.unwrap_err()); + while let Some(chunk) = tokio::stream::StreamExt::next(&mut response_body).await { + dst.write(chunk?.as_ref()).await?; } Ok(DownloadResponse::Downloaded) } else { - Err(ApiError::DataAvailableError("do_download: No destination for downloaded data was specified".into()).into()) + Err(ApiError::DataAvailableError( + "do_download: No destination for downloaded data was specified".into(), + ) + .into()) } } @@ -286,8 +286,11 @@ } } pub fn set_max_chunksize(&mut self, size: usize) -> Result<&mut Self> { - if size % (1024*256) != 0 { - Err(ApiError::InputDataError("ResumableUpload: max_chunksize must be multiple of 256 KiB.".into()).into()) + if size % (1024 * 256) != 0 { + Err(ApiError::InputDataError( + "ResumableUpload: max_chunksize must be multiple of 256 KiB.".into(), + ) + .into()) } else { self.max_chunksize = size; Ok(self)
--- a/drive_example/README.md Sun Oct 25 15:08:03 2020 +0100 +++ b/drive_example/README.md Sun Oct 25 18:22:02 2020 +0100 @@ -16,3 +16,6 @@ [Developer Console](https://console.developers.google.com) and place it into the file `client_secret.json` in your working directory so that `drive_example` can find it. + +Run with `RUST_LOG=debug` in order to see an accurate record of HTTP requests +being sent and received.
--- a/gcs_example/README.md Sun Oct 25 15:08:03 2020 +0100 +++ b/gcs_example/README.md Sun Oct 25 18:22:02 2020 +0100 @@ -29,3 +29,6 @@ ```bash $ gcs_example --help ``` + +Run with `RUST_LOG=debug` in order to see an accurate record of HTTP requests +being sent and received.
--- a/gcs_example/src/main.rs Sun Oct 25 15:08:03 2020 +0100 +++ b/gcs_example/src/main.rs Sun Oct 25 18:22:02 2020 +0100 @@ -20,7 +20,7 @@ ) -> common::Result<()> { let mut params = storage_v1_types::ObjectsInsertParams::default(); params.bucket = bucket.into(); - params.name = Some("test_directory/".to_string() + p.file_name().unwrap().to_str().unwrap()); + params.name = Some(p.file_name().unwrap().to_str().unwrap().into()); let obj = storage_v1_types::Object::default(); let f = tokio::fs::OpenOptions::new().read(true).open(p).await?; @@ -36,6 +36,32 @@ Ok(()) } +async fn download_file( + mut cl: storage_v1_types::ObjectsService, + bucket: &str, + id: &str, +) -> common::Result<()> { + // Set alt=media for download. + let mut gparams = storage_v1_types::StorageParams::default(); + gparams.alt = Some("media".into()); + let mut params = storage_v1_types::ObjectsGetParams::default(); + params.storage_params = Some(gparams); + params.bucket = bucket.into(); + params.object = id.into(); + + let id = id.replace("/", "_"); + let mut f = tokio::fs::OpenOptions::new() + .write(true) + .create(true) + .open(id) + .await?; + let result = cl.get(¶ms, Some(&mut f)).await?; + + println!("Downloaded object: {:?}", result); + + Ok(()) +} + #[tokio::main] async fn main() { env_logger::init(); @@ -45,17 +71,26 @@ .about("Upload objects to GCS.") .arg( clap::Arg::with_name("BUCKET") + .help("target bucket") .long("bucket") - .help("target bucket") + .required(true) + .short("b") .takes_value(true), ) .arg( - clap::Arg::with_name("FILE") - .help("File to upload") + clap::Arg::with_name("ACTION") + .help("What to do.") + .long("action") + .possible_values(&["get", "list", "put"]) .required(true) - .index(1) + .short("a") .takes_value(true), ) + .arg( + clap::Arg::with_name("FILE_OR_OBJECT") + .help("File to upload") + .index(1), + ) .get_matches(); let https_client = https_client(); @@ -69,16 +104,29 @@ .build() .await .expect("ServiceAccount authenticator failed."); + let authenticator = std::rc::Rc::new(authenticator); - let cl = storage_v1_types::ObjectsService::new(https_client, std::rc::Rc::new(authenticator)); + let action = matches.value_of("ACTION").expect("--action is required."); + let buck = matches + .value_of("BUCKET") + .expect("--bucket is a mandatory argument."); - if let Some(fp) = matches.value_of("FILE") { - if let Some(buck) = matches.value_of("BUCKET") { - upload_file(cl, buck, Path::new(&fp)) - .await - .expect("Upload failed :("); - return; - } + if action == "get" { + let obj = matches + .value_of("FILE_OR_OBJECT") + .expect("OBJECT is a mandatory argument."); + let cl = storage_v1_types::ObjectsService::new(https_client, authenticator); + download_file(cl, buck, obj) + .await + .expect("Download failed :("); + } else if action == "put" { + let fp = matches + .value_of("FILE_OR_OBJECT") + .expect("FILE is a mandatory argument."); + let cl = storage_v1_types::ObjectsService::new(https_client, authenticator); + upload_file(cl, buck, Path::new(&fp)) + .await + .expect("Upload failed :("); + return; } - println!("Please specify file to upload as first argument."); }
--- a/gcs_example/src/storage_v1_types.rs Sun Oct 25 15:08:03 2020 +0100 +++ b/gcs_example/src/storage_v1_types.rs Sun Oct 25 18:22:02 2020 +0100 @@ -4426,8 +4426,8 @@ pub async fn delete(&mut self, params: &BucketAccessControlsDeleteParams) -> Result<()> { let rel_path = format!( "b/{bucket}/acl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -4465,8 +4465,8 @@ ) -> Result<BucketAccessControl> { let rel_path = format!( "b/{bucket}/acl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -4503,7 +4503,10 @@ params: &BucketAccessControlsInsertParams, req: &BucketAccessControl, ) -> Result<BucketAccessControl> { - let rel_path = format!("b/{bucket}/acl", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/acl", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4539,7 +4542,10 @@ &mut self, params: &BucketAccessControlsListParams, ) -> Result<BucketAccessControls> { - let rel_path = format!("b/{bucket}/acl", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/acl", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4577,8 +4583,8 @@ ) -> Result<BucketAccessControl> { let rel_path = format!( "b/{bucket}/acl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -4618,8 +4624,8 @@ ) -> Result<BucketAccessControl> { let rel_path = format!( "b/{bucket}/acl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -4688,7 +4694,10 @@ /// Permanently deletes an empty bucket. pub async fn delete(&mut self, params: &BucketsDeleteParams) -> Result<()> { - let rel_path = format!("b/{bucket}", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4720,7 +4729,10 @@ /// Returns metadata for the specified bucket. pub async fn get(&mut self, params: &BucketsGetParams) -> Result<Bucket> { - let rel_path = format!("b/{bucket}", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4752,7 +4764,10 @@ /// Returns an IAM policy for the specified bucket. pub async fn get_iam_policy(&mut self, params: &BucketsGetIamPolicyParams) -> Result<Policy> { - let rel_path = format!("b/{bucket}/iam", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/iam", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4852,7 +4867,10 @@ &mut self, params: &BucketsLockRetentionPolicyParams, ) -> Result<Bucket> { - let rel_path = format!("b/{bucket}/lockRetentionPolicy", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/lockRetentionPolicy", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4884,7 +4902,10 @@ /// Patches a bucket. Changes to the bucket will be readable immediately after writing, but configuration changes may take time to propagate. pub async fn patch(&mut self, params: &BucketsPatchParams, req: &Bucket) -> Result<Bucket> { - let rel_path = format!("b/{bucket}", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4921,7 +4942,10 @@ params: &BucketsSetIamPolicyParams, req: &Policy, ) -> Result<Policy> { - let rel_path = format!("b/{bucket}/iam", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/iam", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4957,7 +4981,10 @@ &mut self, params: &BucketsTestIamPermissionsParams, ) -> Result<TestIamPermissionsResponse> { - let rel_path = format!("b/{bucket}/iam/testPermissions", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/iam/testPermissions", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -4989,7 +5016,10 @@ /// Updates a bucket. Changes to the bucket will be readable immediately after writing, but configuration changes may take time to propagate. pub async fn update(&mut self, params: &BucketsUpdateParams, req: &Bucket) -> Result<Bucket> { - let rel_path = format!("b/{bucket}", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -5127,8 +5157,8 @@ pub async fn delete(&mut self, params: &DefaultObjectAccessControlsDeleteParams) -> Result<()> { let rel_path = format!( "b/{bucket}/defaultObjectAcl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5166,8 +5196,8 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/defaultObjectAcl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5204,7 +5234,10 @@ params: &DefaultObjectAccessControlsInsertParams, req: &ObjectAccessControl, ) -> Result<ObjectAccessControl> { - let rel_path = format!("b/{bucket}/defaultObjectAcl", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/defaultObjectAcl", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -5240,7 +5273,10 @@ &mut self, params: &DefaultObjectAccessControlsListParams, ) -> Result<ObjectAccessControls> { - let rel_path = format!("b/{bucket}/defaultObjectAcl", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/defaultObjectAcl", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -5278,8 +5314,8 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/defaultObjectAcl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5319,8 +5355,8 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/defaultObjectAcl/{entity}", - bucket = params.bucket, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5391,8 +5427,8 @@ pub async fn delete(&mut self, params: &NotificationsDeleteParams) -> Result<()> { let rel_path = format!( "b/{bucket}/notificationConfigs/{notification}", - bucket = params.bucket, - notification = params.notification + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + notification = percent_encode(params.notification.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5427,8 +5463,8 @@ pub async fn get(&mut self, params: &NotificationsGetParams) -> Result<Notification> { let rel_path = format!( "b/{bucket}/notificationConfigs/{notification}", - bucket = params.bucket, - notification = params.notification + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + notification = percent_encode(params.notification.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5465,7 +5501,10 @@ params: &NotificationsInsertParams, req: &Notification, ) -> Result<Notification> { - let rel_path = format!("b/{bucket}/notificationConfigs", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/notificationConfigs", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -5498,7 +5537,10 @@ /// Retrieves a list of notification subscriptions for a given bucket. pub async fn list(&mut self, params: &NotificationsListParams) -> Result<Notifications> { - let rel_path = format!("b/{bucket}/notificationConfigs", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/notificationConfigs", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -5567,9 +5609,9 @@ pub async fn delete(&mut self, params: &ObjectAccessControlsDeleteParams) -> Result<()> { let rel_path = format!( "b/{bucket}/o/{object}/acl/{entity}", - bucket = params.bucket, - object = params.object, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5607,9 +5649,9 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/o/{object}/acl/{entity}", - bucket = params.bucket, - object = params.object, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5648,8 +5690,8 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/o/{object}/acl", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5688,8 +5730,8 @@ ) -> Result<ObjectAccessControls> { let rel_path = format!( "b/{bucket}/o/{object}/acl", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5728,9 +5770,9 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/o/{object}/acl/{entity}", - bucket = params.bucket, - object = params.object, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5770,9 +5812,9 @@ ) -> Result<ObjectAccessControl> { let rel_path = format!( "b/{bucket}/o/{object}/acl/{entity}", - bucket = params.bucket, - object = params.object, - entity = params.entity + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC), + entity = percent_encode(params.entity.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5847,8 +5889,10 @@ ) -> Result<Object> { let rel_path = format!( "b/{destinationBucket}/o/{destinationObject}/compose", - destinationBucket = params.destination_bucket, - destinationObject = params.destination_object + destinationBucket = + percent_encode(params.destination_bucket.as_bytes(), NON_ALPHANUMERIC), + destinationObject = + percent_encode(params.destination_object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5884,10 +5928,12 @@ pub async fn copy(&mut self, params: &ObjectsCopyParams, req: &Object) -> Result<Object> { let rel_path = format!( "b/{sourceBucket}/o/{sourceObject}/copyTo/b/{destinationBucket}/o/{destinationObject}", - sourceBucket = params.source_bucket, - sourceObject = params.source_object, - destinationBucket = params.destination_bucket, - destinationObject = params.destination_object + sourceBucket = percent_encode(params.source_bucket.as_bytes(), NON_ALPHANUMERIC), + sourceObject = percent_encode(params.source_object.as_bytes(), NON_ALPHANUMERIC), + destinationBucket = + percent_encode(params.destination_bucket.as_bytes(), NON_ALPHANUMERIC), + destinationObject = + percent_encode(params.destination_object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5923,8 +5969,8 @@ pub async fn delete(&mut self, params: &ObjectsDeleteParams) -> Result<()> { let rel_path = format!( "b/{bucket}/o/{object}", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -5964,12 +6010,12 @@ pub async fn get( &mut self, params: &ObjectsGetParams, - dst: Option<&mut dyn std::io::Write>, + dst: Option<&mut (dyn tokio::io::AsyncWrite + std::marker::Unpin)>, ) -> Result<DownloadResponse<Object>> { let rel_path = format!( "b/{bucket}/o/{object}", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; @@ -6006,8 +6052,8 @@ pub async fn get_iam_policy(&mut self, params: &ObjectsGetIamPolicyParams) -> Result<Policy> { let rel_path = format!( "b/{bucket}/o/{object}/iam", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6040,7 +6086,10 @@ /// Stores a new object and metadata. pub async fn insert(&mut self, params: &ObjectsInsertParams, req: &Object) -> Result<Object> { - let rel_path = format!("b/{bucket}/o", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/o", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -6080,7 +6129,10 @@ req: &Object, data: hyper::body::Bytes, ) -> Result<Object> { - let rel_path = format!("/upload/storage/v1/b/{bucket}/o", bucket = params.bucket); + let rel_path = format!( + "/upload/storage/v1/b/{bucket}/o", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/".to_string() + &rel_path; let tok; @@ -6127,7 +6179,7 @@ ) -> Result<ResumableUpload<'client, Object>> { let rel_path = format!( "/resumable/upload/storage/v1/b/{bucket}/o", - bucket = params.bucket + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/".to_string() + &rel_path; let tok; @@ -6175,7 +6227,10 @@ /// Retrieves a list of objects matching the criteria. pub async fn list(&mut self, params: &ObjectsListParams) -> Result<Objects> { - let rel_path = format!("b/{bucket}/o", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/o", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -6209,8 +6264,8 @@ pub async fn patch(&mut self, params: &ObjectsPatchParams, req: &Object) -> Result<Object> { let rel_path = format!( "b/{bucket}/o/{object}", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6248,7 +6303,7 @@ params: &ObjectsRewriteParams, req: &Object, ) -> Result<RewriteResponse> { - let rel_path = format!("b/{sourceBucket}/o/{sourceObject}/rewriteTo/b/{destinationBucket}/o/{destinationObject}", sourceBucket=params.source_bucket,sourceObject=params.source_object,destinationBucket=params.destination_bucket,destinationObject=params.destination_object); + let rel_path = format!("b/{sourceBucket}/o/{sourceObject}/rewriteTo/b/{destinationBucket}/o/{destinationObject}", sourceBucket=percent_encode(params.source_bucket.as_bytes(), NON_ALPHANUMERIC),sourceObject=percent_encode(params.source_object.as_bytes(), NON_ALPHANUMERIC),destinationBucket=percent_encode(params.destination_bucket.as_bytes(), NON_ALPHANUMERIC),destinationObject=percent_encode(params.destination_object.as_bytes(), NON_ALPHANUMERIC)); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -6287,8 +6342,8 @@ ) -> Result<Policy> { let rel_path = format!( "b/{bucket}/o/{object}/iam", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6327,8 +6382,8 @@ ) -> Result<TestIamPermissionsResponse> { let rel_path = format!( "b/{bucket}/o/{object}/iam/testPermissions", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6363,8 +6418,8 @@ pub async fn update(&mut self, params: &ObjectsUpdateParams, req: &Object) -> Result<Object> { let rel_path = format!( "b/{bucket}/o/{object}", - bucket = params.bucket, - object = params.object + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC), + object = percent_encode(params.object.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6402,7 +6457,10 @@ params: &ObjectsWatchAllParams, req: &Channel, ) -> Result<Channel> { - let rel_path = format!("b/{bucket}/o/watch", bucket = params.bucket); + let rel_path = format!( + "b/{bucket}/o/watch", + bucket = percent_encode(params.bucket.as_bytes(), NON_ALPHANUMERIC) + ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; if self.scopes.is_empty() { @@ -6507,7 +6565,7 @@ pub async fn create(&mut self, params: &ProjectsHmacKeysCreateParams) -> Result<HmacKey> { let rel_path = format!( "projects/{projectId}/hmacKeys", - projectId = params.project_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6542,8 +6600,8 @@ pub async fn delete(&mut self, params: &ProjectsHmacKeysDeleteParams) -> Result<()> { let rel_path = format!( "projects/{projectId}/hmacKeys/{accessId}", - projectId = params.project_id, - accessId = params.access_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC), + accessId = percent_encode(params.access_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6578,8 +6636,8 @@ pub async fn get(&mut self, params: &ProjectsHmacKeysGetParams) -> Result<HmacKeyMetadata> { let rel_path = format!( "projects/{projectId}/hmacKeys/{accessId}", - projectId = params.project_id, - accessId = params.access_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC), + accessId = percent_encode(params.access_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6614,7 +6672,7 @@ pub async fn list(&mut self, params: &ProjectsHmacKeysListParams) -> Result<HmacKeysMetadata> { let rel_path = format!( "projects/{projectId}/hmacKeys", - projectId = params.project_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6653,8 +6711,8 @@ ) -> Result<HmacKeyMetadata> { let rel_path = format!( "projects/{projectId}/hmacKeys/{accessId}", - projectId = params.project_id, - accessId = params.access_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC), + accessId = percent_encode(params.access_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok; @@ -6728,7 +6786,7 @@ ) -> Result<ServiceAccount> { let rel_path = format!( "projects/{projectId}/serviceAccount", - projectId = params.project_id + projectId = percent_encode(params.project_id.as_bytes(), NON_ALPHANUMERIC) ); let path = "https://storage.googleapis.com/storage/v1/".to_string() + &rel_path; let tok;
--- a/generate/generate.py Sun Oct 25 15:08:03 2020 +0100 +++ b/generate/generate.py Sun Oct 25 18:22:02 2020 +0100 @@ -232,13 +232,13 @@ return frags -def resolve_parameters(string, paramsname='params', suffix=''): +def resolve_parameters(string, paramsname='params'): """Returns a Rust syntax for formatting the given string with API parameters, and a list of (snake-case) API parameters that are used. """ pat = re.compile('\{\+?(\w+)\}') params = re.findall(pat, string) snakeparams = [rust_identifier(p) for p in params] - format_params = ','.join(['{}={}.{}{}'.format(p, paramsname, sp, suffix) for (p, sp) in zip(params, snakeparams)]) + format_params = ','.join(['{}=percent_encode({}.{}.as_bytes(), NON_ALPHANUMERIC)'.format(p, paramsname, sp) for (p, sp) in zip(params, snakeparams)]) string = string.replace('{+', '{') # Some required parameters are in the URL. This rust syntax formats the relative URL part appropriately. return 'format!("{}", {})'.format(string, format_params), snakeparams @@ -276,7 +276,7 @@ for p, pp in method.get('parameters', {}).items() if ('required' in pp and pp['location'] != 'path') } # Types of the function - in_type = method['request']['$ref'] if 'request' in method else '()' + in_type = method['request']['$ref'] if 'request' in method else None out_type = method['response']['$ref'] if 'response' in method else '()' is_download = method.get('supportsMediaDownload', False) @@ -306,8 +306,7 @@ rust_identifier(methodname), 'param_type': params_type_name, - 'in_type': - in_type, + 'in_type': in_type, 'out_type': out_type, 'base_path': @@ -332,8 +331,6 @@ 'http_method': http_method } - if in_type == '()': - data_download.pop('in_type') method_fragments.append(chevron.render(DownloadMethodTmpl, data_download)) else: data_normal = { @@ -341,8 +338,7 @@ rust_identifier(methodname), 'param_type': params_type_name, - 'in_type': - in_type, + 'in_type': in_type, 'out_type': out_type, 'base_path': @@ -367,8 +363,6 @@ 'http_method': http_method } - if in_type == '()': - data_normal.pop('in_type') method_fragments.append(chevron.render(NormalMethodTmpl, data_normal)) # We generate an additional implementation with the option of uploading data.
--- a/generate/templates.py Sun Oct 25 15:08:03 2020 +0100 +++ b/generate/templates.py Sun Oct 25 18:22:02 2020 +0100 @@ -261,7 +261,8 @@ /// `dst`. If `dst` is `None` despite data being available for download, `ApiError::DataAvailableError` /// is returned. pub async fn {{{name}}}( - &mut self, params: &{{{param_type}}}, {{#in_type}}req: &{{{in_type}}},{{/in_type}} dst: Option<&mut dyn std::io::Write>) + &mut self, params: &{{{param_type}}}, {{#in_type}}req: &{{{in_type}}},{{/in_type}} + dst: Option<&mut (dyn tokio::io::AsyncWrite + std::marker::Unpin)>) -> Result<DownloadResponse<{{out_type}}>> { let rel_path = {{{rel_path_expr}}};