#[macro_export]
macro_rules! impl_required_builder {
(
$request_name:ident,
$builder_name:ident,
required: [$($req_field:ident: $req_type:ty),* $(,)?],
optional: [$($opt_field:ident: $opt_type:ty),* $(,)?]
) => {
#[derive(Debug, Default)]
pub struct $builder_name {
$(
$req_field: Option<$req_type>,
)*
$(
$opt_field: Option<$opt_type>,
)*
config: Option<openlark_core::config::Config>,
_phantom: std::marker::PhantomData<$request_name>,
}
impl $builder_name {
#[deprecated(since = "0.5.0", note = "使用 builder() 替代")]
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
pub fn with_config(mut self, config: openlark_core::config::Config) -> Self {
self.config = Some(config);
self
}
$(
pub fn $req_field(mut self, value: impl Into<$req_type>) -> Self {
self.$req_field = Some(value.into());
self
}
)*
$(
pub fn $opt_field(mut self, value: impl Into<$opt_type>) -> Self {
self.$opt_field = Some(value.into());
self
}
)*
pub fn build(self) -> openlark_core::SDKResult<$request_name> {
$(
let $req_field = self.$req_field.ok_or_else(|| {
openlark_core::error::validation_error(
stringify!($req_field),
concat!("必填字段 '", stringify!($req_field), "' 未设置")
)
})?;
)*
let config = self.config.ok_or_else(|| {
openlark_core::error::validation_error(
"config",
"Config 未设置,请使用 with_config() 方法"
)
})?;
Ok($request_name {
$(
$req_field,
)*
$(
$opt_field: self.$opt_field,
)*
config,
})
}
}
impl $request_name {
pub fn builder() -> $builder_name {
$builder_name::default()
}
}
};
}
#[macro_export]
macro_rules! impl_fluent_builder {
(
$request_name:ident,
config: Config,
required: [$($req_field:ident: $req_type:ty),* $(,)?],
optional: [$($opt_field:ident: $opt_type:ty),* $(,)?]
) => {
impl $request_name {
pub fn builder() -> $request_name {
Self {
$(
$req_field: String::new(),
)*
$(
$opt_field: None,
)*
config: Config::default(),
}
}
$(
pub fn $req_field(mut self, value: impl Into<$req_type>) -> Self {
self.$req_field = value.into();
self
}
)*
$(
pub fn $opt_field(mut self, value: impl Into<$opt_type>) -> Self {
self.$opt_field = Some(value.into());
self
}
)*
}
};
}
#[cfg(test)]
mod tests {
use openlark_core::config::Config;
#[derive(Debug, Clone)]
pub struct TestRequest {
app_token: String,
table_id: String,
user_id_type: Option<String>,
#[allow(dead_code)]
config: Config,
}
impl_required_builder!(
TestRequest,
TestRequestBuilder,
required: [
app_token: String,
table_id: String
],
optional: [
user_id_type: String
]
);
#[test]
fn test_required_builder_success() {
let config = Config::builder().app_id("test").app_secret("test").build();
let request = TestRequest::builder()
.with_config(config)
.app_token("token")
.table_id("table")
.build()
.unwrap();
assert_eq!(request.app_token, "token");
assert_eq!(request.table_id, "table");
assert!(request.user_id_type.is_none());
}
#[test]
fn test_required_builder_with_optional() {
let config = Config::builder().app_id("test").app_secret("test").build();
let request = TestRequest::builder()
.with_config(config)
.app_token("token")
.table_id("table")
.user_id_type("open_id")
.build()
.unwrap();
assert_eq!(request.user_id_type, Some("open_id".to_string()));
}
#[test]
fn test_required_builder_missing_required_field() {
let config = Config::builder().app_id("test").app_secret("test").build();
let result = TestRequest::builder()
.with_config(config)
.app_token("token")
.build();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("table_id"));
}
#[test]
fn test_required_builder_missing_config() {
let result = TestRequest::builder()
.app_token("token")
.table_id("table")
.build();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Config"));
}
#[test]
fn test_required_builder_into_string() {
let config = Config::builder().app_id("test").app_secret("test").build();
let request = TestRequest::builder()
.with_config(config)
.app_token("token") .table_id(String::from("table")) .build()
.unwrap();
assert_eq!(request.app_token, "token");
assert_eq!(request.table_id, "table");
}
}