-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathlib.rs
91 lines (84 loc) · 3.01 KB
/
lib.rs
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
mod data;
mod error;
mod types;
mod utils;
use error::{ErrorContext, py_errors};
use pyo3::prelude::*;
use types::python::{
ExcelReader, ExcelSheet,
excelsheet::column_info::{ColumnInfo, ColumnInfoNoDtype},
table::ExcelTable,
};
/// Reads an excel file and returns an object allowing to access its sheets and a bit of metadata
#[pyfunction]
fn read_excel(source: &Bound<'_, PyAny>) -> PyResult<ExcelReader> {
use py_errors::IntoPyResult;
if let Ok(path) = source.extract::<String>() {
ExcelReader::try_from_path(&path)
.with_context(|| format!("could not load excel file at {path}"))
.into_pyresult()
} else if let Ok(bytes) = source.extract::<&[u8]>() {
ExcelReader::try_from(bytes)
.with_context(|| "could not load excel file for those bytes")
.into_pyresult()
} else {
Err(py_errors::InvalidParametersError::new_err(
"source must be a string or bytes",
))
}
}
// Taken from pydantic-core:
// https://github.com/pydantic/pydantic-core/blob/main/src/lib.rs#L24
fn get_version() -> String {
let version = env!("CARGO_PKG_VERSION").to_string();
// cargo uses "1.0-alpha1" etc. while python uses "1.0.0a1", this is not full compatibility,
// but it's good enough for now
// see https://docs.rs/semver/1.0.9/semver/struct.Version.html#method.parse for rust spec
// see https://peps.python.org/pep-0440/ for python spec
// it seems the dot after "alpha/beta" e.g. "-alpha.1" is not necessary, hence why this works
version.replace("-alpha", "a").replace("-beta", "b")
}
#[pymodule]
fn _fastexcel(m: &Bound<'_, PyModule>) -> PyResult<()> {
pyo3_log::init();
let py = m.py();
m.add_function(wrap_pyfunction!(read_excel, m)?)?;
m.add_class::<ColumnInfo>()?;
m.add_class::<ColumnInfoNoDtype>()?;
m.add_class::<ExcelSheet>()?;
m.add_class::<ExcelReader>()?;
m.add_class::<ExcelTable>()?;
m.add("__version__", get_version())?;
// errors
[
("FastExcelError", py.get_type::<py_errors::FastExcelError>()),
(
"UnsupportedColumnTypeCombinationError",
py.get_type::<py_errors::UnsupportedColumnTypeCombinationError>(),
),
(
"CannotRetrieveCellDataError",
py.get_type::<py_errors::CannotRetrieveCellDataError>(),
),
(
"CalamineCellError",
py.get_type::<py_errors::CalamineCellError>(),
),
("CalamineError", py.get_type::<py_errors::CalamineError>()),
(
"SheetNotFoundError",
py.get_type::<py_errors::SheetNotFoundError>(),
),
(
"ColumnNotFoundError",
py.get_type::<py_errors::ColumnNotFoundError>(),
),
("ArrowError", py.get_type::<py_errors::ArrowError>()),
(
"InvalidParametersError",
py.get_type::<py_errors::InvalidParametersError>(),
),
]
.into_iter()
.try_for_each(|(exc_name, exc_type)| m.add(exc_name, exc_type))
}