Wire local TDLib into iOS FFI build

This commit is contained in:
Mikhail Kilin
2026-05-21 15:27:59 +03:00
parent aec3678bd6
commit 217328505c
34 changed files with 24460 additions and 28 deletions

View File

@@ -10,12 +10,14 @@ keywords = ["telegram", "tdlib"]
categories = ["api-bindings"]
[features]
default = []
default = ["tdlib-download"]
images = []
test-support = []
tdlib-download = ["tdlib-rs/download-tdlib"]
tdlib-local = ["tdlib-rs/local-tdlib"]
[dependencies]
tdlib-rs = { version = "1.2.0", features = ["download-tdlib"] }
tdlib-rs = { version = "1.2.0", default-features = false }
tokio = { version = "1", features = ["full"] }
async-trait = "0.1"
serde = { version = "1.0", features = ["derive"] }

View File

@@ -11,15 +11,17 @@ repository = "https://github.com/your-username/tele-tui"
crate-type = ["cdylib", "staticlib", "rlib"]
[features]
default = ["core-session"]
default = ["core-session-download"]
core-session = ["dep:tele-core"]
core-session-download = ["core-session", "tele-core/tdlib-download"]
core-session-local-tdlib = ["core-session", "tele-core/tdlib-local"]
standalone-fake = []
[dependencies]
tele-core = { path = "../tele-core", features = ["test-support"], optional = true }
tele-core = { path = "../tele-core", default-features = false, features = ["test-support"], optional = true }
tokio = { version = "1", features = ["rt-multi-thread"] }
thiserror = "1.0"
uniffi = { version = "0.31.1", features = ["tokio"] }
[dev-dependencies]
tele-core = { path = "../tele-core", features = ["test-support"] }
tele-core = { path = "../tele-core", default-features = false, features = ["test-support", "tdlib-download"] }

View File

@@ -6,7 +6,7 @@ Current scope:
- Exposes a fake-backed `SessionHandle` for Swift integration tests and app shell work.
- Mirrors the `tele-core::session` DTO/event model with UniFFI-compatible records and enums.
- Keeps real TDLib session creation out of this crate until iOS simulator/device linking is validated.
- Supports a fake-only build for UI work and a real TDLib build path using local iOS TDLib artifacts.
Generate Swift bindings and headers:
@@ -40,5 +40,7 @@ Current linking status:
- Xcode is installed at `/Applications/Xcode.app`, and `DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -version` reports Xcode 26.5.
- The iOS 26.5 simulator runtime is installed and `scripts/check-ios-prereqs.sh` passes with available iPhone/iPad simulators.
- The current app shell uses the fake Swift bridge. Real TDLib iOS simulator/device linking is still pending until TDLib is built for `iphonesimulator` and `iphoneos` and wired into the UniFFI target.
- Run `scripts/check-ios-tdlib-linking.sh` to reproduce the current TDLib iOS blocker documented in `docs/ios/tdlib-linking.md`.
- The current app shell uses the fake Swift bridge.
- `tdlib-rs` does not publish iOS `download-tdlib` archives, so real iOS linking uses `tele-core/tdlib-local` and `LOCAL_TDLIB_PATH`.
- Local TDLib linking is validated for `aarch64-apple-ios-sim` via `scripts/check-ios-tdlib-linking.sh` and for `aarch64-apple-ios` via `IOS_RUST_TARGET=aarch64-apple-ios scripts/build-ios-ffi-with-local-tdlib.sh`.
- `scripts/build-ios-real-ffi-xcframework.sh` packages local simulator Rust slices plus local `libtdjson` into app-local XCFrameworks, generates Swift bindings, and enables Xcode builds with `TELE_IOS_USE_LOCAL_FFI=1`.

View File

@@ -19,7 +19,7 @@ images = ["dep:ratatui-image", "dep:image", "tele-core/images"]
test-support = ["tele-core/test-support"]
[dependencies]
tele-core = { path = "../tele-core" }
tele-core = { path = "../tele-core", default-features = false, features = ["tdlib-download"] }
ratatui = "0.29"
crossterm = "0.28"
tdlib-rs = { version = "1.2.0", features = ["download-tdlib"] }

1
crates/vendor/tdlib-rs/.cargo-ok vendored Normal file
View File

@@ -0,0 +1 @@
{"v":1}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "b60e42456ddbcdcef994f9ffd07c92f1e94dc543"
},
"path_in_vcs": "tdlib-rs"
}

2407
crates/vendor/tdlib-rs/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

133
crates/vendor/tdlib-rs/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,133 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "tdlib-rs"
version = "1.2.0"
authors = [
"Federico Bruzzone <federico.bruzzone.i@gmail.com>",
"Andrea Longoni",
]
build = "build.rs"
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Rust wrapper around the Telegram Database Library."
homepage = "https://github.com/FedericoBruzzone/tdlib-rs"
documentation = "https://docs.rs/tdlib-rs"
readme = "README.md"
keywords = [
"telegram",
"tdlib",
"tdjson",
"tdlib-rs",
"telegram-api",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/FedericoBruzzone/tdlib-rs"
[package.metadata.docs.rs]
features = [
"docs",
"bots-only-api",
]
[package.metadata.system-deps]
tdjson = "1.8.29"
[features]
bots-only-api = []
default = []
docs = []
download-tdlib = [
"dep:reqwest",
"dep:zip",
]
local-tdlib = []
pkg-config = ["dep:system-deps"]
[lib]
name = "tdlib_rs"
path = "src/lib.rs"
[[example]]
name = "get_me"
path = "examples/get_me.rs"
[[example]]
name = "test_ci"
path = "examples/test_ci.rs"
[dependencies.dirs]
version = "6.0.0"
[dependencies.futures-channel]
version = "0.3"
[dependencies.log]
version = "0.4"
[dependencies.once_cell]
version = "1.18"
[dependencies.reqwest]
version = "0.12.4"
features = ["blocking"]
optional = true
[dependencies.serde]
version = "1.0"
features = ["derive"]
[dependencies.serde_json]
version = "1.0"
[dependencies.serde_with]
version = "3.2"
[dependencies.system-deps]
version = "7"
optional = true
[dependencies.zip]
version = "2.0.0"
optional = true
[dev-dependencies.tokio]
version = "1"
features = [
"macros",
"rt-multi-thread",
"sync",
"time",
]
[build-dependencies.reqwest]
version = "0.12.4"
features = ["blocking"]
optional = true
[build-dependencies.system-deps]
version = "7"
optional = true
[build-dependencies.tdlib-rs-gen]
version = "1.2.0"
[build-dependencies.tdlib-rs-parser]
version = "1.2.0"
[build-dependencies.zip]
version = "2.0.0"
optional = true

57
crates/vendor/tdlib-rs/Cargo.toml.orig generated vendored Normal file
View File

@@ -0,0 +1,57 @@
[package]
name = "tdlib-rs"
version = "1.2.0"
authors = [
"Federico Bruzzone <federico.bruzzone.i@gmail.com>",
"Andrea Longoni",
]
edition = "2021"
license = "MIT OR Apache-2.0"
homepage = "https://github.com/FedericoBruzzone/tdlib-rs"
repository = "https://github.com/FedericoBruzzone/tdlib-rs"
documentation = "https://docs.rs/tdlib-rs"
keywords = ["telegram", "tdlib", "tdjson", "tdlib-rs", "telegram-api"]
description = "Rust wrapper around the Telegram Database Library."
readme = "README.md"
[package.metadata.docs.rs]
features = ["docs", "bots-only-api"]
[package.metadata.system-deps]
tdjson = "1.8.29"
[features]
# The default feature build the library using the local tdlib library
default = []
# This feature is used to enable the functions only available to the Telegram bots
bots-only-api = []
# This feature is used to build the documentation preventing linking to the tdjson library
docs = []
# This feature is used to build the library using the tdlib library installed in the system
local-tdlib = []
# This feature is used to build the library using pkg-config
pkg-config = ["dep:system-deps"]
# This feature is used to build the library using the tdlib library downloaded from github
download-tdlib = ["dep:reqwest", "dep:zip"]
[dependencies]
log = "0.4"
futures-channel = "0.3"
once_cell = "1.18"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = "3.2"
system-deps = { version = "7", optional = true }
reqwest = { version = "0.12.4", features = ["blocking"], optional = true }
zip = { version = "2.0.0", optional = true }
dirs = "6.0.0"
[build-dependencies]
tdlib-rs-gen = { path = "../tdlib-rs-gen", version = "1.2.0" }
tdlib-rs-parser = { path = "../tdlib-rs-parser", version = "1.2.0" }
system-deps = { version = "7", optional = true }
reqwest = { version = "0.12.4", features = ["blocking"], optional = true }
zip = { version = "2.0.0", optional = true }
[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "time"] }

158
crates/vendor/tdlib-rs/README.md vendored Normal file
View File

@@ -0,0 +1,158 @@
[github-license-mit]: https://github.com/FedericoBruzzone/tdlib-rs/blob/main/LICENSE-MIT
[github-license-apache]: https://github.com/FedericoBruzzone/tdlib-rs/blob/main/LICENSE-APACHE
# tdlib-rs
[![Latest version](https://img.shields.io/crates/v/tdlib_rs.svg)](https://crates.io/crates/tdlib_rs)
[![Documentation](https://docs.rs/tdlib-rs/badge.svg)](https://docs.rs/tdlib-rs/latest/tdlib_rs/)
[![CI Linux](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-linux.yml/badge.svg)](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-linux.yml)
[![CI Windows](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-windows.yml/badge.svg)](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-windows.yml)
[![CI macOS](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-macos.yml/badge.svg)](https://github.com/FedericoBruzzone/tdlib-rs/actions/workflows/ci-macos.yml)
[![downloads](https://img.shields.io/crates/d/tdlib_rs)](https://crates.io/crates/tdlib_rs)
![license](https://img.shields.io/crates/l/tdlib_rs)
A Rust wrapper around the Telegram Database library. It includes a generator to automatically generate the types and functions from the TDLib's [Type Language](https://core.telegram.org/mtproto/TL) file.
## Why this fork?
This is an improved version of the [tdlib-rs](https://github.com/paper-plane-developers/tdlib-rs) library, with the following additional features:
1. It is cross-platform, it works on Windows, Linux and MacOS.
2. Not required `tdlib` to be compiled and installed on the system.
3. Not required `pkg-config` to build the library and associated exported variables.
4. Three different ways to build the library:
- `download-tdlib`: download the precompiled library from the GitHub releases.
- `local-tdlib`: use the `tdlib` installed on the system.
- `pkg-config`: use the `pkg-config` to build the library.
5. It is possible to download the `tdlib` library from the GitHub releases.
## Information
We provide a precompiled version of the library for the supported platforms:
- Linux (x86_64)
- Linux (arm64)
- macOS Intel (x86_64)
- macOS Apple Silicon (arm64)
- Windows (x86_64)
- Windows (arm64)
We compile it in the CI and we upload the artifacts to the GitHub releases, so we can download it and use to build this library.
It's mainly created for using it in the [tgt](https://github.com/FedericoBruzzone/tgt) client, but it should work also for any other Rust project.
Current supported TDLib version: [1.8.29](https://github.com/tdlib/td/commit/af69dd4397b6dc1bf23ba0fd0bf429fcba6454f6).
## Cargo features
Please see the documentation of the module `build` for more information about the features [here](https://docs.rs/tdlib-rs/latest/tdlib_rs/build/index.html).
It functions that you can use to build the library in different ways.
### download-tdlib
If you don't want to compile and intall the `tdlib` on your system manually, you should enable the `download-tdlib` feature in the `Cargo.toml` file:
```toml
[dependencies]
tdlib = { version = "...", features = [ "download-tdlib" ] }
[build-dependencies]
tdlib = { version = "...", features = [ "download-tdlib" ] }
```
```rust
// build.rs
fn main() {
tdlib_rs::build::build(None);
}
```
### local-tdlib
`local-tdlib` require you to have the `tdlib` (version 1.8.29) compiled and installed on your system, and the following variables exported, for example in the `.bashrc` file:
```sh
# The path to the tdlib folder
export LOCAL_TDLIB_PATH=$HOME/lib/tdlib
```
Then you can enable the `local-tdlib` feature in the `Cargo.toml` file:
```toml
[dependencies]
tdlib = { version = "...", features = [ "local-tdlib" ] }
[build-dependencies]
tdlib = { version = "...", features = [ "local-tdlib" ] }
```
```rust
// build.rs
fn main() {
tdlib_rs::build::build(None);
}
```
### pkg-config
If you want to use the `pkg-config` to build this library, you should enable the `pkg-config` feature in the `Cargo.toml` file:
```toml
[dependencies]
tdlib = { version = "...", features = [ "pkg-config" ] }
[build-dependencies]
tdlib = { version = "...", features = [ "pkg-config" ] }
```
```rust
// build.rs
fn main() {
tdlib_rs::build::build(None);
}
```
remember to have the `tdlib` (version 1.8.29) compiled on your system, and the following variables exported, for example in the `.bashrc` file:
```sh
# pkg-config configuration
export PKG_CONFIG_PATH=$HOME/lib/tdlib/lib/pkgconfig/:$PKG_CONFIG_PATH
# dynmic linker configuration
export LD_LIBRARY_PATH=$HOME/lib/tdlib/lib/:$LD_LIBRARY_PATH
```
### docs
This feature skip the linking of the library and only generate the code of `generated.rs`.
Is used only for testing.
### bots-only-api
This feature enable the generation of the functions only used by Telegram bots.
## License
This repository are licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE][github-license-apache] or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT][github-license-mit] or <http://opensource.org/licenses/MIT>)
at your option.
Please review the license file provided in the repository for more information regarding the terms and conditions of the license.
## Contact
If you have any questions, suggestions, or feedback, do not hesitate to [contact me](https://federicobruzzone.github.io/).
Mantainers:
- [FedericoBruzzone](https://github.com/FedericoBruzzone)
- [Andreal2000](https://github.com/Andreal2000)
## Credits
- [grammers](https://github.com/Lonami/grammers): the `tdlib-tl-gen` and `tdlib-tl-parser` projects are forks of the `grammers-tl-gen` and `grammers-tl-parser` projects.
- [rust-tdlib](https://github.com/aCLr/rust-tdlib): for inspiration about some client code.
- [tdlib-rs](https://github.com/paper-plane-developers/tdlib-rs): for inspiration about the generator code.

270
crates/vendor/tdlib-rs/build.rs vendored Normal file
View File

@@ -0,0 +1,270 @@
// Copyright 2020 - developers of the `grammers` project.
// Copyright 2021 - developers of the `tdlib-rs` project.
// Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::fs::File;
use std::io::{BufWriter, Read, Write};
use std::path::Path;
use tdlib_rs_gen::generate_rust_code;
use tdlib_rs_parser::parse_tl_file;
use tdlib_rs_parser::tl::Definition;
#[allow(dead_code)]
#[cfg(not(any(feature = "docs", feature = "pkg-config")))]
/// The version of the TDLib library.
const TDLIB_VERSION: &str = "1.8.29";
/// Load the type language definitions from a certain file.
/// Parse errors will be printed to `stderr`, and only the
/// valid results will be returned.
fn load_tl(file: &str) -> std::io::Result<Vec<Definition>> {
let mut file = File::open(file)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(parse_tl_file(contents)
.filter_map(|d| match d {
Ok(d) => Some(d),
Err(e) => {
eprintln!("TL: parse error: {e:?}");
None
}
})
.collect())
}
#[cfg(feature = "local-tdlib")]
/// Copy all files from a directory to another.
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
std::fs::create_dir_all(&dst)?;
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
} else {
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
#[cfg(feature = "local-tdlib")]
/// Copy all the tdlib folder find in the LOCAL_TDLIB_PATH environment variable to the OUT_DIR/tdlib folder
fn copy_local_tdlib() {
match env::var("LOCAL_TDLIB_PATH") {
Ok(tdlib_path) => {
let out_dir = env::var("OUT_DIR").unwrap();
let prefix = format!("{out_dir}/tdlib");
copy_dir_all(Path::new(&tdlib_path), Path::new(&prefix)).unwrap();
}
Err(_) => {
panic!("The LOCAL_TDLIB_PATH env variable must be set to the path of the tdlib folder");
}
};
}
#[cfg(any(feature = "download-tdlib", feature = "local-tdlib"))]
/// Build the project using the generic build configuration.
/// The current supported platforms are:
/// - Linux x86_64
/// - Linux aarch64
/// - Windows x86_64
/// - Windows aarch64
/// - MacOS x86_64
/// - MacOS aarch64
fn generic_build() {
let out_dir = env::var("OUT_DIR").unwrap();
let prefix = format!("{out_dir}/tdlib");
let include_dir = format!("{prefix}/include");
let lib_dir = format!("{prefix}/lib");
let lib_path = {
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64")
))]
{
format!("{lib_dir}/libtdjson.so.{TDLIB_VERSION}")
}
#[cfg(any(
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "ios", target_arch = "x86_64"),
all(target_os = "ios", target_arch = "aarch64")
))]
{
format!("{lib_dir}/libtdjson.{TDLIB_VERSION}.dylib")
}
#[cfg(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64")
))]
{
format!(r"{lib_dir}\tdjson.lib")
}
};
if !std::path::PathBuf::from(lib_path.clone()).exists() {
panic!("tdjson shared library not found at {lib_path}");
}
#[cfg(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64")
))]
{
let bin_dir = format!(r"{prefix}\bin");
println!("cargo:rustc-link-search=native={bin_dir}");
}
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:include={include_dir}");
println!("cargo:rustc-link-lib=dylib=tdjson");
println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}");
}
#[cfg(feature = "download-tdlib")]
fn download_tdlib() {
let base_url = "https://github.com/FedericoBruzzone/tdlib-rs/releases/download";
let url = format!(
"{}/v{}/tdlib-{}-{}-{}.zip",
base_url,
env!("CARGO_PKG_VERSION"),
TDLIB_VERSION,
std::env::var("CARGO_CFG_TARGET_OS").unwrap(),
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
);
// let target_os = if cfg!(target_os = "windows") {
// "Windows"
// } else if cfg!(target_os = "linux") {
// "Linux"
// } else if cfg!(target_os = "macos") {
// "macOS"
// } else {
// ""
// };
// let target_arch = if cfg!(target_arch = "x86_64") {
// "X64"
// } else if cfg!(target_arch = "aarch64") {
// "ARM64"
// } else {
// ""
// };
// let url = format!(
// "{}/test/{}-{}-TDLib-{}.zip",
// base_url, target_os, target_arch, "2589c3fd46925f5d57e4ec79233cd1bd0f5d0c09"
// );
let out_dir = env::var("OUT_DIR").unwrap();
let tdlib_dir = format!("{}/tdlib", &out_dir);
let zip_path = format!("{}.zip", &tdlib_dir);
// Create the request
let response = reqwest::blocking::get(&url).unwrap();
// Check if the response status is successful
if response.status().is_success() {
// Create a file to write to
let mut dest = File::create(&zip_path).unwrap();
// Get the response bytes and write to the file
let content = response.bytes().unwrap();
std::io::copy(&mut content.as_ref(), &mut dest).unwrap();
} else {
panic!(
"[{}] Failed to download file: {}\n{}\n{}",
"Your OS or architecture may be unsupported.",
"Please try using the `pkg-config` or `local-tdlib` features.",
response.status(),
&url
)
}
let mut archive = zip::ZipArchive::new(File::open(&zip_path).unwrap()).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let outpath = Path::new(&out_dir).join(file.name());
if (*file.name()).ends_with('/') {
std::fs::create_dir_all(&outpath).unwrap();
} else {
if let Some(p) = outpath.parent() {
if !p.exists() {
std::fs::create_dir_all(p).unwrap();
}
}
let mut outfile = File::create(&outpath).unwrap();
std::io::copy(&mut file, &mut outfile).unwrap();
}
// Get and set permissions
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = file.unix_mode() {
std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode)).unwrap();
}
}
}
let _ = std::fs::remove_file(&zip_path);
}
fn main() -> std::io::Result<()> {
#[cfg(all(feature = "docs", feature = "pkg-config"))]
compile_error!(
"feature \"docs\" and feature \"pkg-config\" cannot be enabled at the same time"
);
#[cfg(all(feature = "docs", feature = "download-tdlib"))]
compile_error!(
"feature \"docs\" and feature \"download-tdlib\" cannot be enabled at the same time"
);
#[cfg(all(feature = "pkg-config", feature = "download-tdlib"))]
compile_error!(
"feature \"pkg-config\" and feature \"download-tdlib\" cannot be enabled at the same time"
);
println!("cargo:rerun-if-changed=build.rs");
#[cfg(feature = "local-tdlib")]
println!("cargo:rerun-if-env-changed=LOCAL_TDLIB_PATH");
// Prevent linking libraries to avoid documentation failure
#[cfg(not(feature = "docs"))]
{
// It requires the following variables to be set:
// - export PKG_CONFIG_PATH=$HOME/lib/tdlib/lib/pkgconfig/:$PKG_CONFIG_PATH
// - export LD_LIBRARY_PATH=$HOME/lib/tdlib/lib/:$LD_LIBRARY_PATH
#[cfg(feature = "pkg-config")]
system_deps::Config::new().probe().unwrap();
#[cfg(feature = "download-tdlib")]
download_tdlib();
// It requires the following variable to be set:
// - export LOCAL_TDLIB_PATH=$HOME/lib/tdlib
#[cfg(feature = "local-tdlib")]
copy_local_tdlib();
#[cfg(any(feature = "download-tdlib", feature = "local-tdlib"))]
generic_build();
}
let out_dir = env::var("OUT_DIR").unwrap();
let definitions = load_tl("tl/api.tl")?;
let mut file = BufWriter::new(File::create(Path::new(&out_dir).join("generated.rs"))?);
generate_rust_code(&mut file, &definitions, cfg!(feature = "bots-only-api"))?;
file.flush()?;
Ok(())
}

View File

@@ -0,0 +1,193 @@
// cargo run -p tdlib-rs --example get_me --features default
// cargo run -p tdlib-rs --example get_me --features download-tdlib
// cargo run -p tdlib-rs --example get_me --features pkg-config
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
use tdlib_rs::{
enums::{self, AuthorizationState, Update, User},
functions,
};
use tokio::sync::mpsc::{self, Receiver, Sender};
fn ask_user(string: &str) -> String {
println!("{string}");
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
input.trim().to_string()
}
async fn handle_update(update: Update, auth_tx: &Sender<AuthorizationState>) {
if let Update::AuthorizationState(update) = update {
auth_tx.send(update.authorization_state).await.unwrap();
}
}
async fn handle_authorization_state(
client_id: i32,
mut auth_rx: Receiver<AuthorizationState>,
run_flag: Arc<AtomicBool>,
) -> Receiver<AuthorizationState> {
while let Some(state) = auth_rx.recv().await {
match state {
AuthorizationState::WaitTdlibParameters => {
let response = functions::set_tdlib_parameters(
false,
"get_me_db".into(),
String::new(),
String::new(),
false,
false,
false,
false,
env!("API_ID").parse().unwrap(),
env!("API_HASH").into(),
"en".into(),
"Desktop".into(),
String::new(),
env!("CARGO_PKG_VERSION").into(),
client_id,
)
.await;
if let Err(error) = response {
println!("{}", error.message);
}
}
AuthorizationState::WaitPhoneNumber => loop {
let input = ask_user("Enter your phone number (include the country calling code):");
let response =
functions::set_authentication_phone_number(input, None, client_id).await;
match response {
Ok(_) => break,
Err(e) => println!("{}", e.message),
}
},
AuthorizationState::WaitOtherDeviceConfirmation(x) => {
println!(
"Please confirm this login link on another device: {}",
x.link
);
}
AuthorizationState::WaitEmailAddress(_x) => {
let email_address = ask_user("Please enter email address: ");
let response =
functions::set_authentication_email_address(email_address, client_id).await;
match response {
Ok(_) => break,
Err(e) => println!("{}", e.message),
}
}
AuthorizationState::WaitEmailCode(_x) => {
let code = ask_user("Please enter email authentication code: ");
let response = functions::check_authentication_email_code(
enums::EmailAddressAuthentication::Code(
tdlib_rs::types::EmailAddressAuthenticationCode { code },
),
client_id,
)
.await;
match response {
Ok(_) => break,
Err(e) => println!("{}", e.message),
}
}
AuthorizationState::WaitCode(_) => loop {
let input = ask_user("Enter the verification code:");
let response = functions::check_authentication_code(input, client_id).await;
match response {
Ok(_) => break,
Err(e) => println!("{}", e.message),
}
},
AuthorizationState::WaitRegistration(_x) => {
// x useless but contains the TOS if we want to show it
let first_name = ask_user("Please enter your first name: ");
let last_name = ask_user("Please enter your last name: ");
functions::register_user(first_name, last_name, false, client_id)
.await
.unwrap();
}
AuthorizationState::WaitPassword(_x) => {
let password = ask_user("Please enter password: ");
functions::check_authentication_password(password, client_id)
.await
.unwrap();
}
AuthorizationState::Ready => {
break;
}
AuthorizationState::Closed => {
// Set the flag to false to stop receiving updates from the
// spawned task
run_flag.store(false, Ordering::Release);
break;
}
_ => (),
}
}
auth_rx
}
#[tokio::main]
async fn main() {
// Create the client object
let client_id = tdlib_rs::create_client();
// Create a mpsc channel for handling AuthorizationState updates separately
// from the task
let (auth_tx, auth_rx) = mpsc::channel(5);
// Create a flag to make it possible to stop receiving updates
let run_flag = Arc::new(AtomicBool::new(true));
let run_flag_clone = run_flag.clone();
// Spawn a task to receive updates/responses
let handle = tokio::spawn(async move {
while run_flag_clone.load(Ordering::Acquire) {
let result = tokio::task::spawn_blocking(tdlib_rs::receive)
.await
.unwrap();
if let Some((update, _client_id)) = result {
handle_update(update, &auth_tx).await;
} else {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
}
}
});
// tokio::spawn(async move {
// while run_flag_clone.load(Ordering::Acquire) {
// if let Some((update, _client_id)) = tdlib_rs::receive() {
// handle_update(update, &auth_tx).await;
// }
// }
// });
// Set a fairly low verbosity level. We mainly do this because tdlib
// requires to perform a random request with the client to start receiving
// updates for it.
functions::set_log_verbosity_level(2, client_id)
.await
.unwrap();
// Handle the authorization state to authenticate the client
let auth_rx = handle_authorization_state(client_id, auth_rx, run_flag.clone()).await;
// Run the get_me() method to get user information
let User::User(me) = functions::get_me(client_id).await.unwrap();
println!("Hi, I'm {}", me.first_name);
// Tell the client to close
functions::close(client_id).await.unwrap();
// Handle the authorization state to wait for the "Closed" state
handle_authorization_state(client_id, auth_rx, run_flag.clone()).await;
// Wait for the previously spawned task to end the execution
handle.await.unwrap();
}

View File

@@ -0,0 +1,12 @@
// cargo run -p tdlib-rs --example test_ci --features default
// cargo run -p tdlib-rs --example test_ci --features download-tdlib
// cargo run -p tdlib-rs --example test_ci --features pkg-config
#[tokio::main]
async fn main() {
// Create the client object for testing
let _client_id = tdlib_rs::create_client();
// Exit 0
std::process::exit(0);
}

493
crates/vendor/tdlib-rs/src/build.rs vendored Normal file
View File

@@ -0,0 +1,493 @@
//! The build module is used to build the project using the enabled features.
//! The features are correctly set when exactly one of the following features is enabled:
//! - `local-tdlib`
//! - `pkg-config`
//! - `download-tdlib`
#[allow(dead_code)]
#[cfg(not(any(feature = "docs", feature = "pkg-config")))]
const TDLIB_VERSION: &str = "1.8.29";
#[cfg(feature = "download-tdlib")]
const TDLIB_CARGO_PKG_VERSION: &str = "1.2.0";
// WARNING: This function is not used in the current version of the library.
// #[cfg(not(any(feature = "docs", feature = "pkg-config", feature = "download-tdlib")))]
// fn copy_local_tdlib() {
// match std::env::var("LOCAL_TDLIB_PATH") {
// Ok(tdlib_path) => {
// let out_dir = std::env::var("OUT_DIR").unwrap();
// let prefix = format!("{}/tdlib", out_dir);
// copy_dir_all(std::path::Path::new(&tdlib_path), std::path::Path::new(&prefix)).unwrap();
// }
// Err(_) => {
// panic!("The LOCAL_TDLIB_PATH env variable must be set to the path of the tdlib folder");
// }
// };
// }
#[cfg(feature = "download-tdlib")]
/// Copy all files from a directory to another.
/// It assumes that the source directory exists.
/// If the destination directory does not exist, it will be created.
fn copy_dir_all(
src: impl AsRef<std::path::Path>,
dst: impl AsRef<std::path::Path>,
) -> std::io::Result<()> {
std::fs::create_dir_all(&dst)?;
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
} else {
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
#[cfg(feature = "download-tdlib")]
/// Download the tdlib library from the GitHub release page.
/// The function will download the tdlib library from the GitHub release page, and extract the
/// files in the OUT_DIR/tdlib folder.
/// The OUT_DIR environment variable is set by Cargo and points to the target directory.
/// The OS and architecture currently supported are:
/// - Linux x86_64
/// - Linux aarch64
/// - Windows x86_64
/// - Windows aarch64
/// - MacOS x86_64
/// - MacOS aarch64
///
/// If the OS or architecture is not supported, the function will panic.
fn download_tdlib() {
let base_url = "https://github.com/FedericoBruzzone/tdlib-rs/releases/download";
let url = format!(
"{}/v{}/tdlib-{}-{}-{}.zip",
base_url,
TDLIB_CARGO_PKG_VERSION,
TDLIB_VERSION,
std::env::var("CARGO_CFG_TARGET_OS").unwrap(),
std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
);
let out_dir = std::env::var("OUT_DIR").unwrap();
let tdlib_dir = format!("{}/tdlib", &out_dir);
let zip_path = format!("{}.zip", &tdlib_dir);
// Create the request
let response = reqwest::blocking::get(&url).unwrap();
// Check if the response status is successful
if response.status().is_success() {
// Create a file to write to
let mut dest = std::fs::File::create(&zip_path).unwrap();
// Get the response bytes and write to the file
let content = response.bytes().unwrap();
std::io::copy(&mut content.as_ref(), &mut dest).unwrap();
} else {
panic!(
"[{}] Failed to download file: {}\n{}\n{}",
"Your OS or architecture may be unsupported.",
"Please try using the `pkg-config` or `local-tdlib` features.",
response.status(),
&url
)
}
let mut archive = zip::ZipArchive::new(std::fs::File::open(&zip_path).unwrap()).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let outpath = std::path::Path::new(&out_dir).join(file.name());
if (*file.name()).ends_with('/') {
std::fs::create_dir_all(&outpath).unwrap();
} else {
if let Some(p) = outpath.parent() {
if !p.exists() {
std::fs::create_dir_all(p).unwrap();
}
}
let mut outfile = std::fs::File::create(&outpath).unwrap();
std::io::copy(&mut file, &mut outfile).unwrap();
}
// Get and set permissions
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = file.unix_mode() {
std::fs::set_permissions(&outpath, std::fs::Permissions::from_mode(mode)).unwrap();
}
}
}
let _ = std::fs::remove_file(&zip_path);
}
#[cfg(any(feature = "download-tdlib", feature = "local-tdlib"))]
/// Build the project using the `download-tdlib` or `local-tdlib` feature.
/// # Arguments
/// - `lib_path`: The path where the tdlib library is located. If `None`, the path will be the `OUT_DIR` environment variable.
///
/// The function will pass to the `rustc` the following flags:
/// - `cargo:rustc-link-search=native=.../tdlib/lib`
/// - `cargo:include=.../tdlib/include`
/// - `cargo:rustc-link-lib=dylib=tdjson`
/// - `cargo:rustc-link-arg=-Wl,-rpath,.../tdlib/lib`
/// - `cargo:rustc-link-search=native=.../tdlib/bin` (only for Windows)
///
/// The `...` represents the `dest_path` or the `OUT_DIR` environment variable.
///
/// If the tdlib library is not found at the specified path, the function will panic.
///
/// The function will panic if the tdlib library is not found at the specified path.
fn generic_build(lib_path: Option<String>) {
let correct_lib_path: String;
match lib_path {
Some(lib_path) => {
if lib_path.ends_with('/') || lib_path.ends_with('\\') {
correct_lib_path = lib_path[..lib_path.len() - 1].to_string();
} else {
correct_lib_path = lib_path.to_string();
}
}
None => {
correct_lib_path = format!("{}/tdlib", std::env::var("OUT_DIR").unwrap());
}
}
let prefix = correct_lib_path.to_string();
let include_dir = format!("{prefix}/include");
let lib_dir = format!("{prefix}/lib");
let mut_lib_path = {
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64")
))]
{
format!("{lib_dir}/libtdjson.so.{TDLIB_VERSION}")
}
#[cfg(any(
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "ios", target_arch = "x86_64"),
all(target_os = "ios", target_arch = "aarch64")
))]
{
format!("{lib_dir}/libtdjson.{TDLIB_VERSION}.dylib")
}
#[cfg(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64")
))]
{
format!(r"{lib_dir}\tdjson.lib")
}
};
if !std::path::PathBuf::from(mut_lib_path.clone()).exists() {
panic!("tdjson shared library not found at {mut_lib_path}");
}
// This should be not necessary, but it is a workaround because windows does not find the
// tdjson.dll using the commands below.
// TODO: investigate and if it is a bug in `cargo` or `rustc`, open an issue to `cargo` to fix
// this.
#[cfg(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64")
))]
{
let bin_dir = format!(r"{prefix}\bin");
let cargo_bin = format!("{}/.cargo/bin", dirs::home_dir().unwrap().to_str().unwrap());
let libcrypto3x64 = format!(r"{bin_dir}\libcrypto-3-x64.dll");
let libssl3x64 = format!(r"{bin_dir}\libssl-3-x64.dll");
let tdjson = format!(r"{bin_dir}\tdjson.dll");
let zlib1 = format!(r"{bin_dir}\zlib1.dll");
let cargo_libcrypto3x64 = format!(r"{cargo_bin}\libcrypto-3-x64.dll");
let cargo_libssl3x64 = format!(r"{cargo_bin}\libssl-3-x64.dll");
let cargo_tdjson = format!(r"{cargo_bin}\tdjson.dll");
let cargo_zlib1 = format!(r"{cargo_bin}\zlib1.dll");
// Delete the files if they exist
let _ = std::fs::remove_file(&cargo_libcrypto3x64);
let _ = std::fs::remove_file(&cargo_libssl3x64);
let _ = std::fs::remove_file(&cargo_tdjson);
let _ = std::fs::remove_file(&cargo_zlib1);
// Move all files to cargo_bin
let _ = std::fs::copy(libcrypto3x64.clone(), cargo_libcrypto3x64.clone());
let _ = std::fs::copy(libssl3x64.clone(), cargo_libssl3x64.clone());
let _ = std::fs::copy(tdjson.clone(), cargo_tdjson.clone());
let _ = std::fs::copy(zlib1.clone(), cargo_zlib1.clone());
}
#[cfg(any(
all(target_os = "windows", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "aarch64")
))]
{
let bin_dir = format!(r"{prefix}\bin");
println!("cargo:rustc-link-search=native={bin_dir}");
}
println!("cargo:rustc-link-search=native={lib_dir}");
println!("cargo:include={include_dir}");
println!("cargo:rustc-link-lib=dylib=tdjson");
println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}");
}
/// Check if the features are correctly set.
/// The features are correctly set when exactly one of the following features is enabled:
/// - `local-tdlib`
/// - `pkg-config`
/// - `download-tdlib`
/// - `docs` (only for tdlib documentation)
///
/// The following features cannot be enabled at the same time:
/// - `docs` and `pkg-config`
/// - `docs` and `download-tdlib`
/// - `docs` and `local-tdlib`
/// - `pkg-config` and `local-tdlib`
/// - `pkg-config` and `download-tdlib`
/// - `local-tdlib` and `download-tdlib`
///
/// If the features are not correctly set, the function will generate a compile error
pub fn check_features() {
// #[cfg(not(any(feature = "docs", feature = "local-tdlib", feature = "pkg-config", feature = "download-tdlib")))]
// println!("cargo:warning=No features enabled, you must enable at least one of the following features: docs, local-tdlib, pkg-config, download-tdlib");
// compile_error!("You must enable at least one of the following features: docs, local-tdlib, pkg-config, download-tdlib");
#[cfg(all(feature = "docs", feature = "pkg-config"))]
compile_error!(
"feature \"docs\" and feature \"pkg-config\" cannot be enabled at the same time"
);
#[cfg(all(feature = "docs", feature = "download-tdlib"))]
compile_error!(
"feature \"docs\" and feature \"download-tdlib\" cannot be enabled at the same time"
);
#[cfg(all(feature = "docs", feature = "local-tdlib"))]
compile_error!(
"feature \"docs\" and feature \"local-tdlib\" cannot be enabled at the same time"
);
#[cfg(all(feature = "pkg-config", feature = "local-tdlib"))]
compile_error!(
"feature \"pkg-config\" and feature \"local-tdlib\" cannot be enabled at the same time"
);
#[cfg(all(feature = "pkg-config", feature = "download-tdlib"))]
compile_error!(
"feature \"pkg-config\" and feature \"download-tdlib\" cannot be enabled at the same time"
);
#[cfg(all(feature = "local-tdlib", feature = "download-tdlib"))]
compile_error!(
"feature \"local-tdlib\" and feature \"download-tdlib\" cannot be enabled at the same time"
);
}
/// Set the `rerun-if-changed` and `rerun-if-env-changed` flags for the build script.
/// The `rerun-if-changed` flag is set for the `build.rs` file.
/// The `rerun-if-env-changed` flag is set for the `LOCAL_TDLIB_PATH` environment variable.
pub fn set_rerun_if() {
#[cfg(feature = "local-tdlib")]
println!("cargo:rerun-if-env-changed=LOCAL_TDLIB_PATH");
println!("cargo:rerun-if-changed=build.rs");
}
#[cfg(any(feature = "pkg-config", feature = "docs"))]
#[allow(clippy::needless_doctest_main)]
/// Build the project using the `pkg-config` feature.
/// Using the `pkg-config` feature, the function will probe the system dependencies.
/// It means that the function assumes that the tdlib library is compiled in the system.
/// It requires the following variables to be set:
/// - `PKG_CONFIG_PATH=$HOME/lib/tdlib/lib/pkgconfig/:$PKG_CONFIG_PATH`
/// - `LD_LIBRARY_PATH=$HOME/lib/tdlib/lib/:$LD_LIBRARY_PATH`
///
/// If the variables are not set, the function will panic.
///
/// # Example
/// Cargo.toml:
/// ```toml
/// [dependencies]
/// tdlib = { version = "...", features = ["pkg-config"] }
/// ```
///
/// build.rs:
/// ```rust
/// fn main() {
/// tdlib_rs::build::check_features();
/// tdlib_rs::build::set_rerun_if();
/// tdlib_rs::build::build_pkg_config();
/// // Other build configurations
/// // ...
/// }
/// ```
pub fn build_pkg_config() {
#[cfg(not(feature = "docs"))]
{
system_deps::Config::new().probe().unwrap();
}
}
#[cfg(any(feature = "download-tdlib", feature = "docs"))]
#[allow(clippy::needless_doctest_main)]
#[allow(unused_variables)]
/// Build the project using the `download-tdlib` feature.
///
/// # Arguments
/// - `dest_path`: The destination path where the tdlib library will be copied. If `None`, the path will be the `OUT_DIR` environment variable.
///
/// Note that this function will pass to the `rustc` the following flags:
/// - `cargo:rustc-link-search=native=.../tdlib/lib`
/// - `cargo:include=.../tdlib/include`
/// - `cargo:rustc-link-lib=dylib=tdjson`
/// - `cargo:rustc-link-arg=-Wl,-rpath,.../tdlib/lib`
/// - `cargo:rustc-link-search=native=.../tdlib/bin` (only for Windows)
///
/// The `...` represents the `dest_path` or the `OUT_DIR` environment variable.
///
/// The function will download the tdlib library from the GitHub release page.
/// Using the `download-tdlib` feature, no system dependencies are required.
/// The OS and architecture currently supported are:
/// - Linux x86_64
/// - Linux aarch64
/// - Windows x86_64
/// - Windows aarch64
/// - MacOS x86_64
/// - MacOS aarch64
///
/// If the OS or architecture is not supported, the function will panic.
///
/// # Example
/// Cargo.toml:
/// ```toml
/// [dependencies]
/// tdlib = { version = "...", features = ["download-tdlib"] }
///
/// [build-dependencies]
/// tdlib = { version = "...", features = [ "download-tdlib" ] }
/// ```
///
/// build.rs:
/// ```rust
/// fn main() {
/// tdlib_rs::build::check_features();
/// tdlib_rs::build::set_rerun_if();
/// tdlib_rs::build::build_download_tdlib(None);
/// // Other build configurations
/// // ...
/// }
/// ```
pub fn build_download_tdlib(dest_path: Option<String>) {
#[cfg(not(feature = "docs"))]
{
download_tdlib();
if let Some(dest_path) = &dest_path {
let out_dir = std::env::var("OUT_DIR").unwrap();
let tdlib_dir = format!("{}/tdlib", &out_dir);
copy_dir_all(
std::path::Path::new(&tdlib_dir),
std::path::Path::new(&dest_path),
)
.unwrap();
}
generic_build(dest_path);
}
}
#[cfg(any(feature = "local-tdlib", feature = "docs"))]
#[allow(clippy::needless_doctest_main)]
/// Build the project using the `local-tdlib` feature.
/// Using the `local-tdlib` feature, the function will copy the tdlib library from the
/// `LOCAL_TDLIB_PATH` environment variable.
/// The tdlib folder must contain the `lib` and `include` folders.
/// You can directly download the tdlib library from the [TDLib Release GitHub page](https://github.com/FedericoBruzzone/tdlib-rs/releases).
///
/// The `LOCAL_TDLIB_PATH` environment variable must be set to the path of the tdlib folder.
///
/// The function will pass to the `rustc` the following flags:
/// - `cargo:rustc-link-search=native=.../tdlib/lib`
/// - `cargo:include=.../tdlib/include`
/// - `cargo:rustc-link-lib=dylib=tdjson`
/// - `cargo:rustc-link-arg=-Wl,-rpath,.../tdlib/lib`
/// - `cargo:rustc-link-search=native=.../tdlib/bin` (only for Windows)
///
/// The `...` represents the `LOCAL_TDLIB_PATH` environment variable.
///
/// If the `LOCAL_TDLIB_PATH` environment variable is not set, the function will panic.
///
/// # Example
/// Cargo.toml:
/// ```toml
/// [dependencies]
/// tdlib = { version = "...", features = ["local-tdlib"] }
///
/// [build-dependencies]
/// tdlib = { version = "...", features = [ "download-tdlib" ] }
/// ```
///
/// build.rs:
/// ```rust
/// fn main() {
/// tdlib_rs::build::check_features();
/// tdlib_rs::build::set_rerun_if();
/// tdlib_rs::build::build_local_tdlib();
/// // Other build configurations
/// // ...
/// }
/// ```
pub fn build_local_tdlib() {
#[cfg(not(feature = "docs"))]
{
// copy_local_tdlib();
let path = std::env::var("LOCAL_TDLIB_PATH").unwrap();
generic_build(Some(path));
}
}
#[allow(clippy::needless_doctest_main)]
/// Build the project using the enabled features.
///
/// # Arguments
/// - `dest_path`: The destination path where the tdlib library will be copied. If `None`, the path
/// will be the `OUT_DIR` environment variable. This argument is used only when the
/// `download-tdlib` feature is enabled.
///
/// The function will check if the features are correctly set.
/// The function will set the `rerun-if-changed` and `rerun-if-env-changed` flags for the build
/// script.
/// The function will build the project using the enabled feature.
///
/// # Example
/// Cargo.toml:
/// ```toml
/// [dependencies]
/// tdlib = { version = "...", features = ["download-tdlib"] }
///
/// [build-dependencies]
/// tdlib = { version = "...", features = [ "download-tdlib" ] }
/// ```
///
/// build.rs:
/// ```rust
/// fn main() {
/// tdlib_rs::build::build(None);
/// // Other build configurations
/// // ...
/// }
/// ```
pub fn build(_dest_path: Option<String>) {
check_features();
set_rerun_if();
#[cfg(feature = "pkg-config")]
build_pkg_config();
#[cfg(feature = "download-tdlib")]
build_download_tdlib(_dest_path);
#[cfg(feature = "local-tdlib")]
build_local_tdlib();
}

10
crates/vendor/tdlib-rs/src/generated.rs vendored Normal file
View File

@@ -0,0 +1,10 @@
// Copyright 2020 - developers of the `grammers` project.
// Copyright 2021 - developers of the `tdlib-rs` project.
// Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
include!(concat!(env!("OUT_DIR"), "/generated.rs"));

69
crates/vendor/tdlib-rs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,69 @@
// Copyright 2020 - developers of the `grammers` project.
// Copyright 2021 - developers of the `tdlib-rs` project.
// Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub mod build;
mod generated;
mod observer;
mod tdjson;
pub use generated::{enums, functions, types};
use enums::Update;
use once_cell::sync::Lazy;
use serde_json::Value;
use std::sync::atomic::{AtomicU32, Ordering};
static EXTRA_COUNTER: AtomicU32 = AtomicU32::new(0);
static OBSERVER: Lazy<observer::Observer> = Lazy::new(observer::Observer::new);
/// Create a TdLib client returning its id. Note that to start receiving
/// updates for a client you need to send at least a request with it first.
pub fn create_client() -> i32 {
tdjson::create_client()
}
/// Receive a single update or response from TdLib. If it's an update, it
/// returns a tuple with the `Update` and the associated `client_id`.
/// Note that to start receiving updates for a client you need to send
/// at least a request with it first.
pub fn receive() -> Option<(Update, i32)> {
let response = tdjson::receive(2.0);
if let Some(response_str) = response {
let response: Value = serde_json::from_str(&response_str).unwrap();
match response.get("@extra") {
Some(_) => {
OBSERVER.notify(response);
}
None => {
let client_id = response["@client_id"].as_i64().unwrap() as i32;
match serde_json::from_value(response) {
Ok(update) => {
return Some((update, client_id));
}
Err(e) => {
log::warn!("Received an unknown response: {response_str}\nReason: {e}");
}
}
}
}
}
None
}
pub(crate) async fn send_request(client_id: i32, mut request: Value) -> Value {
let extra = EXTRA_COUNTER.fetch_add(1, Ordering::Relaxed);
request["@extra"] = serde_json::to_value(extra).unwrap();
let receiver = OBSERVER.subscribe(extra);
tdjson::send(client_id, request.to_string());
receiver.await.unwrap()
}

45
crates/vendor/tdlib-rs/src/observer.rs vendored Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2020 - developers of the `grammers` project.
// Copyright 2021 - developers of the `tdlib-rs` project.
// Copyright 2024 - developers of the `tgt` project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use futures_channel::oneshot;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::RwLock;
pub(super) struct Observer {
requests: RwLock<HashMap<u32, oneshot::Sender<Value>>>,
}
impl Observer {
pub fn new() -> Self {
Observer {
requests: RwLock::default(),
}
}
pub fn subscribe(&self, extra: u32) -> oneshot::Receiver<Value> {
let (sender, receiver) = oneshot::channel();
self.requests.write().unwrap().insert(extra, sender);
receiver
}
pub fn notify(&self, response: Value) {
let extra = response["@extra"].as_u64().unwrap() as u32;
match self.requests.write().unwrap().remove(&extra) {
Some(sender) => {
if sender.send(response).is_err() {
log::warn!("Got a response of an unaccessible request");
}
}
None => {
log::warn!("Got a response of an unknown request");
}
}
}
}

35
crates/vendor/tdlib-rs/src/tdjson.rs vendored Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2020 - developers of the `grammers` project.
// Copyright 2021 - developers of the `tdlib-rs` project.
// Copyright 2024 - developers of the `tgt` project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_double, c_int};
#[link(name = "tdjson")]
extern "C" {
fn td_create_client_id() -> c_int;
fn td_send(client_id: c_int, request: *const c_char);
fn td_receive(timeout: c_double) -> *const c_char;
}
pub(crate) fn create_client() -> i32 {
unsafe { td_create_client_id() }
}
pub(crate) fn send(client_id: i32, request: String) {
let cstring = CString::new(request).unwrap();
unsafe { td_send(client_id, cstring.as_ptr()) }
}
pub(crate) fn receive(timeout: f64) -> Option<String> {
unsafe {
td_receive(timeout)
.as_ref()
.map(|response| CStr::from_ptr(response).to_string_lossy().into_owned())
}
}

10841
crates/vendor/tdlib-rs/tl/api.tl vendored Normal file

File diff suppressed because it is too large Load Diff

9177
crates/vendor/tdlib-rs/tl/api_1.8.19.tl vendored Normal file

File diff suppressed because it is too large Load Diff