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")))]
const TDLIB_VERSION: &str = "1.8.29";
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(not(any(feature = "docs", feature = "pkg-config", feature = "download-tdlib")))]
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(not(any(feature = "docs", feature = "pkg-config", feature = "download-tdlib")))]
fn copy_local_tdlib() {
match env::var("LOCAL_TDLIB_PATH") {
Ok(tdlib_path) => {
let out_dir = env::var("OUT_DIR").unwrap();
let prefix = format!("{}/tdlib", out_dir);
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(not(any(feature = "docs", feature = "pkg-config")))]
fn generic_build() {
let out_dir = env::var("OUT_DIR").unwrap();
let prefix = format!("{}/tdlib", out_dir);
let include_dir = format!("{}/include", prefix);
let lib_dir = format!("{}/lib", prefix);
let lib_path = {
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
{
format!("{}/libtdjson.so.{}", lib_dir, TDLIB_VERSION)
}
#[cfg(any(
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "aarch64")
))]
{
format!("{}/libtdjson.{}.dylib", lib_dir, TDLIB_VERSION)
}
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
{
format!(r"{}\tdjson.lib", lib_dir)
}
};
if !std::path::PathBuf::from(lib_path.clone()).exists() {
panic!("tdjson shared library not found at {}", lib_path);
}
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
{
let bin_dir = format!(r"{}\bin", prefix);
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");
}
#[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 out_dir = env::var("OUT_DIR").unwrap();
let tdlib_dir = format!("{}/tdlib", &out_dir);
let zip_path = format!("{}.zip", &tdlib_dir);
let response = reqwest::blocking::get(&url).unwrap();
if response.status().is_success() {
let mut dest = File::create(&zip_path).unwrap();
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();
}
#[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"
);
#[cfg(not(any(feature = "docs", feature = "pkg-config", feature = "download-tdlib")))]
println!("cargo:rerun-if-env-changed=LOCAL_TDLIB_PATH");
println!("cargo:rerun-if-changed=build.rs");
#[cfg(not(feature = "docs"))]
{
#[cfg(feature = "pkg-config")]
system_deps::Config::new().probe().unwrap();
#[cfg(feature = "download-tdlib")]
download_tdlib();
#[cfg(not(feature = "pkg-config"))]
{
#[cfg(not(feature = "download-tdlib"))]
copy_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(())
}