1use ::beancount_parser_lima as lima;
2use pyo3::prelude::*;
3use pyo3::types::PyList;
4use std::io::{self, prelude::*};
5use std::path::PathBuf;
6use types::Options;
7
8#[derive(Debug)]
10#[pyclass(frozen)]
11pub struct BeancountSources(lima::BeancountSources);
12
13#[pymethods]
14impl BeancountSources {
15 #[new]
16 fn new(path: &str) -> PyResult<Self> {
17 let sources = lima::BeancountSources::try_from(PathBuf::from(path))?;
18 Ok(BeancountSources(sources))
19 }
20
21 fn parse(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
22 let mut stderr = &io::stderr();
23
24 let parser = lima::BeancountParser::new(&self.0);
25 writeln!(stderr, "{:?}", &self.0).unwrap();
27
28 parse(py, &parser)
29 }
30
31 fn write(&self, py: Python<'_>, errors_and_warnings: Py<PyList>) -> PyResult<()> {
34 let errors_and_warnings = errors_and_warnings.bind(py);
35 let mut errors = Vec::new();
36 let mut warnings = Vec::new();
37
38 for x in errors_and_warnings.iter() {
39 match Error::extract_bound(&x) {
40 Ok(e) => errors.push(e.0),
41 Err(_) => match Warning::extract_bound(&x) {
42 Ok(w) => warnings.push(w.0),
43 Err(_) => {
44 eprintln!("Failed to extract error or warning");
45 }
46 },
47 }
48 }
49
50 let stderr = &io::stderr();
51 self.0.write(stderr, errors).map_err(PyErr::from)?;
52 self.0.write(stderr, warnings).map_err(PyErr::from)
53 }
54}
55
56#[pymodule]
58fn beancount_parser_lima(m: &Bound<'_, PyModule>) -> PyResult<()> {
59 m.add_class::<BeancountSources>()?;
60 m.add_class::<ParseSuccess>()?;
61 m.add_class::<ParseError>()?;
62
63 Ok(())
64}
65
66fn parse(py: Python<'_>, parser: &lima::BeancountParser) -> PyResult<Py<PyAny>> {
67 match parser.parse() {
68 Ok(lima::ParseSuccess {
69 directives,
70 options,
71 plugins,
72 warnings,
73 }) => {
74 use lima::DirectiveVariant as V;
75
76 let mut c = Converter::new();
77
78 let directives = directives
79 .into_iter()
80 .map(|d| match d.variant() {
81 V::Transaction(x) => c.transaction(py, d.date(), d.metadata(), x),
82 V::Price(x) => c.price(py, d.date(), d.metadata(), x),
83 V::Balance(x) => c.balance(py, d.date(), d.metadata(), x),
84 V::Open(x) => c.open(py, d.date(), d.metadata(), x),
85 V::Close(x) => c.close(py, d.date(), d.metadata(), x),
86 V::Commodity(x) => c.commodity(py, d.date(), d.metadata(), x),
87 V::Pad(x) => c.pad(py, d.date(), d.metadata(), x),
88 V::Document(x) => c.document(py, d.date(), d.metadata(), x),
89 V::Note(x) => c.note(py, d.date(), d.metadata(), x),
90 V::Event(x) => c.event(py, d.date(), d.metadata(), x),
91 V::Query(x) => c.query(py, d.date(), d.metadata(), x),
92 })
93 .collect::<PyResult<Vec<Py<PyAny>>>>()?;
94
95 let options = c.options(py, &options)?;
96 let plugins = plugins
97 .iter()
98 .map(|x| c.plugin(py, x))
99 .collect::<PyResult<Vec<Plugin>>>()?;
100
101 let warnings = warnings.into_iter().map(Warning::new).collect::<Vec<_>>();
102
103 Ok(Py::new(
104 py,
105 (
106 ParseSuccess {
107 directives,
108 options,
109 plugins,
110 warnings,
111 },
112 ParseResult {},
113 ),
114 )?
115 .into_any())
116 }
117
118 Err(lima::ParseError { errors, warnings }) => {
119 let errors = errors.into_iter().map(Error::new).collect::<Vec<_>>();
120 let warnings = warnings.into_iter().map(Warning::new).collect::<Vec<_>>();
121
122 Ok(Py::new(py, (ParseError { errors, warnings }, ParseResult {}))?.into_any())
123 }
124 }
125}
126
127#[derive(Debug)]
129#[pyclass(frozen, subclass)]
130pub struct ParseResult {}
131
132#[derive(Debug)]
134#[pyclass(frozen, extends=ParseResult)]
135pub struct ParseSuccess {
136 #[pyo3(get)]
137 pub(crate) directives: Vec<Py<PyAny>>,
138 #[pyo3(get)]
139 pub(crate) options: Options,
140 #[pyo3(get)]
141 pub(crate) plugins: Vec<Plugin>,
142 #[pyo3(get)]
143 pub(crate) warnings: Vec<Warning>,
144}
145
146#[derive(Debug)]
148#[pyclass(frozen, extends=ParseResult)]
149pub struct ParseError {
150 #[pyo3(get)]
151 pub(crate) errors: Vec<Error>,
152 #[pyo3(get)]
153 pub(crate) warnings: Vec<Warning>,
154}
155
156#[derive(Clone, Debug)]
158#[pyclass(frozen)]
159pub struct Error(lima::Error);
160
161impl Error {
162 fn new(e: lima::Error) -> Self {
163 Error(e)
164 }
165}
166
167#[derive(Clone, Debug)]
169#[pyclass(frozen)]
170pub struct Warning(lima::Warning);
171
172impl Warning {
173 fn new(w: lima::Warning) -> Self {
174 Warning(w)
175 }
176}
177
178mod conversions;
179use conversions::Converter;
180
181use crate::types::Plugin;
182mod types;