mirror of
https://github.com/TREX-CoE/trexio.git
synced 2024-12-22 20:35:44 +01:00
Creating rust binding
This commit is contained in:
parent
537bb19db9
commit
59d14d0a21
12
rust/trexio/Cargo.toml
Normal file
12
rust/trexio/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "trexio"
|
||||
version = "2.4.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.65.1"
|
||||
|
39
rust/trexio/build.py
Executable file
39
rust/trexio/build.py
Executable file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
def main():
|
||||
err = {}
|
||||
be = {}
|
||||
with open("../../include/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 <trexio.h>\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")
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
31
rust/trexio/src/back_end.rs
Normal file
31
rust/trexio/src/back_end.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::c;
|
||||
|
||||
/// Possible back ends
|
||||
#[derive(Debug)]
|
||||
pub enum BackEnd {
|
||||
Text,
|
||||
Hdf5,
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl BackEnd {
|
||||
|
||||
/// Creation from a C value
|
||||
pub fn from(b : c::back_end_t) -> Self {
|
||||
match b {
|
||||
c::TREXIO_TEXT => Self::Text,
|
||||
c::TREXIO_HDF5 => Self::Hdf5,
|
||||
c::TREXIO_AUTO => Self::Auto,
|
||||
_ => panic!("Invalid backend"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to a C value
|
||||
pub fn to_c(self) -> c::back_end_t {
|
||||
match self {
|
||||
Self::Text => c::TREXIO_TEXT,
|
||||
Self::Hdf5 => c::TREXIO_HDF5,
|
||||
Self::Auto => c::TREXIO_AUTO,
|
||||
}
|
||||
}
|
||||
}
|
6
rust/trexio/src/c.rs
Normal file
6
rust/trexio/src/c.rs
Normal file
@ -0,0 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
139
rust/trexio/src/exit_code.rs
Normal file
139
rust/trexio/src/exit_code.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use crate::c;
|
||||
|
||||
/// Exit codes
|
||||
#[derive(Debug)]
|
||||
pub enum ExitCode {
|
||||
Failure,
|
||||
Success,
|
||||
InvalidArg1,
|
||||
InvalidArg2,
|
||||
InvalidArg3,
|
||||
InvalidArg4,
|
||||
InvalidArg5,
|
||||
End,
|
||||
ReadOnly,
|
||||
Errno,
|
||||
InvalidId,
|
||||
AllocationFailed,
|
||||
HasNot,
|
||||
InvalidNum,
|
||||
AttrAlreadyExists,
|
||||
DsetAlreadyExists,
|
||||
OpenError,
|
||||
LockError,
|
||||
UnlockError,
|
||||
FileError,
|
||||
GroupReadError,
|
||||
GroupWriteError,
|
||||
ElemReadError,
|
||||
ElemWriteError,
|
||||
UnsafeArrayDim,
|
||||
AttrMissing,
|
||||
DsetMissing,
|
||||
BackEndMissing,
|
||||
InvalidArg6,
|
||||
InvalidArg7,
|
||||
InvalidArg8,
|
||||
InvalidStrLen,
|
||||
IntSizeOverflow,
|
||||
SafeMode,
|
||||
InvalidElectronNum,
|
||||
InvalidDeterminantNum,
|
||||
InvalidState,
|
||||
VersionParsingIssue,
|
||||
PhaseChange,
|
||||
}
|
||||
|
||||
impl ExitCode {
|
||||
|
||||
/// Creation from a C value
|
||||
pub fn from(rc : c::trexio_exit_code) -> Self {
|
||||
match rc {
|
||||
c::TREXIO_FAILURE => Self::Failure,
|
||||
c::TREXIO_SUCCESS => Self::Success,
|
||||
c::TREXIO_INVALID_ARG_1 => Self::InvalidArg1,
|
||||
c::TREXIO_INVALID_ARG_2 => Self::InvalidArg2,
|
||||
c::TREXIO_INVALID_ARG_3 => Self::InvalidArg3,
|
||||
c::TREXIO_INVALID_ARG_4 => Self::InvalidArg4,
|
||||
c::TREXIO_INVALID_ARG_5 => Self::InvalidArg5,
|
||||
c::TREXIO_END => Self::End,
|
||||
c::TREXIO_READONLY => Self::ReadOnly,
|
||||
c::TREXIO_ERRNO => Self::Errno,
|
||||
c::TREXIO_INVALID_ID => Self::InvalidId,
|
||||
c::TREXIO_ALLOCATION_FAILED => Self::AllocationFailed,
|
||||
c::TREXIO_HAS_NOT => Self::HasNot,
|
||||
c::TREXIO_INVALID_NUM => Self::InvalidNum,
|
||||
c::TREXIO_ATTR_ALREADY_EXISTS => Self::AttrAlreadyExists,
|
||||
c::TREXIO_DSET_ALREADY_EXISTS => Self::DsetAlreadyExists,
|
||||
c::TREXIO_OPEN_ERROR => Self::OpenError,
|
||||
c::TREXIO_LOCK_ERROR => Self::LockError,
|
||||
c::TREXIO_UNLOCK_ERROR => Self::UnlockError,
|
||||
c::TREXIO_FILE_ERROR => Self::FileError,
|
||||
c::TREXIO_GROUP_READ_ERROR => Self::GroupReadError,
|
||||
c::TREXIO_GROUP_WRITE_ERROR => Self::GroupWriteError,
|
||||
c::TREXIO_ELEM_READ_ERROR => Self::ElemReadError,
|
||||
c::TREXIO_ELEM_WRITE_ERROR => Self::ElemWriteError,
|
||||
c::TREXIO_UNSAFE_ARRAY_DIM => Self::UnsafeArrayDim,
|
||||
c::TREXIO_ATTR_MISSING => Self::AttrMissing,
|
||||
c::TREXIO_DSET_MISSING => Self::DsetMissing,
|
||||
c::TREXIO_BACK_END_MISSING => Self::BackEndMissing,
|
||||
c::TREXIO_INVALID_ARG_6 => Self::InvalidArg6,
|
||||
c::TREXIO_INVALID_ARG_7 => Self::InvalidArg7,
|
||||
c::TREXIO_INVALID_ARG_8 => Self::InvalidArg8,
|
||||
c::TREXIO_INVALID_STR_LEN => Self::InvalidStrLen,
|
||||
c::TREXIO_INT_SIZE_OVERFLOW => Self::IntSizeOverflow,
|
||||
c::TREXIO_SAFE_MODE => Self::SafeMode,
|
||||
c::TREXIO_INVALID_ELECTRON_NUM => Self::InvalidElectronNum,
|
||||
c::TREXIO_INVALID_DETERMINANT_NUM => Self::InvalidDeterminantNum,
|
||||
c::TREXIO_INVALID_STATE => Self::InvalidState,
|
||||
c::TREXIO_VERSION_PARSING_ISSUE => Self::VersionParsingIssue,
|
||||
c::TREXIO_PHASE_CHANGE => Self::PhaseChange,
|
||||
_ => panic!("Unknown exit code"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to a C value
|
||||
pub fn to_c(self) -> c::trexio_exit_code {
|
||||
match self {
|
||||
Self::Failure => c::TREXIO_FAILURE,
|
||||
Self::Success => c::TREXIO_SUCCESS,
|
||||
Self::InvalidArg1 => c::TREXIO_INVALID_ARG_1,
|
||||
Self::InvalidArg2 => c::TREXIO_INVALID_ARG_2,
|
||||
Self::InvalidArg3 => c::TREXIO_INVALID_ARG_3,
|
||||
Self::InvalidArg4 => c::TREXIO_INVALID_ARG_4,
|
||||
Self::InvalidArg5 => c::TREXIO_INVALID_ARG_5,
|
||||
Self::End => c::TREXIO_END,
|
||||
Self::ReadOnly => c::TREXIO_READONLY,
|
||||
Self::Errno => c::TREXIO_ERRNO,
|
||||
Self::InvalidId => c::TREXIO_INVALID_ID,
|
||||
Self::AllocationFailed => c::TREXIO_ALLOCATION_FAILED,
|
||||
Self::HasNot => c::TREXIO_HAS_NOT,
|
||||
Self::InvalidNum => c::TREXIO_INVALID_NUM,
|
||||
Self::AttrAlreadyExists => c::TREXIO_ATTR_ALREADY_EXISTS,
|
||||
Self::DsetAlreadyExists => c::TREXIO_DSET_ALREADY_EXISTS,
|
||||
Self::OpenError => c::TREXIO_OPEN_ERROR,
|
||||
Self::LockError => c::TREXIO_LOCK_ERROR,
|
||||
Self::UnlockError => c::TREXIO_UNLOCK_ERROR,
|
||||
Self::FileError => c::TREXIO_FILE_ERROR,
|
||||
Self::GroupReadError => c::TREXIO_GROUP_READ_ERROR,
|
||||
Self::GroupWriteError => c::TREXIO_GROUP_WRITE_ERROR,
|
||||
Self::ElemReadError => c::TREXIO_ELEM_READ_ERROR,
|
||||
Self::ElemWriteError => c::TREXIO_ELEM_WRITE_ERROR,
|
||||
Self::UnsafeArrayDim => c::TREXIO_UNSAFE_ARRAY_DIM,
|
||||
Self::AttrMissing => c::TREXIO_ATTR_MISSING,
|
||||
Self::DsetMissing => c::TREXIO_DSET_MISSING,
|
||||
Self::BackEndMissing => c::TREXIO_BACK_END_MISSING,
|
||||
Self::InvalidArg6 => c::TREXIO_INVALID_ARG_6,
|
||||
Self::InvalidArg7 => c::TREXIO_INVALID_ARG_7,
|
||||
Self::InvalidArg8 => c::TREXIO_INVALID_ARG_8,
|
||||
Self::InvalidStrLen => c::TREXIO_INVALID_STR_LEN,
|
||||
Self::IntSizeOverflow => c::TREXIO_INT_SIZE_OVERFLOW,
|
||||
Self::SafeMode => c::TREXIO_SAFE_MODE,
|
||||
Self::InvalidElectronNum => c::TREXIO_INVALID_ELECTRON_NUM,
|
||||
Self::InvalidDeterminantNum => c::TREXIO_INVALID_DETERMINANT_NUM,
|
||||
Self::InvalidState => c::TREXIO_INVALID_STATE,
|
||||
Self::VersionParsingIssue => c::TREXIO_VERSION_PARSING_ISSUE,
|
||||
Self::PhaseChange => c::TREXIO_PHASE_CHANGE,
|
||||
}
|
||||
}
|
||||
}
|
109
rust/trexio/src/lib.rs
Normal file
109
rust/trexio/src/lib.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use ::std::os::raw::c_char;
|
||||
|
||||
/// C module generated by bindgen
|
||||
pub mod c;
|
||||
|
||||
/// Exit codes
|
||||
pub mod exit_code;
|
||||
pub use exit_code::ExitCode;
|
||||
|
||||
/// Possible backends
|
||||
pub mod back_end;
|
||||
pub use back_end::BackEnd;
|
||||
|
||||
/// Type for a TREXIO file
|
||||
pub type File = *mut c::trexio_t;
|
||||
|
||||
pub const PACKAGE_VERSION : &str = unsafe { std::str::from_utf8_unchecked(c::TREXIO_PACKAGE_VERSION) };
|
||||
|
||||
fn rc_return<T>(rc : c::trexio_exit_code, result: T) -> Result<T,ExitCode> {
|
||||
let rc = ExitCode::from(rc);
|
||||
match rc {
|
||||
ExitCode::Success => Ok(result),
|
||||
x => Err(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn file_name_to_c(file_name: &str) -> std::ffi::CString {
|
||||
std::ffi::CString::new(file_name).unwrap()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pub fn open(file_name: &str, mode: char, back_end: BackEnd) -> Result<File, ExitCode> {
|
||||
let file_name_c = file_name_to_c(file_name);
|
||||
let file_name_c = file_name_c.as_ptr() as *const c_char;
|
||||
let mode = mode as c_char;
|
||||
let back_end = back_end.to_c();
|
||||
let rc: *mut c::trexio_exit_code = &mut c::TREXIO_SUCCESS.clone();
|
||||
let result = unsafe { c::trexio_open(file_name_c, mode, back_end, rc) };
|
||||
let rc = unsafe { *rc };
|
||||
rc_return(rc, result)
|
||||
}
|
||||
|
||||
pub fn close(file: File) -> Result<(), ExitCode> {
|
||||
let rc = unsafe { c::trexio_close(file) };
|
||||
rc_return(rc, ())
|
||||
}
|
||||
|
||||
pub fn inquire(file_name: &str) -> Result<bool, ExitCode> {
|
||||
let file_name_c = file_name_to_c(file_name);
|
||||
let file_name_c = file_name_c.as_ptr() as *const c_char;
|
||||
let rc = unsafe { c::trexio_inquire(file_name_c) };
|
||||
match ExitCode::from(rc) {
|
||||
ExitCode::Failure => Ok(false),
|
||||
ExitCode::Success => Ok(true),
|
||||
x => Err(x),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn has_nucleus_num(trex_file: File) -> Result<bool, ExitCode> {
|
||||
let rc = unsafe { c::trexio_has_nucleus_num(trex_file) };
|
||||
match rc {
|
||||
c::TREXIO_SUCCESS => Ok(true),
|
||||
c::TREXIO_HAS_NOT => Ok(false),
|
||||
x => Err(ExitCode::from(x)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_nucleus_charge(trex_file: File) -> Result<bool, ExitCode> {
|
||||
let rc = unsafe { c::trexio_has_nucleus_charge(trex_file) };
|
||||
match rc {
|
||||
c::TREXIO_SUCCESS => Ok(true),
|
||||
c::TREXIO_HAS_NOT => Ok(false),
|
||||
x => Err(ExitCode::from(x)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn write_nucleus_num(trex_file: File, data: usize) -> Result<(), ExitCode> {
|
||||
let data: i64 = data.try_into().unwrap();
|
||||
let rc = unsafe { c::trexio_write_nucleus_num_64(trex_file, data) };
|
||||
rc_return(rc, ())
|
||||
}
|
||||
|
||||
pub fn write_nucleus_charge(trex_file: File, data: Vec<f64>) -> Result<(), ExitCode> {
|
||||
let size: i64 = data.len().try_into().unwrap();
|
||||
let rc = unsafe { c::trexio_write_safe_nucleus_charge_64(trex_file, data.as_ptr() as *const f64, size) };
|
||||
rc_return(rc, ())
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::mem;
|
||||
use c::*;
|
||||
|
||||
#[test]
|
||||
fn read_trexio_file() {
|
||||
println!("============================================");
|
||||
println!(" TREXIO MAJOR VERSION : {}", TREXIO_VERSION_MAJOR);
|
||||
println!("============================================");
|
||||
|
||||
}
|
||||
}
|
13
rust/trexio/src/main.rs
Normal file
13
rust/trexio/src/main.rs
Normal file
@ -0,0 +1,13 @@
|
||||
mod test;
|
||||
|
||||
fn main() {
|
||||
println!("============================================");
|
||||
println!(" TREXIO VERSION : {}", trexio::PACKAGE_VERSION );
|
||||
println!("============================================");
|
||||
|
||||
let file_name = "test_write_rust.dir";
|
||||
|
||||
let back_end = trexio::BackEnd::Text;
|
||||
test::test_write(file_name, back_end);
|
||||
|
||||
}
|
116
rust/trexio/src/test.rs
Normal file
116
rust/trexio/src/test.rs
Normal file
@ -0,0 +1,116 @@
|
||||
use trexio::back_end::BackEnd;
|
||||
|
||||
pub fn test_write(file_name: &str, back_end: BackEnd) {
|
||||
|
||||
// Prepare data to be written
|
||||
let n_buffers = 5;
|
||||
let buf_size_sparse = 100/n_buffers;
|
||||
let buf_size_det = 50/n_buffers;
|
||||
let mut value_sparse_ao_2e_int_eri = vec![0.0f64 ; 100];
|
||||
let mut index_sparse_ao_2e_int_eri = vec![0i32 ; 400];
|
||||
for i in 0..100 {
|
||||
let i : usize = i;
|
||||
let j : i32 = i.try_into().unwrap();
|
||||
let fj : f64 = j.try_into().unwrap();
|
||||
value_sparse_ao_2e_int_eri[i] = 3.14 + fj;
|
||||
index_sparse_ao_2e_int_eri[4*i + 0] = 4*j - 3;
|
||||
index_sparse_ao_2e_int_eri[4*i + 1] = 4*j+1 - 3;
|
||||
index_sparse_ao_2e_int_eri[4*i + 2] = 4*j+2 - 3;
|
||||
index_sparse_ao_2e_int_eri[4*i + 3] = 4*j+3 - 3;
|
||||
}
|
||||
|
||||
let nucleus_num = 12;
|
||||
let state_id = 2;
|
||||
let charge = vec![6., 6., 6., 6., 6., 6., 1., 1., 1., 1., 1., 1.0f64];
|
||||
let coord = vec![ 0.00000000, 1.39250319 , 0.00 ,
|
||||
-1.20594314, 0.69625160 , 0.00 ,
|
||||
-1.20594314, -0.69625160 , 0.00 ,
|
||||
0.00000000, -1.39250319 , 0.00 ,
|
||||
1.20594314, -0.69625160 , 0.00 ,
|
||||
1.20594314, 0.69625160 , 0.00 ,
|
||||
-2.14171677, 1.23652075 , 0.00 ,
|
||||
-2.14171677, -1.23652075 , 0.00 ,
|
||||
0.00000000, -2.47304151 , 0.00 ,
|
||||
2.14171677, -1.23652075 , 0.00 ,
|
||||
2.14171677, 1.23652075 , 0.00 ,
|
||||
0.00000000, 2.47304151 , 0.00 ];
|
||||
let mo_num = 150;
|
||||
let ao_num = 1000;
|
||||
let basis_shell_num = 24;
|
||||
let basis_nucleus_index = vec!([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 ]);
|
||||
|
||||
let label = vec![
|
||||
"C ",
|
||||
"Na ",
|
||||
"C ",
|
||||
"C 66 ",
|
||||
"C ",
|
||||
"C ",
|
||||
"H 99 ",
|
||||
"Ru ",
|
||||
"H ",
|
||||
"H ",
|
||||
"H ",
|
||||
"H " ];
|
||||
let mut label = label.concat();
|
||||
let sym_str = b"B3U with some comments";
|
||||
|
||||
|
||||
println!("{}", file_name);
|
||||
assert!( ! trexio::inquire(file_name).unwrap() );
|
||||
|
||||
let trex_file = trexio::open(file_name, 'w', back_end).unwrap();
|
||||
|
||||
assert( ! trexio::has_nucleus_num(trex_file).unwrap() );
|
||||
assert( ! trexio::has_nucleus_charge(trex_file).unwrap() );
|
||||
|
||||
/*
|
||||
let rc = unsafe { trexio_has_ao_2e_int_eri(trex_file) };
|
||||
assert!(rc == TREXIO_HAS_NOT);
|
||||
|
||||
let rc = unsafe { trexio_has_determinant_list(trex_file) };
|
||||
assert!(rc == TREXIO_HAS_NOT);
|
||||
|
||||
let rc = unsafe { trexio_has_nucleus(trex_file) };
|
||||
assert!(rc == TREXIO_HAS_NOT);
|
||||
|
||||
let rc = unsafe { trexio_has_ao_2e_int(trex_file) };
|
||||
assert!(rc == TREXIO_HAS_NOT);
|
||||
*/
|
||||
|
||||
trexio::write_nucleus_num(trex_file, nucleus_num).unwrap();
|
||||
trexio::write_nucleus_charge(trex_file, charge).unwrap();
|
||||
|
||||
/*
|
||||
let rc = unsafe { trexio_write_nucleus_coord(trex_file, coord.as_ptr() as *const f64) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
let rc = unsafe { trexio_write_nucleus_label(trex_file, label.as_ptr(), label[0].len().try_into().unwrap()) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
let rc = unsafe { trexio_write_nucleus_point_group(trex_file, sym_str.as_ptr() as *const i8, sym_str.len().try_into().unwrap()) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
let rc = unsafe { trexio_write_basis_shell_num(trex_file, basis_shell_num) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
let rc = unsafe { trexio_write_basis_nucleus_index(trex_file, basis_nucleus_index.as_ptr() as *const i32) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
let rc = unsafe { trexio_write_state_id(trex_file, state_id) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
|
||||
if (unsafe { trexio_has_ao_num(trex_file) } == TREXIO_HAS_NOT) {
|
||||
let rc = unsafe { trexio_write_ao_num(trex_file, ao_num) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
}
|
||||
|
||||
if (unsafe { trexio_has_mo_num(trex_file) } == TREXIO_HAS_NOT) {
|
||||
let rc = unsafe { trexio_write_mo_num(trex_file, mo_num) };
|
||||
assert!(rc == TREXIO_SUCCESS);
|
||||
}
|
||||
*/
|
||||
|
||||
trexio::close(trex_file).expect("Unable to close File");
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user