diff options
Diffstat (limited to 'support/texlab/src/citeproc/bibutils.rs')
-rw-r--r-- | support/texlab/src/citeproc/bibutils.rs | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/support/texlab/src/citeproc/bibutils.rs b/support/texlab/src/citeproc/bibutils.rs new file mode 100644 index 0000000000..03a5c2996a --- /dev/null +++ b/support/texlab/src/citeproc/bibutils.rs @@ -0,0 +1,188 @@ +use bibutils_sys::{ + bibl, bibl_free, bibl_freeparams, bibl_init, bibl_initparams, bibl_read, bibl_write, fclose, + fopen, param, BIBL_ADSABSOUT, BIBL_BIBLATEXIN, BIBL_BIBTEXIN, BIBL_BIBTEXOUT, BIBL_COPACIN, + BIBL_EBIIN, BIBL_ENDNOTEIN, BIBL_ENDNOTEOUT, BIBL_ENDNOTEXMLIN, BIBL_ISIOUT, BIBL_MEDLINEIN, + BIBL_MODSIN, BIBL_MODSOUT, BIBL_NBIBIN, BIBL_NBIBOUT, BIBL_OK, BIBL_RISIN, BIBL_RISOUT, + BIBL_WORD2007OUT, BIBL_WORDIN, FILE, +}; +use std::{ffi::CString, fs, mem::MaybeUninit, 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() as *mut i8, + ); + } + 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: &str, 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() as *mut i8, + 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) +} |