#![allow(async_fn_in_trait)]
pub mod client;
pub mod config;
pub mod error;
pub mod features;
pub mod registry;
pub mod traits;
pub mod types;
pub mod lazy;
#[cfg(test)]
mod test_utils;
#[cfg(feature = "websocket")]
pub mod ws_client;
pub use client::{Client, ClientBuilder};
pub use config::Config;
pub use error::{Error, Result};
pub use error::{
with_context, with_operation_context, ClientErrorExt, ErrorAnalyzer, };
pub use error::{
api_error, authentication_error, business_error, configuration_error, internal_error, network_error, rate_limit_error, registry_error, serialization_error, service_unavailable_error, timeout_error, validation_error, };
pub use features::FeatureLoader;
pub use registry::{
DefaultServiceRegistry, ServiceEntry, ServiceMetadata, ServiceRegistry, ServiceStatus,
};
pub use traits::{LarkClient, ServiceLifecycle};
#[cfg(feature = "cardkit")]
pub use openlark_cardkit::CardkitClient;
#[cfg(feature = "auth")]
pub use client::AuthClient;
#[cfg(feature = "docs")]
pub use openlark_docs::DocsClient;
#[cfg(feature = "communication")]
pub use openlark_communication::CommunicationClient;
#[cfg(feature = "hr")]
pub use openlark_hr::HrClient;
#[cfg(feature = "meeting")]
pub use openlark_meeting::MeetingClient;
pub use openlark_core::{config::Config as CoreConfig, SDKResult as CoreResult};
pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
pub type SDKResult<T> = openlark_core::SDKResult<T>;
pub mod prelude {
pub use crate::{Client, ClientBuilder, Config};
pub use crate::{Error, Result};
pub use crate::{
with_context, with_operation_context, ClientErrorExt, ErrorAnalyzer, };
pub use crate::{
api_error, authentication_error, business_error, configuration_error, internal_error, network_error, rate_limit_error, registry_error, serialization_error, service_unavailable_error, timeout_error, validation_error, };
pub use openlark_core::error::{CoreError, ErrorCode, ErrorSeverity, ErrorTrait, ErrorType};
pub use crate::traits::{LarkClient, ServiceLifecycle, ServiceTrait};
pub use crate::ServiceRegistry;
pub use crate::FeatureLoader;
#[cfg(feature = "cardkit")]
pub use openlark_cardkit::CardkitClient;
#[cfg(feature = "auth")]
pub use crate::AuthClient;
#[cfg(feature = "docs")]
pub use openlark_docs::DocsClient;
#[cfg(feature = "communication")]
pub use openlark_communication::CommunicationClient;
#[cfg(feature = "hr")]
pub use openlark_hr::HrClient;
#[cfg(feature = "meeting")]
pub use openlark_meeting::MeetingClient;
pub type SDKResult<T> = openlark_core::SDKResult<T>;
pub use openlark_core::{config::Config as CoreConfig, SDKResult as CoreResult};
}
pub mod info {
pub const NAME: &str = "OpenLark Client";
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
pub const REPOSITORY: &str = env!("CARGO_PKG_REPOSITORY");
}
pub mod utils;
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
#[test]
fn test_library_info() {
assert!(!info::NAME.is_empty());
assert!(!info::VERSION.is_empty());
assert!(!info::DESCRIPTION.is_empty());
}
#[test]
fn test_enabled_features() {
let features = utils::get_enabled_features();
assert!(features.contains(&"auth"));
}
#[test]
fn test_prelude_reexports() {
use prelude::*;
let _builder: ClientBuilder = ClientBuilder::new();
let _config = Config::builder().app_id("test").app_secret("test").build();
}
#[test]
fn test_check_env_config_success() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_ok());
},
);
}
#[test]
fn test_check_env_config_missing_app_id() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", None),
("OPENLARK_APP_SECRET", Some("test_secret")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_empty_app_id() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("")),
("OPENLARK_APP_SECRET", Some("test_secret")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_missing_app_secret() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", None),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_empty_app_secret() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_invalid_base_url() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
("OPENLARK_BASE_URL", Some("invalid_url")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_valid_base_url() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_ok());
},
);
}
#[test]
fn test_check_env_config_invalid_timeout() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
("OPENLARK_TIMEOUT", Some("not_a_number")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_err());
},
);
}
#[test]
fn test_check_env_config_valid_timeout() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
("OPENLARK_TIMEOUT", Some("30")),
],
|| {
let result = utils::check_env_config();
assert!(result.is_ok());
},
);
}
#[test]
fn test_create_config_from_env_success() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
("OPENLARK_BASE_URL", Some("https://open.feishu.cn")),
],
|| {
let result = utils::create_config_from_env();
assert!(result.is_ok());
let config = result.unwrap();
assert_eq!(config.app_id, "test_app_id");
assert_eq!(config.app_secret, "test_secret");
},
);
}
#[test]
fn test_create_config_from_env_missing_vars() {
test_utils::with_env_vars(
&[("OPENLARK_APP_ID", None), ("OPENLARK_APP_SECRET", None)],
|| {
let result = utils::create_config_from_env();
assert!(result.is_err());
},
);
}
#[test]
fn test_get_config_summary() {
let config = Config::builder()
.app_id("test_app_id")
.app_secret("test_secret_key")
.base_url("https://open.feishu.cn")
.timeout(std::time::Duration::from_secs(30))
.build()
.unwrap();
let summary = utils::get_config_summary(&config);
assert_eq!(summary.app_id, "test_app_id");
assert!(summary.app_secret_set);
assert_eq!(summary.base_url, "https://open.feishu.cn");
assert!(summary.timeout > std::time::Duration::ZERO);
}
#[test]
fn test_config_summary_friendly_description() {
let summary = config::ConfigSummary {
app_id: "test_app".to_string(),
app_secret_set: true,
app_type: openlark_core::constants::AppType::SelfBuild,
enable_token_cache: true,
base_url: "https://open.feishu.cn".to_string(),
timeout: std::time::Duration::from_secs(30),
retry_count: 3,
enable_log: false,
header_count: 0,
};
let description = summary.friendly_description();
assert!(description.contains("test_app"));
assert!(description.contains("open.feishu.cn"));
assert!(description.contains("30s"));
}
#[test]
fn test_config_summary_friendly_description_no_timeout() {
let summary = config::ConfigSummary {
app_id: "test_app".to_string(),
app_secret_set: true,
app_type: openlark_core::constants::AppType::SelfBuild,
enable_token_cache: true,
base_url: "https://open.feishu.cn".to_string(),
timeout: std::time::Duration::ZERO,
retry_count: 3,
enable_log: false,
header_count: 0,
};
let description = summary.friendly_description();
assert!(description.contains("test_app"));
assert!(description.contains("0ns"));
}
#[test]
fn test_validate_feature_dependencies_success() {
let result = utils::validate_feature_dependencies();
assert!(result.is_ok());
}
#[test]
fn test_diagnose_system_success() {
test_utils::with_env_vars(
&[
("OPENLARK_APP_ID", Some("test_app_id")),
("OPENLARK_APP_SECRET", Some("test_secret")),
],
|| {
let diagnostics = utils::diagnose_system();
assert!(
diagnostics.env_config_status.contains("✅")
|| diagnostics.env_config_status.contains("❌")
);
assert!(diagnostics.feature_deps_status.contains("✅"));
assert!(!diagnostics.enabled_features.is_empty());
},
);
}
#[test]
fn test_system_diagnostics_new() {
let diagnostics = utils::SystemDiagnostics::new();
assert_eq!(diagnostics.env_config_status, "未检查");
assert_eq!(diagnostics.feature_deps_status, "未检查");
assert!(diagnostics.enabled_features.is_empty());
assert!(diagnostics.issues.is_empty());
}
#[test]
fn test_system_diagnostics_add_issue() {
let mut diagnostics = utils::SystemDiagnostics::new();
diagnostics.add_issue("测试类别", "测试描述");
assert_eq!(diagnostics.issues.len(), 1);
assert_eq!(diagnostics.issues[0].category, "测试类别");
assert_eq!(diagnostics.issues[0].description, "测试描述");
}
#[test]
fn test_system_diagnostics_health_summary_healthy() {
let diagnostics = utils::SystemDiagnostics::new();
let summary = diagnostics.health_summary();
assert!(summary.contains("🟢"));
assert!(summary.contains("健康"));
}
#[test]
fn test_system_diagnostics_health_summary_with_issues() {
let mut diagnostics = utils::SystemDiagnostics::new();
diagnostics.add_issue("测试类别", "测试描述");
let summary = diagnostics.health_summary();
assert!(summary.contains("🟡"));
assert!(summary.contains("1"));
}
#[test]
fn test_system_diagnostics_has_critical_issues_true() {
let mut diagnostics = utils::SystemDiagnostics::new();
diagnostics.add_issue("环境变量", "配置错误");
assert!(diagnostics.has_critical_issues());
}
#[test]
fn test_system_diagnostics_has_critical_issues_false() {
let mut diagnostics = utils::SystemDiagnostics::new();
diagnostics.add_issue("其他问题", "一般错误");
assert!(!diagnostics.has_critical_issues());
}
#[test]
fn test_system_diagnostics_default() {
let diagnostics: utils::SystemDiagnostics = Default::default();
assert_eq!(diagnostics.env_config_status, "未检查");
}
#[test]
fn test_diagnostic_issue_clone() {
let issue = utils::DiagnosticIssue {
category: "测试".to_string(),
description: "描述".to_string(),
};
let cloned = issue.clone();
assert_eq!(cloned.category, "测试");
assert_eq!(cloned.description, "描述");
}
}