From fcedff429c2f9f0f64b37fd85346ca532e8b8d4c Mon Sep 17 00:00:00 2001 From: Anthony Scemama Date: Wed, 18 Oct 2023 18:09:00 +0200 Subject: [PATCH] Moving code generation in build.rs --- README.md | 2 +- rust/trexio/Cargo.toml | 11 +- rust/trexio/README.md | 42 +++++ rust/trexio/build.py | 297 ++++--------------------------- rust/trexio/build.rs | 389 ++++++++++++++++++++++++++++++++++++++++- rust/trexio/src/lib.rs | 1 + 6 files changed, 476 insertions(+), 266 deletions(-) create mode 100644 rust/trexio/README.md diff --git a/README.md b/README.md index 5e16e3b..70a2e7c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![build](https://github.com/TREX-CoE/trexio/actions/workflows/actions.yml/badge.svg)](https://github.com/TREX-CoE/trexio/actions/workflows/actions.yml) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/TREX-CoE/trexio) -TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements, making it an important tool for researchers in the field of quantum chemistry. The library consists of a front-end implemented in the C programming language and two different back-ends: a text back-end and a binary back-end utilizing the HDF5 library which enables fast read and write operations. It is compatible with a variety of platforms and has interfaces for the Fortran, Python, OCaml and Rust programming languages. +TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements. The library consists of a front-end implemented in the C programming language and two different back-ends: a text back-end and a binary back-end utilizing the HDF5 library which enables fast read and write operations. It is compatible with a variety of platforms and has interfaces for the Fortran, Python, OCaml and Rust programming languages. ## Minimal requirements (for users): diff --git a/rust/trexio/Cargo.toml b/rust/trexio/Cargo.toml index 758c86f..6f31333 100644 --- a/rust/trexio/Cargo.toml +++ b/rust/trexio/Cargo.toml @@ -3,14 +3,21 @@ name = "trexio" version = "2.4.0" edition = "2021" license = "BSD-3-Clause" +authors = ["Anthony Scemama ", "Evgeny Posenitskiy"] description = "TREXIO is an open-source file format and library developed for the storage and manipulation of data produced by quantum chemistry calculations. It is designed with the goal of providing a reliable and efficient method of storing and exchanging wave function parameters and matrix elements." +repository = "https://github.com/trex-coe/trexio" +keywords = ["quantum chemistry"] +readme = "README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] - [build-dependencies] bindgen = "0.65.1" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } [lib] doctest = false + +[dependencies] + diff --git a/rust/trexio/README.md b/rust/trexio/README.md new file mode 100644 index 0000000..0774ebe --- /dev/null +++ b/rust/trexio/README.md @@ -0,0 +1,42 @@ +# TREXIO + + +TREXIO is an open-source file format and library developed for the storage and +manipulation of data produced by quantum chemistry calculations. It is designed +with the goal of providing a reliable and efficient method of storing and +exchanging wave function parameters and matrix elements. + +This crate is the Rust binding for the TREXIO C library: +![GitHub release (latest by date)](https://img.shields.io/github/v/release/TREX-CoE/trexio) + + +## Documentation + +[TREXIO Documentation.](https://trex-coe.github.io/trexio/) + + +## Citation + +The journal article reference describing TREXIO can be cited as follows: + +``` +@article{10.1063/5.0148161, + author = {Posenitskiy, Evgeny and Chilkuri, Vijay Gopal and Ammar, Abdallah and Hapka, Michał and Pernal, Katarzyna and Shinde, Ravindra and Landinez Borda, Edgar Josué and Filippi, Claudia and Nakano, Kosuke and Kohulák, Otto and Sorella, Sandro and de Oliveira Castro, Pablo and Jalby, William and Ríos, Pablo López and Alavi, Ali and Scemama, Anthony}, + title = "{TREXIO: A file format and library for quantum chemistry}", + journal = {The Journal of Chemical Physics}, + volume = {158}, + number = {17}, + year = {2023}, + month = {05}, + issn = {0021-9606}, + doi = {10.1063/5.0148161}, + url = {https://doi.org/10.1063/5.0148161}, + note = {174801}, + eprint = {https://pubs.aip.org/aip/jcp/article-pdf/doi/10.1063/5.0148161/17355866/174801\_1\_5.0148161.pdf}, +} +``` + +Journal paper: [![doi](https://img.shields.io/badge/doi-10.1063/5.0148161-5077AB.svg)](https://doi.org/10.1063/5.0148161) + +ArXiv paper: [![arXiv](https://img.shields.io/badge/arXiv-2302.14793-b31b1b.svg)](https://arxiv.org/abs/2302.14793) + diff --git a/rust/trexio/build.py b/rust/trexio/build.py index f3082fd..ace960d 100755 --- a/rust/trexio/build.py +++ b/rust/trexio/build.py @@ -2,275 +2,38 @@ import json json_file = "../../trex.json" -trexio_h = "../../include/trexio.h" -wrapper_h = "wrapper.h" -generated_rs = "src/generated.rs" +generated_rs = "src/generated_py.rs" -def check_version(): - with open('Cargo.toml','r') as f: - for line in f: - if line.startswith("version"): - rust_version = line.split('=')[1].strip()[1:-1] - break - with open('../../configure.ac','r') as f: - for line in f: - if line.startswith("AC_INIT"): - trexio_version = line.split(',')[1].strip()[1:-1] - break - if rust_version != trexio_version: - print(f"Inconsistent versions:\nTREXIO:{trexio_version}\nRust: {rust_version}\n") - raise +convert_r = { "int": "i64", + "int special" : "usize", + "float" : "f64", + "float sparse" : "f64", + "float buffered" : "f64", + "dim" : "usize", + "dim readonly" : "usize", + "index" : "usize", + "str" : "str"} +convert_c = { "int": "i64", + "int special" : "i64", + "float" : "f64", + "float sparse" : "f64", + "float buffered" : "f64", + "dim" : "i64", + "dim readonly" : "i64", + "index" : "i64", + "str" : "str"} - -def make_interface(): - err = {} - be = {} - with open(trexio_h, 'r') as f: - for line in f: - buf = line.lstrip() - - # Get exit codes - if buf.startswith("#define TREXIO_") and "(trexio_exit_code)" in buf : - buf2 = buf.replace(")","").replace("(","").split() - err[buf2[1]] = int(buf2[3].strip()) - - # Get backends - if buf.startswith("#define TREXIO_") and "(back_end_t)" in buf : - buf2 = buf.replace(")","").replace("(","").split() - be[buf2[1]] = int(buf2[3].strip()) - - with open(wrapper_h,'w') as f: - f.write("#include \n") - - # Write exit codes - for k, v in err.items(): - f.write(f"#undef {k}\n") - f.write(f"const trexio_exit_code {k} = {v};\n") - - # Write backends - for k, v in be.items(): - f.write(f"#undef {k}\n") - f.write(f"const back_end_t {k} = {v};\n") - - f.write(f"#undef TREXIO_AUTO;\n") - f.write(f"const back_end_t TREXIO_AUTO = TREXIO_INVALID_BACK_END;\n") - - - -def make_functions(): - with open(json_file,'r') as f: - data = json.load(f) - - convert_r = { "int": "i64", - "int special" : "usize", - "float" : "f64", - "float sparse" : "f64", - "float buffered" : "f64", - "dim" : "usize", - "dim readonly" : "usize", - "index" : "usize", - "str" : "str"} - - convert_c = { "int": "i64", - "int special" : "i64", - "float" : "f64", - "float sparse" : "f64", - "float buffered" : "f64", - "dim" : "i64", - "dim readonly" : "i64", - "index" : "i64", - "str" : "str"} - - r = [""" -use std::iter::zip; -use std::ffi::CString; - -/// This implementation block includes additional functions automatically generated from tables. -/// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). -impl File { -#![allow(clippy::unnecessary_cast)] -#![allow(clippy::useless_conversion)] -#![allow(clippy::type_complexity)] -""" ] - +def make_array_functions(data): + r = [] for group in data: group_l = group.lower() - r += [ """ -/// Checks if the group `{group}` exists in the file. -/// # Parameters -/// -/// None -/// -/// # Returns -/// -/// * `Result` - Returns `Ok(true)` if the element exists in the file, -/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. -pub fn has_{group_l}(&self) -> Result { - let rc = unsafe { c::trexio_has_{group}(self.ptr) }; - match rc { - c::TREXIO_SUCCESS => Ok(true), - c::TREXIO_HAS_NOT => Ok(false), - x => Err(ExitCode::from(x)), - } -} -""" -.replace("{group}",group) -.replace("{group_l}",group_l) ] for element in data[group]: type_c = convert_c[data[group][element][0]] type_r = convert_r[data[group][element][0]] element_l = element.lower() - r += [ """ -/// Checks if the element `{element}` of the group `{group}` exists in the file. -/// -/// # Parameters -/// -/// None -/// -/// # Returns -/// -/// * `Result` - Returns `Ok(true)` if the element exists in the file, -/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. -pub fn has_{group_l}_{element_l}(&self) -> Result { - let rc = unsafe { c::trexio_has_{group}_{element}(self.ptr) }; - match rc { - c::TREXIO_SUCCESS => Ok(true), - c::TREXIO_HAS_NOT => Ok(false), - x => Err(ExitCode::from(x)), - } -} -""" -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - # Scalar elements - if data[group][element][1] == []: - if data[group][element][0] in [ "int", "float", "dim", "index" ]: - r += [ """ -/// Reads the scalar element `{element}` from the group `{group}` in the file. -/// -/// # Parameters -/// -/// None -/// -/// # Returns -/// -/// * `Result<{type_r}, ExitCode>` - Returns the scalar element as a `{type_r}` upon successful -/// operation. If the operation fails, it returns `Err(ExitCode)`. -pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> { - let mut data_c: {type_c} = 0{type_c}; - let (rc, data) = unsafe { - let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); - (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) - }; - rc_return(data, rc) -} - -/// Writes the scalar element `{element}` into the group `{group}` in the file. -/// -/// # Parameters -/// -/// * `data: {type_r}` - A `{type_r}` scalar element that will be written into `{element}` in the group `{group}`. -/// -/// # Returns -/// -/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation, otherwise returns `Err(ExitCode)`. -pub fn write_{group_l}_{element_l}(&self, data: {type_r}) -> Result<(), ExitCode> { - let data: {type_c} = data.try_into().expect("try_into failed in write_{group_l}_{element_l}"); - let rc = unsafe { c::trexio_write_{group}_{element}_64(self.ptr, data) }; - rc_return((), rc) -} -""" -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - elif data[group][element][0] in [ "str" ]: - r += [ """ -/// Reads the string attribute `{element}` contained in the group `{group}`. -/// # Parameters -/// -/// * `capacity: usize` - The maximum buffer size allocated for the string to be read. -/// -/// # Returns -/// -/// * `Result` - Returns the attribute as a `String` upon successful operation. -/// If the operation fails, it returns `Err(ExitCode)`. -pub fn read_{group_l}_{element_l}(&self, capacity: usize) -> Result { - let data_c = CString::new(vec![ b' ' ; capacity]).expect("CString::new failed"); - let (rc, data) = unsafe { - let data_c = data_c.into_raw() as *mut c_char; - let rc = c::trexio_read_{group}_{element}(self.ptr, data_c, capacity.try_into().expect("try_into failed in read_{group_l}_{element_l}")); - (rc, CString::from_raw(data_c)) - }; - let result : String = CString::into_string(data).expect("into_string failed in read_{group_l}_{element_l}"); - rc_return(result, rc) -} - - -/// Writes the string attribute `{element}` into the group `{group}`. -/// -/// # Parameters -/// -/// * `data: &str` - The string attribute that will be written into the `{element}` field in the `{group}` group. -/// -/// # Returns -/// -/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation. -/// If the operation fails, it returns `Err(ExitCode)`. -pub fn write_{group_l}_{element_l}(&self, data: &str) -> Result<(), ExitCode> { - let size : i32 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); - let data = string_to_c(data); - let data = data.as_ptr() as *const c_char; - let rc = unsafe { c::trexio_write_{group}_{element}(self.ptr, data, size) }; - rc_return((), rc) -} -""" -.replace("{type_c}",type_c) -.replace("{type_r}",type_r) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - - elif data[group][element][0] in [ "dim readonly" ]: - r += [ """ -/// Reads the dimensioning variable `{element}` from the group `{group}`. -/// -/// # Parameters -/// -/// None. -/// -/// # Returns -/// -/// * `Result<{type_r}, ExitCode>` - Returns the dimensioning variable `{element}` of type `{type_r}` -/// upon successful operation. If the operation fails, it returns `Err(ExitCode)`. -pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> { - let mut data_c: {type_c} = 0{type_c}; - let (rc, data) = unsafe { - let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); - (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) - }; - rc_return(data, rc) -} -""" -.replace("{type_r}",type_r) -.replace("{type_c}",type_c) -.replace("{group}",group) -.replace("{group_l}",group_l) -.replace("{element}",element) -.replace("{element_l}",element_l) ] - - # Array elements - else: + if data[group][element][1] != []: if data[group][element][0] in [ "int", "float", "dim", "index" ]: t = [ f""" /// Reads the `{element}` array from the group `{group}` in the file. @@ -586,11 +349,25 @@ f"\n val.push(d.{size});" + .replace("{group_l}",group_l) .replace("{element}",element) .replace("{element_l}",element_l) ] + return r +def make_functions(): + with open(json_file,'r') as f: + data = json.load(f) + r = [""" +use std::iter::zip; +/// This implementation block includes additional functions automatically generated from tables. +/// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). +impl File { +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::useless_conversion)] +#![allow(clippy::type_complexity)] +""" ] + r += make_array_functions(data) r += [ "}" ] with open(generated_rs,'w') as f: @@ -599,6 +376,4 @@ f"\n val.push(d.{size});" + if __name__ == '__main__': - check_version() - make_interface() make_functions() diff --git a/rust/trexio/build.rs b/rust/trexio/build.rs index 52e604c..569102d 100644 --- a/rust/trexio/build.rs +++ b/rust/trexio/build.rs @@ -1,16 +1,401 @@ +const TREXIO_H: &str = "../../include/trexio.h"; +const WRAPPER_H: &str = "wrapper.h"; +const JSON_FILE: &str = "../../trex.json"; +const GENERATED_RS: &str = "src/generated.rs"; + use std::env; use std::path::PathBuf; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, BufRead, BufReader, Write}; +use serde_json::Value; +use std::path::Path; + + + + +/// Checks that the version specified in the Cargo.toml file is consistent with the version of the TREXIO C library. +fn check_version() -> Result<(), String> { + // Read version from Cargo.toml + let mut rust_version = String::new(); + { + let file = File::open("Cargo.toml").map_err(|e| e.to_string())?; + let reader = io::BufReader::new(file); + + for line in reader.lines() { + let line = line.map_err(|e| e.to_string())?; + if line.starts_with("version") { + rust_version = line.split('=').nth(1).unwrap().trim().to_string(); + rust_version = rust_version[1..rust_version.len() - 1].to_string(); + break; + } + } + } + + // Read version from ../../configure.ac + let mut trexio_version = String::new(); + { + let file = File::open("../../configure.ac").map_err(|e| e.to_string())?; + let reader = io::BufReader::new(file); + + for line in reader.lines() { + let line = line.map_err(|e| e.to_string())?; + if line.starts_with("AC_INIT") { + trexio_version = line.split(',').nth(1).unwrap().trim().to_string(); + trexio_version = trexio_version[1..trexio_version.len() - 1].to_string(); + break; + } + } + } + + // Compare versions + if rust_version != trexio_version { + eprintln!("Inconsistent versions:\nTREXIO: {}\nRust: {}\n", trexio_version, rust_version); + return Err("Inconsistent versions".to_string()); + } + + Ok(()) +} + + + +/// This function reads from `trexio.h`, extracts the exit codes and backends, and writes them to `wrapper.h`. +fn make_interface() -> io::Result<()> { + let mut err = HashMap::new(); + let mut be = HashMap::new(); + + let trexio_file = File::open(TREXIO_H)?; + let trexio_reader = BufReader::new(trexio_file); + + for line in trexio_reader.lines() { + let line = line?; + let buf = line.trim_start(); + + if buf.starts_with("#define TREXIO_") && buf.contains("(trexio_exit_code)") { + let buf2 = buf.replace(")", ""); + let buf2 = buf2.replace("(", ""); + let buf2: Vec<&str> = buf2.split_whitespace().collect(); + err.insert(buf2[1].to_string(), buf2[3].trim().parse::().unwrap()); + } + + if buf.starts_with("#define TREXIO_") && buf.contains("(back_end_t)") { + let buf2 = buf.replace(")", ""); + let buf2 = buf2.replace("(", ""); + let buf2: Vec<&str> = buf2.split_whitespace().collect(); + be.insert(buf2[1].to_string(), buf2[3].trim().parse::().unwrap()); + } + } + + let mut wrapper_file = File::create(WRAPPER_H)?; + write!(&mut wrapper_file, "#include \n")?; + + for (k, v) in &err { + write!(&mut wrapper_file, "#undef {}\n", k)?; + write!(&mut wrapper_file, "const trexio_exit_code {} = {};\n", k, v)?; + } + + for (k, v) in &be { + write!(&mut wrapper_file, "#undef {}\n", k)?; + write!(&mut wrapper_file, "const back_end_t {} = {};\n", k, v)?; + } + + write!(&mut wrapper_file, "#undef TREXIO_AUTO;\n")?; + write!(&mut wrapper_file, "const back_end_t TREXIO_AUTO = TREXIO_INVALID_BACK_END;\n")?; + + Ok(()) +} + + +/// Dictionnary of type conversions +fn convert_r(typ: &str) -> String { + match typ { + "int" => "i64", + "int special" => "usize", + "float" | "float sparse" | "float buffered" => "f64", + "dim" | "dim readonly" | "index" => "usize", + "str" => "str", + _ => panic!("Unknown type to convert: {}", typ) + }.to_string() +} + +fn convert_c(typ: &str) -> String { + match typ { + "int" | "int special" | "dim" | "dim readonly" | "index" => "i64", + "float" | "float sparse" | "float buffered" => "f64", + "str" => "str", + _ => panic!("Unknown type to convert: {}", typ) + }.to_string() +} + + +/// Generate has-functions for checking the existence of groups and elements in a TREXIO file. +/// +/// # Parameters +/// * `data` - The JSON-like data containing the groups and elements. +/// +/// # Returns +/// A `Vec` containing the generated Rust code as strings. +fn make_has_functions(data: &Value) -> Vec { + let mut r = Vec::new(); + + if let Value::Object(groups) = data { + for (group, elements) in groups.iter() { + let group_l = group.to_lowercase(); + + let has_group_func = format!( + "/// Checks if the group `{group}` exists in the file. +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result` - Returns `Ok(true)` if the element exists in the file, +/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. +pub fn has_{group_l}(&self) -> Result {{ + let rc = unsafe {{ c::trexio_has_{group}(self.ptr) }}; + match rc {{ + c::TREXIO_SUCCESS => Ok(true), + c::TREXIO_HAS_NOT => Ok(false), + x => Err(ExitCode::from(x)), + }} +}}", + group = group, + group_l = group_l + ); + + r.push(has_group_func); + + if let Value::Object(elements_map) = elements { + for (element, _types_value) in elements_map.iter() { + let element_l = element.to_lowercase(); + + let has_element_func = format!( + "/// Checks if the element `{element}` of the group `{group}` exists in the file. +/// +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result` - Returns `Ok(true)` if the element exists in the file, +/// otherwise returns `Ok(false)`. An error during the execution results in `Err(ExitCode)`. +pub fn has_{group_l}_{element_l}(&self) -> Result {{ + let rc = unsafe {{ c::trexio_has_{group}_{element}(self.ptr) }}; + match rc {{ + c::TREXIO_SUCCESS => Ok(true), + c::TREXIO_HAS_NOT => Ok(false), + x => Err(ExitCode::from(x)), + }} +}}", + group = group, + group_l = group_l, + element = element, + element_l = element_l + ); + + r.push(has_element_func); + } + } + } + } + + r +} + + + + +fn make_scalar_functions(data: &serde_json::Value) -> Vec { + let mut r: Vec = Vec::new(); + + for group in data.as_object().unwrap().keys() { + let group_l = group.to_lowercase(); + + for (element, attributes) in data[group].as_object().unwrap() { + let typ = attributes[0].as_str().unwrap(); + let type_c = convert_c(typ); + let type_r = convert_r(typ); + let element_l = element.to_lowercase(); + + if attributes[1].as_array().unwrap().is_empty() { + match typ { + "int" | "float" | "dim" | "index" => { + let s = format!(r#" +/// Reads the scalar element `{element}` from the group `{group}` in the file. +/// +/// # Parameters +/// +/// None +/// +/// # Returns +/// +/// * `Result<{type_r}, ExitCode>` - Returns the scalar element as a `{type_r}` upon successful +/// operation. If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ + let mut data_c: {type_c} = 0{type_c}; + let (rc, data) = unsafe {{ + let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); + (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) + }}; + rc_return(data, rc) +}} + +/// Writes the scalar element `{element}` into the group `{group}` in the file. +/// +/// # Parameters +/// +/// * `data: {type_r}` - A `{type_r}` scalar element that will be written into `{element}` in the group `{group}`. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation, otherwise returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: {type_r}) -> Result<(), ExitCode> {{ + let data: {type_c} = data.try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let rc = unsafe {{ c::trexio_write_{group}_{element}_64(self.ptr, data) }}; + rc_return((), rc) +}} +"#, + type_c=type_c, type_r=type_r, group=group, group_l=group_l, element=element, element_l=element_l); + r.push(s); + }, + "str" => { + let s = format!(r#" +/// Reads the string attribute `{element}` contained in the group `{group}`. +/// # Parameters +/// +/// * `capacity: usize` - The maximum buffer size allocated for the string to be read. +/// +/// # Returns +/// +/// * `Result` - Returns the attribute as a `String` upon successful operation. +/// If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self, capacity: usize) -> Result {{ + let data_c = CString::new(vec![ b' ' ; capacity]).expect("CString::new failed"); + let (rc, data) = unsafe {{ + let data_c = data_c.into_raw() as *mut c_char; + let rc = c::trexio_read_{group}_{element}(self.ptr, data_c, capacity.try_into().expect("try_into failed in read_{group_l}_{element_l}")); + (rc, CString::from_raw(data_c)) + }}; + let result : String = CString::into_string(data).expect("into_string failed in read_{group_l}_{element_l}"); + rc_return(result, rc) +}} + + +/// Writes the string attribute `{element}` into the group `{group}`. +/// +/// # Parameters +/// +/// * `data: &str` - The string attribute that will be written into the `{element}` field in the `{group}` group. +/// +/// # Returns +/// +/// * `Result<(), ExitCode>` - Returns `Ok(())` upon successful operation. +/// If the operation fails, it returns `Err(ExitCode)`. +pub fn write_{group_l}_{element_l}(&self, data: &str) -> Result<(), ExitCode> {{ + let size : i32 = data.len().try_into().expect("try_into failed in write_{group_l}_{element_l}"); + let data = string_to_c(data); + let data = data.as_ptr() as *const c_char; + let rc = unsafe {{ c::trexio_write_{group}_{element}(self.ptr, data, size) }}; + rc_return((), rc) +}} +"#, + group=group, group_l=group_l, element=element, element_l=element_l); + r.push(s); + }, + "dim readonly" => { + let s = format!(r#" +/// Reads the dimensioning variable `{element}` from the group `{group}`. +/// +/// # Parameters +/// +/// None. +/// +/// # Returns +/// +/// * `Result<{type_r}, ExitCode>` - Returns the dimensioning variable `{element}` of type `{type_r}` +/// upon successful operation. If the operation fails, it returns `Err(ExitCode)`. +pub fn read_{group_l}_{element_l}(&self) -> Result<{type_r}, ExitCode> {{ + let mut data_c: {type_c} = 0{type_c}; + let (rc, data) = unsafe {{ + let rc = c::trexio_read_{group}_{element}_64(self.ptr, &mut data_c); + (rc, data_c.try_into().expect("try_into failed in read_{group_l}_{element_l}")) + }}; + rc_return(data, rc) +}} +"#, + type_c=type_c, type_r=type_r, group=group, group_l=group_l, element=element, element_l=element_l); + r.push(s); + }, + _ => {} + } + } + } + } + r +} + + + + + + + + + + + + +/// Reads the JSON file, processes its contents, and generates Rust functions according to the specifications in the JSON data. +fn make_functions() -> std::io::Result<()> { + let json_file = Path::new(JSON_FILE); + let generated_rs = Path::new(GENERATED_RS); + + let data: Value = serde_json::from_reader(File::open(&json_file)?)?; + + let mut r: Vec = vec![ + String::from(" +use std::ffi::CString; + +/// This implementation block includes additional functions automatically generated from tables. +/// For more details, refer to [TREXIO tables documentation](https://trex-coe.github.io/trexio/trex.html). +impl File { + #![allow(clippy::unnecessary_cast)] + #![allow(clippy::useless_conversion)] + #![allow(clippy::type_complexity)] +"), + ]; + + r.append(&mut make_has_functions(&data)); + r.append(&mut make_scalar_functions(&data)); +// r.append(&mut make_array_functions(&data)); + + r.push(String::from("}")); + + let mut f = File::create(&generated_rs)?; + f.write_all(r.join("\n").as_bytes())?; + Ok(()) +} + + + + fn main() { + check_version().unwrap(); + make_interface().unwrap(); + make_functions().unwrap(); + // Tell cargo to look for shared libraries in the specified directory println!("cargo:rustc-link-search=/usr/local/lib"); - // Tell cargo to tell rustc to link the system bzip2 - // shared library. + // Tell cargo to tell rustc to link the system trexio shared library. println!("cargo:rustc-link-lib=trexio"); // Tell cargo to invalidate the built crate whenever the wrapper changes println!("cargo:rerun-if-changed=wrapper.h"); + println!("cargo:rerun-if-changed=build.py"); // The bindgen::Builder is the main entry point // to bindgen, and lets you build up options for diff --git a/rust/trexio/src/lib.rs b/rust/trexio/src/lib.rs index 69bf73d..71bbee3 100644 --- a/rust/trexio/src/lib.rs +++ b/rust/trexio/src/lib.rs @@ -258,3 +258,4 @@ impl File { } include!("generated.rs"); +include!("generated_py.rs");