Skip to content

Commit

Permalink
Refactor TeX distribution wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
pfoerster committed Feb 28, 2020
1 parent 402478c commit 77d5b20
Show file tree
Hide file tree
Showing 12 changed files with 754 additions and 2 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ authors = [
edition = "2018"

[workspace]
members = []
members = [
"crates/futures_boxed"
]

[dependencies]
byteorder = "1.3"
clap = "2.33"
futures = "0.3"
futures-boxed = { path = "crates/futures_boxed" }
log = "0.4"
lsp-types = "0.71"
once_cell = "1.3"
Expand All @@ -23,7 +27,7 @@ serde_json = "1.0"
serde_repr = "0.1"
stderrlog = "0.4"
tempfile = "3.1"
tokio = { version = "0.2", features = ["fs", "process", "macros"] }
tokio = { version = "0.2", features = ["fs", "macros", "process", "time"] }
tokio-util = { version = "0.2", features = ["codec"] }

[profile.release]
Expand Down
14 changes: 14 additions & 0 deletions crates/futures_boxed/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "futures-boxed"
version = "0.1.0"
authors = [
"Eric Förster <efoerster@users.noreply.github.com>",
"Patrick Förster <pfoerster@users.noreply.github.com>"]
edition = "2018"

[lib]
proc-macro = true

[dependencies]
syn = "1.0"
quote = "1.0"
67 changes: 67 additions & 0 deletions crates/futures_boxed/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#![recursion_limit = "128"]

extern crate proc_macro;

use proc_macro::{TokenStream, TokenTree};
use quote::{quote, ToTokens};
use std::iter::FromIterator;
use syn::{export::TokenStream2, *};

#[proc_macro_attribute]
pub fn boxed(_attr: TokenStream, item: TokenStream) -> TokenStream {
match parse::<ItemFn>(item.clone()) {
Ok(fn_) => boxed_fn(fn_),
Err(_) => {
let item = TokenStream::from_iter(item.into_iter().filter(|x| match x {
TokenTree::Ident(x) if x.to_string() == "async" => false,
_ => true,
}));

let method: TraitItemMethod = parse(item).unwrap();
boxed_trait_method(method)
}
}
}

fn boxed_fn(fn_: ItemFn) -> TokenStream {
let attrs = &fn_.attrs;
let vis = &fn_.vis;
let sig = boxed_fn_sig(&fn_.sig);
let block = &fn_.block;
let tokens = quote! {
#(#attrs)*
#vis #sig {
use futures::future::FutureExt;
let task = async move #block;
task.boxed()
}
};

tokens.into()
}

fn boxed_trait_method(method: TraitItemMethod) -> TokenStream {
let attrs = &method.attrs;
let sig = boxed_fn_sig(&method.sig);
let tokens = quote! {
#(#attrs)*
#sig;
};

tokens.into()
}

fn boxed_fn_sig(sig: &Signature) -> TokenStream2 {
let constness = &sig.constness;
let ident = &sig.ident;
let generics = &sig.generics;
let inputs = &sig.inputs;
let return_ty = match &sig.output {
ReturnType::Default => quote!(()),
ReturnType::Type(_, ty) => ty.into_token_stream(),
};

quote! {
#constness fn #ident #generics(#inputs) -> futures::future::BoxFuture<'_, #return_ty>
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod tex;

pub use self::tex::*;
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use clap::{app_from_crate, crate_authors, crate_description, crate_name, crate_version, Arg};
use std::error;
use stderrlog::{ColorChoice, Timestamp};
use texlab::Distribution;

#[tokio::main]
async fn main() -> Result<(), Box<dyn error::Error>> {
let _ = Distribution::detect().await;

let matches = app_from_crate!()
.author("")
.arg(
Expand Down
122 changes: 122 additions & 0 deletions src/tex/compile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use std::{error, fmt, io, process::Stdio, time::Duration};
use tempfile::{tempdir, TempDir};
use tokio::{
fs,
process::Command,
time::{timeout, Elapsed},
};

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Format {
Latex,
Pdflatex,
Xelatex,
Lualatex,
}

impl Format {
pub fn executable(self) -> &'static str {
match self {
Self::Latex => "latex",
Self::Pdflatex => "pdflatex",
Self::Xelatex => "xelatex",
Self::Lualatex => "lualatex",
}
}
}

#[derive(Debug)]
pub struct Artifacts {
pub directory: TempDir,
pub log: String,
}

#[derive(Debug)]
pub enum CompileError {
IO(io::Error),
NotInstalled,
Timeout(Elapsed),
}

impl From<io::Error> for CompileError {
fn from(why: io::Error) -> Self {
Self::IO(why)
}
}

impl From<Elapsed> for CompileError {
fn from(why: Elapsed) -> Self {
Self::Timeout(why)
}
}

impl fmt::Display for CompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IO(why) => write!(f, "{}", why),
Self::NotInstalled => write!(f, "TeX compiler not installed"),
Self::Timeout(why) => write!(f, "{}", why),
}
}
}

impl error::Error for CompileError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::IO(why) => why.source(),
Self::NotInstalled => None,
Self::Timeout(why) => why.source(),
}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct CompileParams<'a> {
pub format: Format,
pub file_name: &'a str,
pub code: &'a str,
pub timeout: Duration,
}

impl<'a> Default for CompileParams<'a> {
fn default() -> Self {
Self {
format: Format::Lualatex,
file_name: "code.tex",
code: "",
timeout: Duration::from_secs(15),
}
}
}

#[derive(Debug, Clone, Copy)]
pub struct Compiler<'a> {
pub executable: &'a str,
pub args: &'a [&'a str],
pub file_name: &'a str,
pub timeout: Duration,
}

impl<'a> Compiler<'a> {
pub async fn compile<'b>(&'a self, code: &'b str) -> Result<Artifacts, CompileError> {
let directory = tempdir()?;
let tex_file = directory.path().join(self.file_name);
fs::write(&tex_file, code).await?;

let child = Command::new(self.executable)
.args(self.args)
.current_dir(&directory)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();

timeout(self.timeout, child)
.await?
.map_err(|_| CompileError::NotInstalled)?;

let log_file = tex_file.with_extension("log");
let log_bytes = fs::read(log_file).await?;
let log = String::from_utf8_lossy(&log_bytes).into_owned();
Ok(Artifacts { directory, log })
}
}
Loading

0 comments on commit 77d5b20

Please sign in to comment.