use layer_tl_types as tl;
use std::sync::Mutex;
use crate::errors::InvocationError;
pub fn fallback_dc_addr(dc_id: i32) -> &'static str {
match dc_id {
1 => "149.154.175.53:443",
2 => "149.154.167.51:443",
3 => "149.154.175.100:443",
4 => "149.154.167.91:443",
5 => "91.108.56.130:443",
_ => "149.154.167.51:443", }
}
pub fn default_dc_addresses() -> Vec<(i32, String)> {
(1..=5)
.map(|id| (id, fallback_dc_addr(id).to_string()))
.collect()
}
pub struct DcAuthTracker {
copied: Mutex<Vec<i32>>,
}
impl DcAuthTracker {
pub fn new() -> Self {
Self {
copied: Mutex::new(Vec::new()),
}
}
pub fn has_copied(&self, dc_id: i32) -> bool {
self.copied.lock().unwrap().contains(&dc_id)
}
pub fn mark_copied(&self, dc_id: i32) {
self.copied.lock().unwrap().push(dc_id);
}
}
impl Default for DcAuthTracker {
fn default() -> Self {
Self::new()
}
}
pub async fn copy_auth_to_dc<F, Fut>(
home_dc_id: i32,
target_dc_id: i32,
tracker: &DcAuthTracker,
invoke_fn: F, invoke_on_dc_fn: impl Fn(i32, tl::functions::auth::ImportAuthorization) -> Fut,
) -> Result<(), InvocationError>
where
F: std::future::Future<
Output = Result<tl::enums::auth::ExportedAuthorization, InvocationError>,
>,
Fut: std::future::Future<Output = Result<tl::enums::auth::Authorization, InvocationError>>,
{
if target_dc_id == home_dc_id {
return Ok(());
}
if tracker.has_copied(target_dc_id) {
return Ok(());
}
let tl::enums::auth::ExportedAuthorization::ExportedAuthorization(exported) = invoke_fn.await?;
invoke_on_dc_fn(
target_dc_id,
tl::functions::auth::ImportAuthorization {
id: exported.id,
bytes: exported.bytes,
},
)
.await?;
tracker.mark_copied(target_dc_id);
Ok(())
}
pub const MIGRATE_PATCH_DESCRIPTION: &str = "see doc comment above";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_dcs_return_correct_ips() {
assert_eq!(fallback_dc_addr(1), "149.154.175.53:443");
assert_eq!(fallback_dc_addr(2), "149.154.167.51:443");
assert_eq!(fallback_dc_addr(3), "149.154.175.100:443");
assert_eq!(fallback_dc_addr(4), "149.154.167.91:443");
assert_eq!(fallback_dc_addr(5), "91.108.56.130:443");
}
#[test]
fn unknown_dc_falls_back_to_dc2() {
assert_eq!(fallback_dc_addr(99), "149.154.167.51:443");
}
#[test]
fn default_dc_addresses_has_five_entries() {
let addrs = default_dc_addresses();
assert_eq!(addrs.len(), 5);
for id in 1..=5_i32 {
assert!(addrs.iter().any(|(dc_id, _)| *dc_id == id));
}
}
#[test]
fn tracker_starts_empty() {
let t = DcAuthTracker::new();
assert!(!t.has_copied(2));
assert!(!t.has_copied(4));
}
#[test]
fn tracker_marks_and_checks() {
let t = DcAuthTracker::new();
t.mark_copied(4);
assert!(t.has_copied(4));
assert!(!t.has_copied(2));
}
#[test]
fn tracker_marks_multiple_dcs() {
let t = DcAuthTracker::new();
t.mark_copied(2);
t.mark_copied(4);
t.mark_copied(5);
assert!(t.has_copied(2));
assert!(t.has_copied(4));
assert!(t.has_copied(5));
assert!(!t.has_copied(1));
assert!(!t.has_copied(3));
}
#[test]
fn rpc_error_migrate_detection_all_variants() {
use crate::errors::RpcError;
for name in &[
"PHONE_MIGRATE",
"NETWORK_MIGRATE",
"FILE_MIGRATE",
"USER_MIGRATE",
] {
let e = RpcError {
code: 303,
name: name.to_string(),
value: Some(4),
};
assert_eq!(e.migrate_dc_id(), Some(4), "failed for {name}");
}
}
#[test]
fn invocation_error_migrate_dc_id_delegates() {
use crate::errors::{InvocationError, RpcError};
let e = InvocationError::Rpc(RpcError {
code: 303,
name: "PHONE_MIGRATE".into(),
value: Some(5),
});
assert_eq!(e.migrate_dc_id(), Some(5));
}
}