pycall 0.1.0

Python Codegen for Rust
Documentation
use std::fmt::{Display, Error, Formatter};
use std::io::Write;

pub trait AsPythonLitteral {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result;
}

macro_rules! as_py_lit_impl {
    ($t: ty, $fmt_str: expr) => {
        impl AsPythonLitteral for $t {
            fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
                write!(f, $fmt_str, &self)
            }
        }
    };
}

as_py_lit_impl!(str, "\"{}\"");
as_py_lit_impl!(u8, "{}");
as_py_lit_impl!(u16, "{}");
as_py_lit_impl!(u32, "{}");
as_py_lit_impl!(u64, "{}");
as_py_lit_impl!(u128, "{}");
as_py_lit_impl!(usize, "{}");
as_py_lit_impl!(i8, "{}");
as_py_lit_impl!(i16, "{}");
as_py_lit_impl!(i32, "{}");
as_py_lit_impl!(i64, "{}");
as_py_lit_impl!(i128, "{}");
as_py_lit_impl!(isize, "{}");
as_py_lit_impl!(f32, "{:.6e}");
as_py_lit_impl!(f64, "{:.6e}");

impl<T: AsPythonLitteral> AsPythonLitteral for Vec<T> {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "[")?;
        for x in self.iter() {
            write!(f, "{},", PythonLitteral(x))?;
        }
        write!(f, "]")
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Indents(pub isize);

impl std::fmt::Display for Indents {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        for _ in 0..self.0 {
            write!(f, "\t")?;
        }
        Ok(())
    }
}

pub struct PythonProgram {
    file: tempfile::NamedTempFile,
    indents: Indents,
}

struct PythonLitteral<'l, T: AsPythonLitteral + ?Sized>(pub &'l T);
impl<'l, T: AsPythonLitteral + ?Sized> Display for PythonLitteral<'l, T> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        self.0.fmt(f)
    }
}

impl PythonProgram {
    pub fn new() -> PythonProgram {
        PythonProgram {
            file: tempfile::NamedTempFile::new().unwrap(),
            indents: Indents(0),
        }
    }

    pub fn run(&self) -> Result<std::process::Output, std::io::Error> {
        std::process::Command::new("python3")
            .arg(self.file.path())
            .output()
    }

    pub fn flush(&mut self) -> &mut Self {
        self.file.flush().unwrap();
        self
    }

    fn indent(&mut self, n: isize) -> &mut Self {
        if n >= 0 {
            self.indents.0 += n as isize
        } else {
            self.indents.0 -= n as isize
        }
        self
    }

    pub fn end_block(&mut self) -> &mut Self {
        self.indent(-1)
    }

    pub fn define_variable<T: AsPythonLitteral + ?Sized>(
        &mut self,
        name: &str,
        value: &T,
    ) -> &mut Self {
        writeln!(
            &mut self.file,
            "{}{} = {}",
            self.indents,
            name,
            PythonLitteral(value)
        )
        .unwrap();
        self
    }

    pub fn write_line(&mut self, line: &str) -> &mut Self {
        writeln!(&mut self.file, "{}{}", self.indents, line).unwrap();
        self
    }

    pub fn r#if(&mut self, condition: &str) -> &mut Self {
        writeln!(&mut self.file, "{}if {}:", self.indents, condition).unwrap();
        self.indent(1)
    }
    pub fn elif(&mut self, condition: &str) -> &mut Self {
        self.indent(-1);
        writeln!(&mut self.file, "{}elif {}:", self.indents, condition).unwrap();
        self.indent(1)
    }
    pub fn r#else(&mut self) -> &mut Self {
        self.indent(-1).write_line("else:").indent(1)
    }

    pub fn r#for(&mut self, range: &str) -> &mut Self {
        writeln!(&mut self.file, "{}for {}:", self.indents, range).unwrap();
        self.indent(1)
    }

    pub fn r#while(&mut self, condition: &str) -> &mut Self {
        writeln!(&mut self.file, "{}while {}:", self.indents, condition).unwrap();
        self.indent(1)
    }
}

impl Write for PythonProgram {
    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
        self.file.write(buf)
    }

    fn flush(&mut self) -> Result<(), std::io::Error> {
        self.file.flush()
    }
}

impl Display for PythonProgram {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        use std::io::BufRead;
        let read_file = std::fs::File::open(self.file.path()).unwrap();
        let reader = std::io::BufReader::new(read_file);
        for line in reader.lines() {
            writeln!(f, "{}", line.unwrap())?
        }
        Ok(())
    }
}
pub mod plots {
    use std::io::Write;
    use crate::{AsPythonLitteral, PythonProgram, PythonLitteral};

    pub fn plot_xyargs<X: AsPythonLitteral, Y: AsPythonLitteral>(
        x: &X,
        y: &Y,
        args: &str,
    ) -> Result<std::process::Output, std::io::Error> {
        let mut program = PythonProgram::new();
        program.write_line("import matplotlib.pyplot as plt");
        writeln!(
            &program.file,
            "plt.plot({}, {}, {})",
            PythonLitteral(x),
            PythonLitteral(y),
            PythonLitteral(args)
        );
        program.write_line("plt.show()").run()
    }

    pub fn plot_xy<X: AsPythonLitteral, Y: AsPythonLitteral>(
        x: &X,
        y: &Y,
    ) -> Result<std::process::Output, std::io::Error> {
        let mut program = PythonProgram::new();
        program.write_line("import matplotlib.pyplot as plt");
        writeln!(
            &program.file,
            "plt.plot({}, {})",
            PythonLitteral(x),
            PythonLitteral(y),
        );
        program.write_line("plt.show()").run()
    }

    pub fn plot_y<Y: AsPythonLitteral>(y: &Y) -> Result<std::process::Output, std::io::Error> {
        let mut program = PythonProgram::new();
        program.write_line("import matplotlib.pyplot as plt");
        writeln!(&program.file, "plt.plot({})", PythonLitteral(y));
        program.write_line("plt.show()").run()
}
}

#[macro_export]
macro_rules! plot {
    ($y: expr) => {pycall::plots::plot_y($y)};
    ($x: expr, $y: expr) => {pycall::plots::plot_xy($x, $y)};
    ($x: expr, $y: expr, $args: expr) => {pycall::plots::plot_xyargs($x, $y, $args)};
}

#[test]
fn run() {
    let join = std::thread::spawn(|| quick_plot(&(-50..50).map(|x| (-x * x)).collect::<Vec<_>>()));
    let mut program = PythonProgram::new();
    program
        .write_line("import matplotlib.pyplot as plt")
        .define_variable(
            "hello",
            &(-50..50).map(|x| (x * x) as f64).collect::<Vec<_>>(),
        )
        .write_line("print(hello)")
        .write_line("plt.plot(hello)")
        .write_line("plt.show()");
    println!("program: {}\r\n{}", program.file.path().display(), &program);
    let output = program.run().unwrap();
    join.join();
}