opendal_service_cos/
config.rs1use std::fmt::Debug;
19
20use serde::Deserialize;
21use serde::Serialize;
22
23use opendal_core::Configurator;
24use opendal_core::OperatorUri;
25use opendal_core::Result;
26
27use super::backend::CosBuilder;
28
29#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
31#[serde(default)]
32pub struct CosConfig {
33 pub root: Option<String>,
35 pub endpoint: Option<String>,
37 pub secret_id: Option<String>,
39 pub secret_key: Option<String>,
41 pub security_token: Option<String>,
54 pub bucket: Option<String>,
56 #[deprecated(
58 since = "0.57.0",
59 note = "COS versioning capability is enabled by default and this option is no longer needed."
60 )]
61 pub enable_versioning: bool,
62 pub disable_config_load: bool,
64}
65
66impl Debug for CosConfig {
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68 f.debug_struct("CosConfig")
69 .field("root", &self.root)
70 .field("endpoint", &self.endpoint)
71 .field("bucket", &self.bucket)
72 .field(
73 "security_token",
74 &self.security_token.as_ref().map(|_| "<redacted>"),
75 )
76 .field("disable_config_load", &self.disable_config_load)
77 .finish_non_exhaustive()
78 }
79}
80
81impl Configurator for CosConfig {
82 type Builder = CosBuilder;
83
84 fn from_uri(uri: &OperatorUri) -> Result<Self> {
85 let mut map = uri.options().clone();
86
87 if let Some(name) = uri.name() {
88 map.insert("bucket".to_string(), name.to_string());
89 }
90
91 if let Some(root) = uri.root() {
92 map.insert("root".to_string(), root.to_string());
93 }
94
95 Self::from_iter(map)
96 }
97
98 fn into_builder(self) -> Self::Builder {
99 CosBuilder { config: self }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use opendal_core::Configurator;
107 use opendal_core::OperatorUri;
108
109 #[test]
110 fn from_uri_extracts_bucket_and_root() {
111 let uri = OperatorUri::new(
112 "cos://example-bucket/path/to/root",
113 Vec::<(String, String)>::new(),
114 )
115 .unwrap();
116 let cfg = CosConfig::from_uri(&uri).unwrap();
117 assert_eq!(cfg.bucket.as_deref(), Some("example-bucket"));
118 assert_eq!(cfg.root.as_deref(), Some("path/to/root"));
119 }
120
121 #[test]
122 fn from_uri_accepts_cosn_scheme() {
123 let uri = OperatorUri::new(
124 "cosn://example-bucket/path/to/root",
125 Vec::<(String, String)>::new(),
126 )
127 .unwrap();
128 let cfg = CosConfig::from_uri(&uri).unwrap();
129 assert_eq!(cfg.bucket.as_deref(), Some("example-bucket"));
130 assert_eq!(cfg.root.as_deref(), Some("path/to/root"));
131 }
132
133 #[test]
134 fn from_uri_extracts_security_token() {
135 let uri = OperatorUri::new(
136 "cos://example-bucket/",
137 vec![
138 ("secret_id".to_string(), "id".to_string()),
139 ("secret_key".to_string(), "key".to_string()),
140 ("security_token".to_string(), "token".to_string()),
141 ],
142 )
143 .unwrap();
144 let cfg = CosConfig::from_uri(&uri).unwrap();
145 assert_eq!(cfg.secret_id.as_deref(), Some("id"));
146 assert_eq!(cfg.secret_key.as_deref(), Some("key"));
147 assert_eq!(cfg.security_token.as_deref(), Some("token"));
148 }
149
150 #[test]
151 fn debug_redacts_security_token() {
152 let cfg = CosConfig {
153 security_token: Some("super-secret-token".to_string()),
154 ..Default::default()
155 };
156 let debug_output = format!("{cfg:?}");
157 assert!(!debug_output.contains("super-secret-token"));
158 assert!(debug_output.contains("<redacted>"));
159 }
160}