summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: 720075002d03d5a02cc6c0f6c1a4f1cdfaa083e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#![feature(once_cell)]

mod args;
mod error;
mod logger;
mod theming;
mod xdg;

use crate::args::parse_args;
use crate::logger::LogItem;
use crate::theming::{curses_attr, parse_attrs};

use chrono::{Datelike, Timelike};
use pyo3::{
    conversion::{IntoPy, ToPyObject},
    create_exception,
    exceptions::PyIOError,
    marker::Python,
    prelude::{pyfunction, pymodule, wrap_pyfunction, PyErr, PyModule, PyObject, PyResult},
    types::{PyDateTime, PyDict},
};

create_exception!(libpoezio, LogParseError, pyo3::exceptions::PyException);

#[pymodule]
fn libpoezio(py: Python, m: &PyModule) -> PyResult<()> {
    m.add("LogParseError", py.get_type::<LogParseError>())?;
    m.add_function(wrap_pyfunction!(to_curses_attr, m)?)?;
    m.add_function(wrap_pyfunction!(parse_logs, m)?)?;
    m.add_function(wrap_pyfunction!(run_cmdline_args, m)?)?;
    m.add("XDG", xdg::PyProject::new(xdg::PROJECT.clone()))?;

    Ok(())
}

macro_rules! py_object {
    ($py:ident, $i:expr) => {
        $i.into_py($py).to_object($py)
    };
}

fn nom_to_py_err(py: Python, err: nom::Err<nom::error::Error<&str>>) -> PyErr {
    LogParseError::new_err(py_object!(py, err.to_string()))
}

#[pyfunction]
fn to_curses_attr(py: Python, fg: i16, bg: i16, attrs: &str) -> PyResult<PyObject> {
    let attrs = match parse_attrs(attrs) {
        Ok(attrs) => attrs,
        Err(err) => return Err(nom_to_py_err(py, err)),
    };
    let result = curses_attr(fg, bg, attrs);
    Ok(py_object!(py, result))
}

fn chrono_to_datetime(py: Python, chrono: &chrono::DateTime<chrono::Utc>) -> PyResult<PyObject> {
    let datetime = PyDateTime::new(
        py,
        chrono.year(),
        chrono.month() as u8,
        chrono.day() as u8,
        chrono.hour() as u8,
        chrono.minute() as u8,
        chrono.second() as u8,
        0,
        None,
    )?;
    Ok(datetime.to_object(py))
}

#[pyfunction]
fn parse_logs(py: Python, input: &str) -> PyResult<PyObject> {
    let logs = match logger::parse_logs(input) {
        Ok((_, logs)) => logs,
        Err(err) => return Err(nom_to_py_err(py, err)),
    };
    let mut items = Vec::new();
    for item in logs {
        let dict = PyDict::new(py);
        let (time, txt) = match item {
            logger::Item::Message(message) => {
                let time = chrono_to_datetime(py, message.get_time())?;
                dict.set_item("nickname", message.get_nick())?;
                (time, message.get_message())
            }
            logger::Item::Info(info) => {
                let time = chrono_to_datetime(py, info.get_time())?;
                (time, info.get_message())
            }
        };
        dict.set_item("history", true)?;
        dict.set_item("time", time)?;
        dict.set_item("txt", txt)?;
        items.push(dict);
    }
    Ok(items.into_py(py).to_object(py))
}

#[pyfunction]
fn run_cmdline_args(py: Python, argv: Vec<String>) -> PyResult<(PyObject, bool)> {
    let (args, firstrun) = parse_args(argv).map_err(|err| PyIOError::new_err(err.to_string()))?;
    Ok((args.into_py(py), firstrun))
}