summaryrefslogtreecommitdiff
path: root/support/texlab/crates/bibutils/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/bibutils/src/lib.rs')
-rw-r--r--support/texlab/crates/bibutils/src/lib.rs185
1 files changed, 185 insertions, 0 deletions
diff --git a/support/texlab/crates/bibutils/src/lib.rs b/support/texlab/crates/bibutils/src/lib.rs
new file mode 100644
index 0000000000..f14db68b59
--- /dev/null
+++ b/support/texlab/crates/bibutils/src/lib.rs
@@ -0,0 +1,185 @@
+use bibutils_sys::*;
+use std::ffi::CString;
+use std::fs;
+use std::mem::MaybeUninit;
+use std::path::Path;
+use tempfile::tempdir;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum InputFormat {
+ Bibtex,
+ Biblatex,
+ Copac,
+ Ebi,
+ Endnote,
+ EndnoteXml,
+ Medline,
+ Mods,
+ Nbib,
+ Ris,
+ Word,
+}
+
+impl InputFormat {
+ fn read_mode(self) -> u32 {
+ match self {
+ Self::Bibtex => BIBL_BIBTEXIN,
+ Self::Biblatex => BIBL_BIBLATEXIN,
+ Self::Copac => BIBL_COPACIN,
+ Self::Ebi => BIBL_EBIIN,
+ Self::Endnote => BIBL_ENDNOTEIN,
+ Self::EndnoteXml => BIBL_ENDNOTEXMLIN,
+ Self::Medline => BIBL_MEDLINEIN,
+ Self::Mods => BIBL_MODSIN,
+ Self::Nbib => BIBL_NBIBIN,
+ Self::Ris => BIBL_RISIN,
+ Self::Word => BIBL_WORDIN,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum OutputFormat {
+ Adsabs,
+ Bibtex,
+ Endnote,
+ Isi,
+ Mods,
+ Nbib,
+ Ris,
+ Word2007,
+}
+
+impl OutputFormat {
+ fn write_mode(self) -> u32 {
+ match self {
+ Self::Adsabs => BIBL_ADSABSOUT,
+ Self::Bibtex => BIBL_BIBTEXOUT,
+ Self::Endnote => BIBL_ENDNOTEOUT,
+ Self::Isi => BIBL_ISIOUT,
+ Self::Mods => BIBL_MODSOUT,
+ Self::Nbib => BIBL_NBIBOUT,
+ Self::Ris => BIBL_RISOUT,
+ Self::Word2007 => BIBL_WORD2007OUT,
+ }
+ }
+}
+
+struct Context {
+ inner: MaybeUninit<bibl>,
+}
+
+impl Context {
+ fn new() -> Self {
+ let mut inner = MaybeUninit::zeroed();
+ unsafe {
+ bibl_init(inner.as_mut_ptr());
+ }
+ Self { inner }
+ }
+}
+
+impl Drop for Context {
+ fn drop(&mut self) {
+ unsafe {
+ bibl_free(self.inner.as_mut_ptr());
+ }
+ }
+}
+
+unsafe impl Send for Context {}
+
+struct Params {
+ inner: MaybeUninit<param>,
+}
+
+impl Params {
+ fn new(from: InputFormat, to: OutputFormat) -> Self {
+ let program = CString::new("texlab").unwrap();
+ let mut inner = MaybeUninit::zeroed();
+ unsafe {
+ bibl_initparams(
+ inner.as_mut_ptr(),
+ from.read_mode() as i32,
+ to.write_mode() as i32,
+ program.as_ptr(),
+ );
+ }
+ Self { inner }
+ }
+}
+
+impl Drop for Params {
+ fn drop(&mut self) {
+ unsafe {
+ bibl_freeparams(self.inner.as_mut_ptr());
+ }
+ }
+}
+
+unsafe impl Send for Params {}
+
+struct File {
+ path: CString,
+ handle: *mut FILE,
+}
+
+impl File {
+ fn new<M: Into<Vec<u8>>>(path: &Path, mode: M) -> Self {
+ let path = CString::new(path.to_str().unwrap()).unwrap();
+ let mode = CString::new(mode).unwrap();
+ let handle = unsafe { fopen(path.as_ptr(), mode.as_ptr()) };
+ Self { path, handle }
+ }
+}
+
+impl Drop for File {
+ fn drop(&mut self) {
+ unsafe {
+ fclose(self.handle);
+ }
+ }
+}
+
+unsafe impl Send for File {}
+
+pub fn convert(input: String, from: InputFormat, to: OutputFormat) -> Option<String> {
+ let mut context = Context::new();
+ let mut params = Params::new(from, to);
+ let dir = tempdir().expect("failed to create a temporary directory");
+
+ let input_path = dir.path().join("input");
+ fs::write(&input_path, input).ok()?;
+ let input_file = File::new(&input_path, "r");
+ unsafe {
+ let status = bibl_read(
+ context.inner.as_mut_ptr(),
+ input_file.handle,
+ input_file.path.as_ptr(),
+ params.inner.as_mut_ptr(),
+ );
+
+ if status != BIBL_OK as i32 {
+ return None;
+ }
+ }
+
+ let output_path = dir.path().join("output");
+ let output_file = File::new(&output_path, "w");
+ unsafe {
+ let status = bibl_write(
+ context.inner.as_mut_ptr(),
+ output_file.handle,
+ params.inner.as_mut_ptr(),
+ );
+
+ if status != BIBL_OK as i32 {
+ return None;
+ }
+ }
+
+ // Remove BOM
+ let data = fs::read(&output_path).ok()?;
+ let text = String::from_utf8_lossy(&data[3..]).into_owned();
+ Some(text)
+}