1use async_trait::async_trait;
4
5use crate::symlink_common::{build_get_symlink_request, build_put_symlink_request, GetSymlinkOptions, PutSymlinkOptions, PutSymlinkResult};
6use crate::{Client, Result};
7
8#[async_trait]
9pub trait ObjectSymlinkOperations {
10 async fn put_symlink<S1, S2, S3>(
16 &self,
17 bucket_name: S1,
18 symlink_object_key: S2,
19 target_object_key: S3,
20 options: Option<PutSymlinkOptions>,
21 ) -> Result<PutSymlinkResult>
22 where
23 S1: AsRef<str> + Send,
24 S2: AsRef<str> + Send,
25 S3: AsRef<str> + Send;
26
27 async fn get_symlink<S1, S2>(&self, bucket_name: S1, symlink_object_key: S2, options: Option<GetSymlinkOptions>) -> Result<String>
31 where
32 S1: AsRef<str> + Send,
33 S2: AsRef<str> + Send;
34}
35
36#[async_trait]
37impl ObjectSymlinkOperations for Client {
38 async fn put_symlink<S1, S2, S3>(
44 &self,
45 bucket_name: S1,
46 symlink_object_key: S2,
47 target_object_key: S3,
48 options: Option<PutSymlinkOptions>,
49 ) -> Result<PutSymlinkResult>
50 where
51 S1: AsRef<str> + Send,
52 S2: AsRef<str> + Send,
53 S3: AsRef<str> + Send,
54 {
55 let request = build_put_symlink_request(bucket_name.as_ref(), symlink_object_key.as_ref(), target_object_key.as_ref(), &options)?;
56 let (headers, _) = self.do_request::<()>(request).await?;
57 Ok(headers.into())
58 }
59
60 async fn get_symlink<S1, S2>(&self, bucket_name: S1, symlink_object_key: S2, options: Option<GetSymlinkOptions>) -> Result<String>
64 where
65 S1: AsRef<str> + Send,
66 S2: AsRef<str> + Send,
67 {
68 let request = build_get_symlink_request(bucket_name.as_ref(), symlink_object_key.as_ref(), &options)?;
69 let (headers, _) = self.do_request::<()>(request).await?;
70 let object_key = headers.get("x-oss-symlink-target").unwrap_or(&String::new()).to_string();
71
72 Ok(urlencoding::decode(&object_key)?.to_string())
73 }
74}
75
76#[cfg(test)]
77mod test_symlink_async {
78 use std::sync::Once;
79
80 use uuid::Uuid;
81
82 use crate::{
83 object::ObjectOperations,
84 object_common::{PutObjectApiResponse, PutObjectResult},
85 symlink::ObjectSymlinkOperations,
86 symlink_common::PutSymlinkOptionsBuilder,
87 Client,
88 };
89
90 static INIT: Once = Once::new();
91
92 fn setup() {
93 INIT.call_once(|| {
94 simple_logger::init_with_level(log::Level::Debug).unwrap();
95 dotenvy::dotenv().unwrap();
96 });
97 }
98
99 #[tokio::test]
100 async fn test_symlink_async() {
101 log::debug!("test symlink");
102 setup();
103
104 let client = Client::from_env();
105 let bucket_name = "yuanyq-2";
106 let object_key = format!("versioning-test/{}.webp", Uuid::new_v4());
107 let file_path = "/home/yuanyq/Pictures/test-7.webp";
108
109 let link_name = format!("versioning-test/{}-link.webp", Uuid::new_v4());
110
111 let response = client.put_object_from_file(bucket_name, &object_key, file_path, None).await;
112 assert!(response.is_ok());
113
114 let ret = response.unwrap();
115 let version_id = if let PutObjectResult::ApiResponse(PutObjectApiResponse {
116 request_id: _,
117 etag: _,
118 content_md5: _,
119 hash_crc64ecma: _,
120 version_id,
121 }) = ret
122 {
123 assert!(version_id.is_some());
124 version_id.clone().unwrap()
125 } else {
126 panic!("Unexpected response type");
127 };
128
129 log::debug!("version id: {}", version_id);
130
131 let options = PutSymlinkOptionsBuilder::new().metadata("x-oss-meta-a", "meta value b").build();
132
133 let response = client.put_symlink(bucket_name, &link_name, &object_key, Some(options)).await;
134 assert!(response.is_ok());
135
136 let ret = response.unwrap();
137 assert!(ret.version_id.is_some());
138
139 let response = client.get_symlink(bucket_name, &link_name, None).await;
140 assert!(response.is_ok());
141 let ret = response.unwrap();
142 assert_eq!(ret, object_key);
143
144 let response = client.delete_object(bucket_name, &link_name, None).await;
145 assert!(response.is_ok());
146
147 let response = client.delete_object(bucket_name, &object_key, None).await;
148 assert!(response.is_ok());
149 }
150}