opendal/services/seafile/
config.rs1use std::fmt::Debug;
19
20use serde::Deserialize;
21use serde::Serialize;
22
23use super::SEAFILE_SCHEME;
24use super::backend::SeafileBuilder;
25
26#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
28#[serde(default)]
29#[non_exhaustive]
30pub struct SeafileConfig {
31 pub root: Option<String>,
35 pub endpoint: Option<String>,
37 pub username: Option<String>,
39 pub password: Option<String>,
41 pub repo_name: String,
45}
46
47impl Debug for SeafileConfig {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("SeafileConfig")
50 .field("root", &self.root)
51 .field("endpoint", &self.endpoint)
52 .field("username", &self.username)
53 .field("repo_name", &self.repo_name)
54 .finish_non_exhaustive()
55 }
56}
57
58impl crate::Configurator for SeafileConfig {
59 type Builder = SeafileBuilder;
60
61 fn from_uri(uri: &crate::types::OperatorUri) -> crate::Result<Self> {
62 let authority = uri.authority().ok_or_else(|| {
63 crate::Error::new(crate::ErrorKind::ConfigInvalid, "uri authority is required")
64 .with_context("service", SEAFILE_SCHEME)
65 })?;
66
67 let raw_path = uri.root().ok_or_else(|| {
68 crate::Error::new(
69 crate::ErrorKind::ConfigInvalid,
70 "uri path must start with repo name",
71 )
72 .with_context("service", SEAFILE_SCHEME)
73 })?;
74
75 let mut segments = raw_path.splitn(2, '/');
76 let repo_name = segments.next().filter(|s| !s.is_empty()).ok_or_else(|| {
77 crate::Error::new(
78 crate::ErrorKind::ConfigInvalid,
79 "repo name is required in uri path",
80 )
81 .with_context("service", SEAFILE_SCHEME)
82 })?;
83
84 let mut map = uri.options().clone();
85 map.insert("endpoint".to_string(), format!("https://{authority}"));
86 map.insert("repo_name".to_string(), repo_name.to_string());
87
88 if let Some(rest) = segments.next() {
89 if !rest.is_empty() {
90 map.insert("root".to_string(), rest.to_string());
91 }
92 }
93
94 Self::from_iter(map)
95 }
96
97 #[allow(deprecated)]
98 fn into_builder(self) -> Self::Builder {
99 SeafileBuilder {
100 config: self,
101 http_client: None,
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::Configurator;
110 use crate::types::OperatorUri;
111
112 #[test]
113 fn from_uri_sets_endpoint_repo_and_root() {
114 let uri = OperatorUri::new(
115 "seafile://files.example.com/myrepo/projects/app",
116 Vec::<(String, String)>::new(),
117 )
118 .unwrap();
119
120 let cfg = SeafileConfig::from_uri(&uri).unwrap();
121 assert_eq!(cfg.endpoint.as_deref(), Some("https://files.example.com"));
122 assert_eq!(cfg.repo_name, "myrepo".to_string());
123 assert_eq!(cfg.root.as_deref(), Some("projects/app"));
124 }
125
126 #[test]
127 fn from_uri_requires_repo_name() {
128 let uri = OperatorUri::new(
129 "seafile://files.example.com",
130 Vec::<(String, String)>::new(),
131 )
132 .unwrap();
133
134 assert!(SeafileConfig::from_uri(&uri).is_err());
135 }
136}