diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9a685e6 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,11 @@ +name: Build docs +on: [push, pull_request] + +jobs: + docs_ubuntu: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Build documentation + run: cargo doc diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4eaa44b..76960dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,8 +9,15 @@ jobs: uses: actions/checkout@v4 - name: Install libeditorconfig run: brew install editorconfig - - name: Run tests - run: cargo test + - name: Build and run tests with existing bindings + run: | + cargo build + cargo test + cargo clean + - name: Build and run tests with auto-generated bindings + run: | + cargo build --features buildtime-bindgen + cargo test --features buildtime-bindgen test_ubuntu: runs-on: ubuntu-latest steps: @@ -18,5 +25,12 @@ jobs: uses: actions/checkout@v4 - name: Install libeditorconfig run: sudo apt-get update && sudo apt-get install libeditorconfig-dev - - name: Run tests - run: cargo test + - name: Build and run tests with existing bindings + run: | + cargo build + cargo test + cargo clean + - name: Build and run tests with auto-generated bindings + run: | + cargo build --features buildtime-bindgen + cargo test --features buildtime-bindgen diff --git a/Cargo.toml b/Cargo.toml index b752b6a..dabb9a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Thorsten Blum "] homepage = "https://github.com/toblux/editorconfig-sys" repository = "https://github.com/toblux/editorconfig-sys" documentation = "https://docs.rs/editorconfig-sys" -description = "Native Rust bindings to libeditorconfig" +description = "Auto-generated Rust FFI bindings to libeditorconfig" readme = "README.md" license = "MIT" keywords = ["editorconfig", "libeditorconfig", "bindings", "ffi", "sys"] @@ -20,3 +20,6 @@ rand = "0.8.5" [build-dependencies] bindgen = "0.69.2" pkg-config = "0.3.29" + +[features] +buildtime-bindgen = [] diff --git a/README.md b/README.md index fff40d0..9614587 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Native Rust Bindings to libeditorconfig +# Rust FFI Bindings to libeditorconfig -This crate uses [bindgen](https://crates.io/crates/bindgen) and [pkg-config](https://crates.io/crates/pkg-config) to automatically generate Rust FFI bindings to the [EditorConfig Core C](https://github.com/editorconfig/editorconfig-core-c) library. +This crate uses [bindgen](https://crates.io/crates/bindgen) and [pkg-config](https://crates.io/crates/pkg-config) to automatically generate Rust FFI bindings to the [editorconfig-core](https://github.com/editorconfig/editorconfig-core-c) C library. Following the `*-sys` package convention, `editorconfig-sys` is just a thin wrapper around the native `libeditorconfig` library. diff --git a/build.rs b/build.rs index bf099ce..472b044 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use pkg_config::Library; +use pkg_config::{Config, Library}; const LIBRARY_NAME: &str = "editorconfig"; @@ -8,12 +8,19 @@ const MIN_VERSION: &str = "0.12.5"; const MAX_VERSION: &str = "1.0.0"; fn main() { - let err_msg = format!("Unable to find library {} >= {}", LIBRARY_NAME, MIN_VERSION); - let lib = pkg_config::Config::new() + if let Ok(lib) = Config::new() .range_version(MIN_VERSION..MAX_VERSION) .probe(LIBRARY_NAME) - .expect(&err_msg); - gen_bindings(lib); + { + if cfg!(feature = "buildtime-bindgen") { + gen_bindings(lib); + } + } else { + eprintln!( + "Unable to find lib {} >= {} < {}", + LIBRARY_NAME, MIN_VERSION, MAX_VERSION + ); + } } fn gen_bindings(lib: Library) { @@ -29,7 +36,7 @@ fn gen_bindings(lib: Library) { .generate() .expect("Failed to generate bindings"); - // Write bindings to `$OUT_DIR/bindings.rs` + // Write auto-generated bindings to `$OUT_DIR/bindings.rs` let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); bindings .write_to_file(out_path.join("bindings.rs")) diff --git a/src/bindings.rs b/src/bindings.rs new file mode 100644 index 0000000..fef7a1a --- /dev/null +++ b/src/bindings.rs @@ -0,0 +1,92 @@ +/* automatically generated by rust-bindgen 0.69.2 */ + +pub const EDITORCONFIG_PARSE_NOT_FULL_PATH: i32 = -2; +pub const EDITORCONFIG_PARSE_MEMORY_ERROR: i32 = -3; +pub const EDITORCONFIG_PARSE_VERSION_TOO_NEW: i32 = -4; +#[doc = " @brief The editorconfig handle object type"] +pub type editorconfig_handle = *mut ::std::os::raw::c_void; +extern "C" { + #[doc = " @brief Create and intialize a default editorconfig_handle object.\n\n @retval NULL Failed to create the editorconfig_handle object.\n\n @retval non-NULL The created editorconfig_handle object is returned."] + pub fn editorconfig_handle_init() -> editorconfig_handle; +} +extern "C" { + #[doc = " @brief Destroy an editorconfig_handle object\n\n @param h The editorconfig_handle object needs to be destroyed.\n\n @retval zero The editorconfig_handle object is destroyed successfully.\n\n @retval non-zero Failed to destroy the editorconfig_handle object."] + pub fn editorconfig_handle_destroy(h: editorconfig_handle) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the err_file field of an editorconfig_handle object\n\n @param h The editorconfig_handle object whose err_file needs to be obtained.\n\n @retval NULL No error file exists.\n\n @retval non-NULL The pointer to the path of the file caused the parsing\n error is returned."] + pub fn editorconfig_handle_get_err_file( + h: editorconfig_handle, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the version fields of an editorconfig_handle object.\n\n @param h The editorconfig_handle object whose version field need to be\n obtained.\n\n @param major If not null, the integer pointed by major will be filled with\n the major version field of the editorconfig_handle object.\n\n @param minor If not null, the integer pointed by minor will be filled with\n the minor version field of the editorconfig_handle object.\n\n @param patch If not null, the integer pointed by patch will be filled\n with the patch version field of the editorconfig_handle object.\n\n @return None."] + pub fn editorconfig_handle_get_version( + h: editorconfig_handle, + major: *mut ::std::os::raw::c_int, + minor: *mut ::std::os::raw::c_int, + patch: *mut ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the version fields of an editorconfig_handle object.\n\n @param h The editorconfig_handle object whose version fields need to be set.\n\n @param major If not less than 0, the major version field will be set to\n major. If this parameter is less than 0, the major version field of the\n editorconfig_handle object will remain unchanged.\n\n @param minor If not less than 0, the minor version field will be set to\n minor. If this parameter is less than 0, the minor version field of the\n editorconfig_handle object will remain unchanged.\n\n @param patch If not less than 0, the patch version field will be set to\n patch. If this parameter is less than 0, the patch version field of the\n editorconfig_handle object will remain unchanged.\n\n @return None."] + pub fn editorconfig_handle_set_version( + h: editorconfig_handle, + major: ::std::os::raw::c_int, + minor: ::std::os::raw::c_int, + patch: ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Set the conf_file_name field of an editorconfig_handle object.\n\n @param h The editorconfig_handle object whose conf_file_name field needs to\n be set.\n\n @param conf_file_name The new value of the conf_file_name field of the\n editorconfig_handle object.\n\n @return None."] + pub fn editorconfig_handle_set_conf_file_name( + h: editorconfig_handle, + conf_file_name: *const ::std::os::raw::c_char, + ); +} +extern "C" { + #[doc = " @brief Get the conf_file_name field of an editorconfig_handle object.\n\n @param h The editorconfig_handle object whose conf_file_name field needs to\n be obtained.\n\n @return The value of the conf_file_name field of the editorconfig_handle\n object."] + pub fn editorconfig_handle_get_conf_file_name( + h: editorconfig_handle, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the nth name and value fields of an editorconfig_handle object.\n\n @param h The editorconfig_handle object whose name and value fields need to\n be obtained.\n\n @param n The zero-based index of the name and value fields to be obtained.\n\n @param name If not null, *name will be set to point to the obtained name.\n\n @param value If not null, *value will be set to point to the obtained value.\n\n @return None."] + pub fn editorconfig_handle_get_name_value( + h: editorconfig_handle, + n: ::std::os::raw::c_int, + name: *mut *const ::std::os::raw::c_char, + value: *mut *const ::std::os::raw::c_char, + ); +} +extern "C" { + #[doc = " @brief Get the count of name and value fields of an editorconfig_handle\n object.\n\n @param h The editorconfig_handle object whose count of name and value fields\n need to be obtained.\n\n @return the count of name and value fields of the editorconfig_handle\n object."] + pub fn editorconfig_handle_get_name_value_count( + h: editorconfig_handle, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Parse editorconfig files corresponding to the file path given by\n full_filename, and related information is input and output in h.\n\n An example is available at\n src/bin/main.c\n in EditorConfig C Core source code.\n\n @param full_filename The full path of a file that is edited by the editor\n for which the parsing result is.\n\n @param h The @ref editorconfig_handle to be used and returned from this\n function (including the parsing result). The @ref editorconfig_handle should\n be created by editorconfig_handle_init().\n\n @retval 0 Everything is OK.\n\n @retval \"Positive Integer\" A parsing error occurs. The return value would be\n the line number of parsing error. err_file obtained from h by calling\n editorconfig_handle_get_err_file() will also be filled with the file path\n that caused the parsing error.\n\n @retval \"Negative Integer\" Some error occured. See below for the reason of\n the error for each return value.\n\n @retval EDITORCONFIG_PARSE_NOT_FULL_PATH The full_filename is not a full\n path name.\n\n @retval EDITORCONFIG_PARSE_MEMORY_ERROR A memory error occurs.\n\n @retval EDITORCONFIG_PARSE_VERSION_TOO_NEW The required version specified in\n @ref editorconfig_handle is greater than the current version.\n"] + pub fn editorconfig_parse( + full_filename: *const ::std::os::raw::c_char, + h: editorconfig_handle, + ) -> ::std::os::raw::c_int; +} +extern "C" { + #[doc = " @brief Get the error message from the error number returned by\n editorconfig_parse().\n\n An example is available at\n src/bin/main.c\n in EditorConfig C Core source code.\n\n @param err_num The error number that is used to obtain the error message.\n\n @return The error message corresponding to err_num."] + pub fn editorconfig_get_error_msg( + err_num: ::std::os::raw::c_int, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + #[doc = " @brief Get the version number of EditorConfig.\n\n An example is available at\n src/bin/main.c\n in EditorConfig C Core source code.\n\n @param major If not null, the integer pointed by major will be filled with\n the major version of EditorConfig.\n\n @param minor If not null, the integer pointed by minor will be filled with\n the minor version of EditorConfig.\n\n @param patch If not null, the integer pointed by patch will be filled\n with the patch version of EditorConfig.\n\n @return None."] + pub fn editorconfig_get_version( + major: *mut ::std::os::raw::c_int, + minor: *mut ::std::os::raw::c_int, + patch: *mut ::std::os::raw::c_int, + ); +} +extern "C" { + #[doc = " @brief Get the version suffix.\n\n @return The version suffix, such as \"-development\" for a development\n version, empty string for a stable version."] + pub fn editorconfig_get_version_suffix() -> *const ::std::os::raw::c_char; +} diff --git a/src/lib.rs b/src/lib.rs index 0ec680f..51e52cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,10 @@ #![allow(non_camel_case_types)] +#[cfg(feature = "buildtime-bindgen")] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(not(feature = "buildtime-bindgen"))] +mod bindings; + +#[cfg(not(feature = "buildtime-bindgen"))] +pub use bindings::*;