#[macro_use]
pub mod macros;
pub mod elements;
use std::fmt::Debug;
use std::io::Write;
use std::{io, string};
pub trait TexElement: Debug {
fn boxed(self) -> Box<dyn TexElement>
where
Self: Sized + 'static,
{
Box::new(self) as Box<dyn TexElement>
}
fn render(&self) -> Result<String, string::FromUtf8Error> {
let mut buffer: Vec<u8> = Vec::new();
self.write_tex(&mut buffer)
.expect("should always be able to write to in-memory buffer");
String::from_utf8(buffer)
}
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()>;
}
pub trait IntoTexElement {
fn into_tex_element(self) -> Box<dyn TexElement>;
}
impl IntoTexElement for Box<dyn TexElement> {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
self
}
}
impl<'a> IntoTexElement for &'a str {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
self.to_owned().into_tex_element()
}
}
impl IntoTexElement for String {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
Box::new(Text::new(self))
}
}
impl IntoTexElement for () {
fn into_tex_element(self) -> Box<dyn TexElement> {
Box::new(RawTex(Vec::new()))
}
}
impl<T: TexElement + Sized + 'static> IntoTexElement for T {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
Box::new(self)
}
}
impl IntoTexElement for Vec<Box<dyn TexElement>> {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
Box::new(Group::new(self))
}
}
macro_rules! using_display {
($ty:ty) => {
impl IntoTexElement for $ty {
#[inline]
fn into_tex_element(self) -> Box<dyn TexElement> {
Box::new(Text::new(format!("{}", self)))
}
}
};
}
using_display!(u8);
using_display!(u16);
using_display!(u32);
using_display!(u64);
using_display!(u128);
using_display!(i8);
using_display!(i16);
using_display!(i32);
using_display!(i64);
using_display!(i128);
using_display!(f32);
using_display!(f64);
pub fn write_list<'a, I>(writer: &mut dyn Write, separator: &str, iter: I) -> io::Result<()>
where
I: Iterator<Item = &'a Box<dyn TexElement>> + 'a,
{
for (idx, arg) in iter.enumerate() {
if idx != 0 {
writer.write_all(separator.as_bytes())?;
}
arg.write_tex(writer)?;
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct RawTex(Vec<u8>);
impl RawTex {
#[inline]
pub fn new(raw: Vec<u8>) -> Self {
RawTex(raw)
}
}
impl TexElement for RawTex {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
writer.write_all(self.0.as_slice())
}
}
#[derive(Clone, Debug)]
pub struct Text(String);
impl Text {
#[inline]
pub fn new(raw: String) -> Self {
Text(raw)
}
}
impl TexElement for Text {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
crate::tex_escape::write_escaped(writer, &self.0)
}
}
#[derive(Debug, Default)]
pub struct OptArgs(Vec<Box<dyn TexElement>>);
impl OptArgs {
#[inline]
pub fn new(elements: Vec<Box<dyn TexElement>>) -> Self {
OptArgs(elements)
}
#[inline]
pub fn single<T: IntoTexElement>(elem: T) -> Self {
OptArgs(vec![elem.into_tex_element()])
}
}
impl TexElement for OptArgs {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
if !self.0.is_empty() {
writer.write_all(b"[")?;
write_list(writer, ",", self.0.iter())?;
writer.write_all(b"]")?;
}
Ok(())
}
}
#[derive(Debug, Default)]
pub struct Args(Vec<Box<dyn TexElement>>);
impl Args {
#[inline]
pub fn new(elements: Vec<Box<dyn TexElement>>) -> Self {
Args(elements)
}
#[inline]
pub fn single<T: IntoTexElement>(elem: T) -> Self {
Args(vec![elem.into_tex_element()])
}
}
impl TexElement for Args {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
if !self.0.is_empty() {
writer.write_all(b"{")?;
write_list(writer, "}{", self.0.iter())?;
writer.write_all(b"}")?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct MacroCall {
ident: Box<dyn TexElement>,
opt_args: OptArgs,
args: Args,
newline: bool,
}
impl MacroCall {
pub fn new<T: IntoTexElement>(ident: T, opt_args: OptArgs, args: Args) -> Self {
MacroCall {
ident: ident.into_tex_element(),
opt_args,
args,
newline: true,
}
}
pub fn new_inline<T: IntoTexElement>(ident: T, opt_args: OptArgs, args: Args) -> Self {
MacroCall {
ident: ident.into_tex_element(),
opt_args,
args,
newline: false,
}
}
}
impl TexElement for MacroCall {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
writer.write_all(br"\")?;
self.ident.write_tex(writer)?;
self.opt_args.write_tex(writer)?;
self.args.write_tex(writer)?;
if self.newline {
writer.write_all(b"\n")?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct BeginEndBlock {
ident: Box<dyn TexElement>,
opt_args: OptArgs,
args: Args,
children: Vec<Box<dyn TexElement>>,
}
impl BeginEndBlock {
pub fn new<T: IntoTexElement>(
ident: T,
opt_args: OptArgs,
args: Args,
children: Vec<Box<dyn TexElement>>,
) -> Self {
BeginEndBlock {
ident: ident.into_tex_element(),
opt_args,
args,
children,
}
}
}
impl TexElement for BeginEndBlock {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
writer.write_all(b"\\begin{")?;
self.ident.write_tex(writer)?;
writer.write_all(b"}")?;
self.opt_args.write_tex(writer)?;
self.args.write_tex(writer)?;
writer.write_all(b"\n")?;
for child in &self.children {
child.write_tex(writer)?;
}
writer.write_all(b"\n\\end{")?;
self.ident.write_tex(writer)?;
writer.write_all(b"}\n")?;
Ok(())
}
}
#[derive(Debug)]
pub struct AnonymousBlock(Vec<Box<dyn TexElement>>);
impl AnonymousBlock {
pub fn new(elems: Vec<Box<dyn TexElement>>) -> Self {
AnonymousBlock(elems)
}
}
impl TexElement for AnonymousBlock {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
writer.write_all(b"{")?;
for child in &self.0 {
child.write_tex(writer)?;
}
writer.write_all(b"}")?;
Ok(())
}
}
#[derive(Debug)]
pub struct Group(Vec<Box<dyn TexElement>>);
impl Group {
pub fn new(elems: Vec<Box<dyn TexElement>>) -> Self {
Group(elems)
}
}
impl TexElement for Group {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
for child in &self.0 {
child.write_tex(writer)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct TableRow(Vec<Box<dyn TexElement>>);
impl TableRow {
pub fn new(elems: Vec<Box<dyn TexElement>>) -> Self {
TableRow(elems)
}
}
impl TexElement for TableRow {
fn write_tex(&self, writer: &mut dyn Write) -> io::Result<()> {
write_list(writer, " & ", self.0.iter())?;
writer.write_all(b"\\\\\n")
}
}