diff --git a/Cargo.lock b/Cargo.lock index 289136ca88..eda1fead4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,6 +332,14 @@ dependencies = [ "url", ] +[[package]] +name = "azure_data_cosmos_native" +version = "0.27.0" +dependencies = [ + "azure_data_cosmos", + "cbindgen", +] + [[package]] name = "azure_identity" version = "0.29.0" @@ -691,6 +699,25 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cbindgen" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "975982cdb7ad6a142be15bdf84aea7ec6a9e5d4d797c004d43185b24cfe4e684" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.104", + "tempfile", + "toml", +] + [[package]] name = "cc" version = "1.2.32" @@ -2614,6 +2641,15 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_test" version = "1.0.177" @@ -2998,11 +3034,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -3011,10 +3062,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tower" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index 270868cf15..973264acaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "sdk/core/azure_core_test_macros", "sdk/core/azure_core_opentelemetry", "sdk/cosmos/azure_data_cosmos", + "sdk/cosmos/azure_data_cosmos_native", "sdk/identity/azure_identity", "sdk/eventhubs/azure_messaging_eventhubs", "sdk/eventhubs/azure_messaging_eventhubs_checkpointstore_blob", diff --git a/deny.toml b/deny.toml index 249310d38f..42e142afde 100644 --- a/deny.toml +++ b/deny.toml @@ -13,6 +13,7 @@ allow = [ "BSL-1.0", "ISC", "MIT", + "MPL-2.0", # "OpenSSL", "Unicode-3.0", "Zlib", diff --git a/eng/dict/crates.txt b/eng/dict/crates.txt index 7af9fde24f..8fa4cd83d6 100644 --- a/eng/dict/crates.txt +++ b/eng/dict/crates.txt @@ -16,8 +16,9 @@ azure_messaging_eventhubs azure_security_keyvault_keys azure_security_keyvault_secrets azure_storage_blob -azure_canary -azure_canary_core +azure_storage_common +azure_template +azure_template_core base64 bytes cargo_metadata diff --git a/eng/dict/rust-custom.txt b/eng/dict/rust-custom.txt index b3b37a5653..174812db94 100644 --- a/eng/dict/rust-custom.txt +++ b/eng/dict/rust-custom.txt @@ -12,3 +12,7 @@ rustflags rustls rustsec turbofish +dylib +cdylib +staticlib +cbindgen diff --git a/sdk/cosmos/.dict.txt b/sdk/cosmos/.dict.txt index f6b563e481..7d5bae540f 100644 --- a/sdk/cosmos/.dict.txt +++ b/sdk/cosmos/.dict.txt @@ -8,3 +8,6 @@ udfs # Cosmos' docs all use "Autoscale" as a single word, rather than a compound "AutoScale" or "Auto Scale" autoscale + +# Words used within the Cosmos Native Client (azure_data_cosmos_native) +cosmosclient diff --git a/sdk/cosmos/azure_data_cosmos/Cargo.toml b/sdk/cosmos/azure_data_cosmos/Cargo.toml index 22dce2f890..e16d9f86ce 100644 --- a/sdk/cosmos/azure_data_cosmos/Cargo.toml +++ b/sdk/cosmos/azure_data_cosmos/Cargo.toml @@ -43,4 +43,9 @@ hmac_rust = ["azure_core/hmac_rust"] hmac_openssl = ["azure_core/hmac_openssl"] [package.metadata.docs.rs] -features = ["key_auth"] +features = [ + "key_auth", + "preview_query_engine", + "hmac_rust", + "hmac_openssl", +] diff --git a/sdk/cosmos/azure_data_cosmos_native/.gitignore b/sdk/cosmos/azure_data_cosmos_native/.gitignore new file mode 100644 index 0000000000..567609b123 --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/sdk/cosmos/azure_data_cosmos_native/CMakeLists.txt b/sdk/cosmos/azure_data_cosmos_native/CMakeLists.txt new file mode 100644 index 0000000000..e1b8b7289b --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/CMakeLists.txt @@ -0,0 +1,33 @@ +# cSpell:ignore cosmosctest CRATETYPES endforeach + +project(cosmosctest C) +cmake_minimum_required(VERSION 4.1) + +# CMake automatically uses this option, but we should define it. +option(BUILD_SHARED_LIBS "Build using shared libraries" ON) + +include(FetchContent) +include(CTest) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5.2 +) +FetchContent_MakeAvailable(Corrosion) + +corrosion_import_crate( + MANIFEST_PATH ./Cargo.toml + CRATETYPES staticlib cdylib +) + +set(TEST_FILES + ./c_tests/version.c) + +foreach(test_file ${TEST_FILES}) + get_filename_component(test_name ${test_file} NAME_WE) + add_executable(${test_name} ${test_file}) + target_link_libraries(${test_name} PRIVATE azurecosmos) + add_test(${test_name} ${test_name}) +endforeach() + diff --git a/sdk/cosmos/azure_data_cosmos_native/Cargo.toml b/sdk/cosmos/azure_data_cosmos_native/Cargo.toml new file mode 100644 index 0000000000..504ff19443 --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "azure_data_cosmos_native" +publish = false +description = "The Cosmos Native Client is a C library, written in Rust but exporting a C-compatible API, that provides a full SDK for Azure Cosmos DB." +version = "0.27.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +name = "azurecosmos" +crate-type = ["cdylib", "staticlib"] + +[dependencies] +azure_data_cosmos = { path = "../azure_data_cosmos" } + +[build-dependencies] +cbindgen = "0.29.0" + +[lints] +workspace = true diff --git a/sdk/cosmos/azure_data_cosmos_native/build.rs b/sdk/cosmos/azure_data_cosmos_native/build.rs new file mode 100644 index 0000000000..9546699488 --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/build.rs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// cSpell:ignore SOURCEVERSION, SOURCEBRANCH, BUILDID, BUILDNUMBER, COSMOSCLIENT, cosmosclient, libcosmosclient, cbindgen + +fn main() { + let build_id = format!( + "$Id: {}, Version: {}, Commit: {}, Branch: {}, Build ID: {}, Build Number: {}, Timestamp: {}$", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + option_env!("BUILD_SOURCEVERSION").unwrap_or("unknown"), + option_env!("BUILD_SOURCEBRANCH").unwrap_or("unknown"), + option_env!("BUILD_BUILDID").unwrap_or("unknown"), + option_env!("BUILD_BUILDNUMBER").unwrap_or("unknown"), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(), + ); + println!("cargo:rustc-env=BUILD_IDENTIFIER={}", build_id); + + let mut header: String = r"// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// This file is auto-generated by cbindgen. Do not edit manually. +// cSpell: disable +" + .to_string(); + header.push_str(&format!("// Build identifier: {}\n", build_id)); + + let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_language(cbindgen::Language::C) + .with_after_include(format!( + "\n// Specifies the version of cosmosclient this header file was generated from.\n// This should match the version of libcosmosclient you are referencing.\n#define COSMOSCLIENT_H_VERSION \"{}\"", + env!("CARGO_PKG_VERSION") + )) + .with_cpp_compat(true) + .with_header(header) + .generate() + .expect("unable to generate bindings") + .write_to_file("include/cosmosclient.h"); +} diff --git a/sdk/cosmos/azure_data_cosmos_native/c_tests/README.md b/sdk/cosmos/azure_data_cosmos_native/c_tests/README.md new file mode 100644 index 0000000000..962e1103cd --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/c_tests/README.md @@ -0,0 +1,44 @@ +# Cosmos Client C Tests + +This directory contains tests written in C that utilize the Cosmos Client library. The tests are designed to validate the functionality of the Cosmos Client C API, ensuring that it correctly interacts with the Azure CosmosDB service. + +Building this directory requires CMake, Rust, and a C compiler. + +## Running the tests + +To run the tests, follow these steps: + +1. Create a `build` directory: + +```bash +mkdir build +cd build +``` + +1. Configure the project with CMake: + +```bash +cmake .. +``` + +1. Run the build AND tests: + +```bash +make && make test +``` + +## Test Structure + +Each test is a separate C program located in the `c_tests` directory. +The tests are compiled into executables that can be run independently. +Tests must be manually listed in `CMakeLists.txt` to be included in the build process: + +```cmake +set(TEST_FILES + ./c_tests/version.c + ./c_tests/your_test_here.c) +``` + +Once a test is present in the `TEST_FILES` list, it will be automatically compiled and linked against the Cosmos Client library (either static, or dynamic, depending on the value of `BUILD_SHARED_LIBS` when `cmake` is run, which defaults to `ON`). + +Helper code should be added to an include file in the `c_tests` directory, such as `c_tests/test_helpers.h`, and included in the test files as needed. diff --git a/sdk/cosmos/azure_data_cosmos_native/c_tests/version.c b/sdk/cosmos/azure_data_cosmos_native/c_tests/version.c new file mode 100644 index 0000000000..bf193c926d --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/c_tests/version.c @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include +#include +#include "../include/cosmosclient.h" + +int main() { + const char *version = cosmosclient_version(); + const char *header_version = COSMOSCLIENT_H_VERSION; + printf("Cosmos Client Version: %s\n", version); + printf("Header Version: %s\n", header_version); + if (!strcmp(version, header_version)) { + printf("Version match successful.\n"); + return 0; + } else { + printf("Version mismatch: %s != %s\n", version, header_version); + return 1; + } +} diff --git a/sdk/cosmos/azure_data_cosmos_native/include/.gitignore b/sdk/cosmos/azure_data_cosmos_native/include/.gitignore new file mode 100644 index 0000000000..b429f3066b --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/include/.gitignore @@ -0,0 +1,3 @@ +# Ignore everything except this ignore file, this directory contains build artifacts +* +!.gitignore diff --git a/sdk/cosmos/azure_data_cosmos_native/src/lib.rs b/sdk/cosmos/azure_data_cosmos_native/src/lib.rs new file mode 100644 index 0000000000..6ad18528ce --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/src/lib.rs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +use std::ffi::{c_char, CStr}; + +#[macro_use] +mod macros; + +/// cbindgen:ignore +#[no_mangle] // Necessary to prevent the compiler from stripping it when optimizing +pub static BUILD_IDENTIFIER: &CStr = c_str!(env!("BUILD_IDENTIFIER")); + +const VERSION: &CStr = c_str!(env!("CARGO_PKG_VERSION")); + +/// Returns a constant C string containing the version of the Cosmos Client library. +#[no_mangle] +pub extern "C" fn cosmosclient_version() -> *const c_char { + VERSION.as_ptr() +} diff --git a/sdk/cosmos/azure_data_cosmos_native/src/macros.rs b/sdk/cosmos/azure_data_cosmos_native/src/macros.rs new file mode 100644 index 0000000000..a5ba7d4360 --- /dev/null +++ b/sdk/cosmos/azure_data_cosmos_native/src/macros.rs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +macro_rules! c_str { + ($s:expr) => { + const { + // This does a few funky things to make sure we can stay in a const context + // Which ensures the string is generated as a c-str at compile time + const STR: &str = $s; + const BYTES: [u8; STR.len() + 1] = const { + let mut cstr_buf: [u8; STR.len() + 1] = [0; STR.len() + 1]; + let mut i = 0; + // For loops over ranges don't work in const contexts yet. + while i < STR.len() { + cstr_buf[i] = STR.as_bytes()[i]; + i += 1; + } + cstr_buf + }; + match CStr::from_bytes_with_nul(&BYTES) { + Ok(cstr) => cstr, + Err(_) => panic!("failed to convert value to C string"), + } + } + }; +}