unitforge 0.3.11__tar.gz → 0.3.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unitforge-0.3.11 → unitforge-0.3.13}/.gitignore +3 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/Cargo.lock +2 -2
- {unitforge-0.3.11 → unitforge-0.3.13}/Cargo.toml +2 -2
- {unitforge-0.3.11 → unitforge-0.3.13}/PKG-INFO +1 -1
- {unitforge-0.3.11 → unitforge-0.3.13}/pyproject.toml +4 -0
- unitforge-0.3.13/python/unitforge/__init__.py +4 -0
- unitforge-0.3.13/src/quantities.template.rs +624 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/unitforge_macros/Cargo.toml +1 -1
- {unitforge-0.3.11 → unitforge-0.3.13}/unitforge_macros/src/lib.rs +104 -4
- unitforge-0.3.11/python/unitforge/__init__.py +0 -5
- unitforge-0.3.11/src/quantities.template.rs +0 -240
- {unitforge-0.3.11 → unitforge-0.3.13}/.gitlab-ci.yml +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/Readme.md +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/Readme_py.md +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/scripts/generate_python_stubs.py +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/impl_macros.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/lib.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/prelude.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/acceleration.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/angle.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/angular_acceleration.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/angular_velocity.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/area.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/area_of_moment.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/charge.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/compliance.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/density.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/distance.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_area.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_distance.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_div_distance_power_four.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_per_volume.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_stress.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/force_volume.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/inverse_area.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/inverse_distance.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/inverse_stress.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/mass.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/mass_div_time_squared_volume.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/mass_per_distance.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/rotational_stiffness.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/stiffness.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/strain.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/stress.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/stress_squared.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/time.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/velocity.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/velocity_squared.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/voltage.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/quantities/volume.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/relations.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/bindings/matrix3_py.template.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/bindings/vector3_py.template.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/matrix2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/matrix2x3.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/matrix3.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/matrix3x2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/vector2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/small_linalg/vector3.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/tests/quantity_unit_tests.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/src/tests/serde_tests.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/matrix2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/matrix2x3.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/matrix3.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/matrix3x2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/ndarray_dot.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/quantities.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/small_linalg_interfaces.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/test_quantities.py +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/test_small_linalg.py +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/typecheck/mypy_smoke.py +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/vector2.rs +0 -0
- {unitforge-0.3.11 → unitforge-0.3.13}/tests/vector3.rs +0 -0
|
@@ -369,7 +369,7 @@ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
|
369
369
|
|
|
370
370
|
[[package]]
|
|
371
371
|
name = "unitforge"
|
|
372
|
-
version = "0.3.
|
|
372
|
+
version = "0.3.13"
|
|
373
373
|
dependencies = [
|
|
374
374
|
"ndarray",
|
|
375
375
|
"num-traits",
|
|
@@ -384,7 +384,7 @@ dependencies = [
|
|
|
384
384
|
|
|
385
385
|
[[package]]
|
|
386
386
|
name = "unitforge_macros"
|
|
387
|
-
version = "0.3.
|
|
387
|
+
version = "0.3.13"
|
|
388
388
|
dependencies = [
|
|
389
389
|
"regex",
|
|
390
390
|
]
|
|
@@ -4,7 +4,7 @@ crate-type = ["cdylib", "rlib"]
|
|
|
4
4
|
|
|
5
5
|
[package]
|
|
6
6
|
name = "unitforge"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.13"
|
|
8
8
|
edition = "2021"
|
|
9
9
|
authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
|
|
10
10
|
description = "A library for unit and quantity consistent computations in Rust"
|
|
@@ -23,7 +23,7 @@ pyo3 = { version = "0.26.0", features = ["extension-module", "auto-initialize",
|
|
|
23
23
|
numpy = { version = "0.26.0", optional = true }
|
|
24
24
|
strum = { version = "0.27.2", optional = true }
|
|
25
25
|
strum_macros = { version = "0.27.2", optional = true }
|
|
26
|
-
unitforge_macros = { version = "0.3.
|
|
26
|
+
unitforge_macros = { version = "0.3.13", path = "unitforge_macros" }
|
|
27
27
|
|
|
28
28
|
[features]
|
|
29
29
|
serde = ["dep:serde", "dep:serde_json", "ndarray/serde"]
|
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
use std::fmt::Display;
|
|
2
|
+
use std::cmp::Ordering;
|
|
3
|
+
use std::fmt;
|
|
4
|
+
use std::fmt::Formatter;
|
|
5
|
+
use std::ops::{Add, Sub, Mul, Div, Neg};
|
|
6
|
+
#[cfg(feature = "pyo3")]
|
|
7
|
+
use pyo3::{Bound, PyAny, prelude::*};
|
|
8
|
+
|
|
9
|
+
#[derive(Debug)]
|
|
10
|
+
pub enum QuantityOperationError {
|
|
11
|
+
AddError,
|
|
12
|
+
SubError,
|
|
13
|
+
MulError,
|
|
14
|
+
DivError,
|
|
15
|
+
SqrtError,
|
|
16
|
+
ComparisonError,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
impl fmt::Display for QuantityOperationError {
|
|
20
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
21
|
+
match self {
|
|
22
|
+
QuantityOperationError::AddError => write!(f, "Addition operation failed"),
|
|
23
|
+
QuantityOperationError::SubError => write!(f, "Subtraction operation failed"),
|
|
24
|
+
QuantityOperationError::MulError => write!(f, "Multiplication operation failed"),
|
|
25
|
+
QuantityOperationError::DivError => write!(f, "Division operation failed"),
|
|
26
|
+
QuantityOperationError::SqrtError => write!(f, "Sqrt operation failed"),
|
|
27
|
+
QuantityOperationError::ComparisonError => write!(f, "Comparison operation failed"),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
33
|
+
pub enum Quantity {
|
|
34
|
+
//Is used for runtime checked operations with quantities
|
|
35
|
+
FloatQuantity(f64),
|
|
36
|
+
// __QUANTITY_VARIANTS__
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
impl Quantity {
|
|
40
|
+
pub fn to(&self, unit: Unit) -> Result<f64, String> {
|
|
41
|
+
match (self, unit) {
|
|
42
|
+
(Quantity::FloatQuantity(value), Unit::NoUnit) => Ok(*value),
|
|
43
|
+
// __QUANTITY_TO_VARIANTS__
|
|
44
|
+
_ => Err("Cannot use given pair of quantity and unit.".to_string())
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn abs(&self) -> Quantity {
|
|
49
|
+
match self {
|
|
50
|
+
Quantity::FloatQuantity(value) => Quantity::FloatQuantity(value.abs()),
|
|
51
|
+
// __QUANTITY_ABS_VARIANTS__
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub fn is_nan(&self) -> bool {
|
|
56
|
+
match self {
|
|
57
|
+
Quantity::FloatQuantity(value) => value.is_nan(),
|
|
58
|
+
// __QUANTITY_NAN_VARIANTS__
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
impl Neg for Quantity {
|
|
65
|
+
type Output = Quantity;
|
|
66
|
+
fn neg(self) -> Quantity {
|
|
67
|
+
match self {
|
|
68
|
+
Quantity::FloatQuantity(value) => Quantity::FloatQuantity(-value),
|
|
69
|
+
// __QUANTITY_NEG_VARIANTS__
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
76
|
+
pub enum Unit {
|
|
77
|
+
//Is used for runtime checked operations with quantities
|
|
78
|
+
NoUnit,
|
|
79
|
+
// __UNIT_VARIANTS__
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
impl Display for Unit {
|
|
83
|
+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
|
84
|
+
write!(f, "{}", self.get_name())
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
impl Unit {
|
|
89
|
+
pub fn to_quantity(&self, value: f64) -> Quantity {
|
|
90
|
+
match self {
|
|
91
|
+
Unit::NoUnit => Quantity::FloatQuantity(value),
|
|
92
|
+
// __TO_QUANTITY_VARIANTS__
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pub fn get_name(&self) -> &str {
|
|
97
|
+
match self {
|
|
98
|
+
Unit::NoUnit => "No Unit",
|
|
99
|
+
// __TO_UNIT_NAME_VARIANTS__
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
impl Mul for Quantity {
|
|
105
|
+
type Output = Result<Quantity, QuantityOperationError>;
|
|
106
|
+
fn mul(self, other: Quantity) -> Result<Quantity, QuantityOperationError> {
|
|
107
|
+
fn try_multiply(lhs: &Quantity, rhs: &Quantity) -> Result<Quantity, QuantityOperationError> {
|
|
108
|
+
use Quantity::*;
|
|
109
|
+
match (lhs, rhs) {
|
|
110
|
+
(FloatQuantity(v_lhs), FloatQuantity(v_rhs)) => Ok(FloatQuantity(v_lhs * v_rhs)),
|
|
111
|
+
// __MUL_MATCHES__
|
|
112
|
+
_ => Err(QuantityOperationError::MulError)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
match try_multiply(&self, &other) {
|
|
116
|
+
Ok(result) => Ok(result),
|
|
117
|
+
Err(_) => try_multiply(&other, &self)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
impl Div for Quantity {
|
|
123
|
+
type Output = Result<Quantity, QuantityOperationError>;
|
|
124
|
+
fn div(self, other: Quantity) -> Result<Quantity, QuantityOperationError> {
|
|
125
|
+
use Quantity::*;
|
|
126
|
+
match (self, other) {
|
|
127
|
+
(FloatQuantity(v_lhs), FloatQuantity(v_rhs)) => Ok(FloatQuantity(v_lhs / v_rhs)),
|
|
128
|
+
// __DIV_MATCHES__
|
|
129
|
+
_ => Err(QuantityOperationError::DivError)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
impl Add for Quantity {
|
|
135
|
+
type Output = Result<Quantity, QuantityOperationError>;
|
|
136
|
+
fn add(self, other: Quantity) -> Result<Quantity, QuantityOperationError> {
|
|
137
|
+
use Quantity::*;
|
|
138
|
+
match (self, other) {
|
|
139
|
+
(Quantity::FloatQuantity(v_lhs), Quantity::FloatQuantity(v_rhs)) => Ok(Quantity::FloatQuantity(v_lhs + v_rhs)),
|
|
140
|
+
// __ADD_QUANTITY_MATCHES__
|
|
141
|
+
_ => Err(QuantityOperationError::AddError)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
impl Sub for Quantity {
|
|
147
|
+
type Output = Result<Quantity, QuantityOperationError>;
|
|
148
|
+
fn sub(self, other: Quantity) -> Result<Self, QuantityOperationError> {
|
|
149
|
+
use Quantity::*;
|
|
150
|
+
match (self, other) {
|
|
151
|
+
(Quantity::FloatQuantity(v_lhs), Quantity::FloatQuantity(v_rhs)) => Ok(Quantity::FloatQuantity(v_lhs - v_rhs)),
|
|
152
|
+
// __SUB_QUANTITY_MATCHES__
|
|
153
|
+
_ => Err(QuantityOperationError::SubError)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
impl Quantity {
|
|
159
|
+
pub fn extract_float(&self) -> Result<f64, String> {
|
|
160
|
+
match self {
|
|
161
|
+
Quantity::FloatQuantity(v) => Ok(*v),
|
|
162
|
+
_ => Err("Cannot extract float from Quantity enum".into()),
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// __BASE_QUANTITY_MATCHES__
|
|
167
|
+
pub fn sqrt(&self) -> Result<Self, QuantityOperationError> {
|
|
168
|
+
match self {
|
|
169
|
+
Quantity::FloatQuantity(v) => Ok(Self::FloatQuantity(v.sqrt())),
|
|
170
|
+
// __QUANTITY_SQRTS__
|
|
171
|
+
_=> Err(QuantityOperationError::SqrtError)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
impl PartialOrd for Quantity {
|
|
177
|
+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
178
|
+
use Quantity::*;
|
|
179
|
+
match (self, other) {
|
|
180
|
+
(FloatQuantity(lhs), FloatQuantity(rhs)) => lhs.partial_cmp(rhs),
|
|
181
|
+
// __QUANTITY_COMPARISONS__
|
|
182
|
+
_ => panic!("Cannot compare non matching quantities!")
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#[cfg(feature = "pyo3")]
|
|
188
|
+
fn extract_f64(v: &Bound<PyAny>) -> Option<f64> {
|
|
189
|
+
if let Ok(inner) = v.extract::<f64>() {
|
|
190
|
+
Some(inner)
|
|
191
|
+
} else if let Ok(inner) = v.extract::<f32>() {
|
|
192
|
+
Some(inner as f64)
|
|
193
|
+
} else if let Ok(inner) = v.extract::<i32>() {
|
|
194
|
+
Some(inner as f64)
|
|
195
|
+
} else if let Ok(inner) = v.extract::<i64>() {
|
|
196
|
+
Some(inner as f64)
|
|
197
|
+
} else {
|
|
198
|
+
None
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[cfg(feature = "pyo3")]
|
|
203
|
+
impl Quantity {
|
|
204
|
+
pub fn from_py_any(v: &Bound<PyAny>) -> Result<Self, String> {
|
|
205
|
+
if let Some(inner) = extract_f64(v) {
|
|
206
|
+
Ok(Quantity::FloatQuantity(inner))
|
|
207
|
+
}
|
|
208
|
+
// __EXTRACT_QUANTITY_MATCHES__
|
|
209
|
+
else if let Ok(inner) = pyo3_capsule_api::extract_quantity_from_capsule(v) {
|
|
210
|
+
Ok(inner)
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
Err("Cannot interpret given value as Quantity".to_string())
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub fn to_pyobject(self, py: Python) -> PyResult<Py<PyAny>> {
|
|
218
|
+
Ok(match self {
|
|
219
|
+
Quantity::FloatQuantity(v) => v.into_pyobject(py).map(|obj| obj.into())?,
|
|
220
|
+
// __TO_PYOBJECT_MATCHES__
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
impl Display for Quantity {
|
|
226
|
+
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
|
227
|
+
match self {
|
|
228
|
+
Quantity::FloatQuantity(v) => write!(f, "{v}"),
|
|
229
|
+
// __QUANTITY_FMT_MATCHES__
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
#[cfg(feature = "pyo3")]
|
|
235
|
+
impl Unit {
|
|
236
|
+
|
|
237
|
+
pub fn from_py_any(v: &Bound<PyAny>) -> Result<Self, String> {
|
|
238
|
+
// __EXTRACT_UNIT_MATCHES__
|
|
239
|
+
else if let Ok(inner) = pyo3_capsule_api::extract_unit_from_capsule(v) {
|
|
240
|
+
Ok(inner)
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
Err("Cannot interpret given value as Quantity".to_string())
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[cfg(feature = "pyo3")]
|
|
249
|
+
pub mod pyo3_capsule_api {
|
|
250
|
+
use super::*;
|
|
251
|
+
use pyo3::exceptions::PyValueError;
|
|
252
|
+
use pyo3::ffi;
|
|
253
|
+
use pyo3::types::{PyAnyMethods, PyCapsule, PyCapsuleMethods, PyList, PyModule};
|
|
254
|
+
use pyo3::{Bound, PyErr, PyRef, PyResult, Python};
|
|
255
|
+
use std::ffi::{c_int, CString};
|
|
256
|
+
|
|
257
|
+
pub const UNITFORGE_API_VERSION: u64 = 1;
|
|
258
|
+
pub const CAPSULE_NAME: &str = "_UNITFORGE_API";
|
|
259
|
+
|
|
260
|
+
#[repr(C)]
|
|
261
|
+
#[derive(Clone, Copy, Debug)]
|
|
262
|
+
pub struct QuantityRepr {
|
|
263
|
+
pub kind_id: u32,
|
|
264
|
+
pub multiplier: f64,
|
|
265
|
+
pub power: i32,
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
#[repr(C)]
|
|
269
|
+
#[derive(Clone, Copy, Debug)]
|
|
270
|
+
pub struct UnitRepr {
|
|
271
|
+
pub kind_id: u32,
|
|
272
|
+
pub variant_id: i32,
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
pub trait QuantityKindId {
|
|
276
|
+
const KIND_ID: u32;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
pub trait UnitKindId {
|
|
280
|
+
const KIND_ID: u32;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub trait UnitVariantId {
|
|
284
|
+
fn to_variant_id(self) -> i32;
|
|
285
|
+
fn from_variant_id(variant_id: i32) -> Option<Self>
|
|
286
|
+
where
|
|
287
|
+
Self: Sized;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// __QUANTITY_KIND_ID_IMPLS__
|
|
291
|
+
// __UNIT_KIND_ID_IMPLS__
|
|
292
|
+
// __UNIT_VARIANT_ID_IMPLS__
|
|
293
|
+
|
|
294
|
+
pub fn quantity_to_repr(quantity: Quantity) -> Result<QuantityRepr, String> {
|
|
295
|
+
match quantity {
|
|
296
|
+
Quantity::FloatQuantity(value) => Ok(QuantityRepr {
|
|
297
|
+
kind_id: 0,
|
|
298
|
+
multiplier: value,
|
|
299
|
+
power: 0,
|
|
300
|
+
}),
|
|
301
|
+
// __QUANTITY_TO_REPR_MATCHES__
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
fn quantity_from_repr(repr: QuantityRepr) -> Result<Quantity, String> {
|
|
306
|
+
match repr.kind_id {
|
|
307
|
+
0 => Ok(Quantity::FloatQuantity(repr.multiplier * 10f64.powi(repr.power))),
|
|
308
|
+
// __QUANTITY_FROM_REPR_MATCHES__
|
|
309
|
+
_ => Err(format!("Unknown quantity kind id: {}", repr.kind_id)),
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
pub fn unit_to_repr(unit: Unit) -> UnitRepr {
|
|
314
|
+
match unit {
|
|
315
|
+
Unit::NoUnit => UnitRepr {
|
|
316
|
+
kind_id: 0,
|
|
317
|
+
variant_id: 0,
|
|
318
|
+
},
|
|
319
|
+
// __UNIT_TO_REPR_MATCHES__
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
fn unit_from_repr(repr: UnitRepr) -> Result<Unit, String> {
|
|
324
|
+
match repr.kind_id {
|
|
325
|
+
0 => Ok(Unit::NoUnit),
|
|
326
|
+
// __UNIT_FROM_REPR_MATCHES__
|
|
327
|
+
_ => Err(format!("Unknown unit kind id: {}", repr.kind_id)),
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
pub fn extract_quantity_from_capsule(obj: &Bound<PyAny>) -> PyResult<Quantity> {
|
|
332
|
+
let py = obj.py();
|
|
333
|
+
let module = PyModule::import(py, "unitforge")?;
|
|
334
|
+
let api = get_api(py, &module)?;
|
|
335
|
+
let mut repr = QuantityRepr {
|
|
336
|
+
kind_id: 0,
|
|
337
|
+
multiplier: 0.0,
|
|
338
|
+
power: 0,
|
|
339
|
+
};
|
|
340
|
+
// SAFETY: `obj` is a valid python object and `repr` points to writable memory.
|
|
341
|
+
let rc = unsafe { (api.extract_quantity_repr)(obj.as_ptr(), &mut repr as *mut QuantityRepr) };
|
|
342
|
+
if rc != 0 {
|
|
343
|
+
return Err(PyErr::fetch(py));
|
|
344
|
+
}
|
|
345
|
+
quantity_from_repr(repr).map_err(PyValueError::new_err)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
pub fn extract_unit_from_capsule(obj: &Bound<PyAny>) -> PyResult<Unit> {
|
|
349
|
+
let py = obj.py();
|
|
350
|
+
let module = PyModule::import(py, "unitforge")?;
|
|
351
|
+
let api = get_api(py, &module)?;
|
|
352
|
+
let mut repr = UnitRepr {
|
|
353
|
+
kind_id: 0,
|
|
354
|
+
variant_id: 0,
|
|
355
|
+
};
|
|
356
|
+
// SAFETY: `obj` is a valid python object and `repr` points to writable memory.
|
|
357
|
+
let rc = unsafe { (api.extract_unit_repr)(obj.as_ptr(), &mut repr as *mut UnitRepr) };
|
|
358
|
+
if rc != 0 {
|
|
359
|
+
return Err(PyErr::fetch(py));
|
|
360
|
+
}
|
|
361
|
+
unit_from_repr(repr).map_err(PyValueError::new_err)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#[repr(C)]
|
|
365
|
+
#[derive(Clone, Copy)]
|
|
366
|
+
pub struct UnitforgeApiV1 {
|
|
367
|
+
pub version: u64,
|
|
368
|
+
pub extract_quantity_repr:
|
|
369
|
+
unsafe extern "C" fn(obj: *mut ffi::PyObject, out: *mut QuantityRepr) -> c_int,
|
|
370
|
+
pub quantity_repr_to_object:
|
|
371
|
+
unsafe extern "C" fn(repr: QuantityRepr) -> *mut ffi::PyObject,
|
|
372
|
+
pub extract_unit_repr:
|
|
373
|
+
unsafe extern "C" fn(obj: *mut ffi::PyObject, out: *mut UnitRepr) -> c_int,
|
|
374
|
+
pub unit_repr_to_object: unsafe extern "C" fn(repr: UnitRepr) -> *mut ffi::PyObject,
|
|
375
|
+
pub extract_vector3_repr:
|
|
376
|
+
unsafe extern "C" fn(obj: *mut ffi::PyObject, out: *mut QuantityRepr) -> c_int,
|
|
377
|
+
pub vector3_repr_to_object:
|
|
378
|
+
unsafe extern "C" fn(data: *const QuantityRepr) -> *mut ffi::PyObject,
|
|
379
|
+
pub extract_matrix3_repr:
|
|
380
|
+
unsafe extern "C" fn(obj: *mut ffi::PyObject, out: *mut QuantityRepr) -> c_int,
|
|
381
|
+
pub matrix3_repr_to_object:
|
|
382
|
+
unsafe extern "C" fn(data: *const QuantityRepr) -> *mut ffi::PyObject,
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
unsafe extern "C" fn extract_quantity_repr_impl(
|
|
386
|
+
obj: *mut ffi::PyObject,
|
|
387
|
+
out: *mut QuantityRepr,
|
|
388
|
+
) -> c_int {
|
|
389
|
+
let py = Python::assume_attached();
|
|
390
|
+
let result: PyResult<()> = (|| {
|
|
391
|
+
if out.is_null() {
|
|
392
|
+
return Err(PyValueError::new_err("output pointer for quantity repr is null"));
|
|
393
|
+
}
|
|
394
|
+
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
395
|
+
let quantity = Quantity::from_py_any(&obj_bound).map_err(PyValueError::new_err)?;
|
|
396
|
+
unsafe {
|
|
397
|
+
*out = quantity_to_repr(quantity).map_err(PyValueError::new_err)?;
|
|
398
|
+
}
|
|
399
|
+
Ok(())
|
|
400
|
+
})();
|
|
401
|
+
|
|
402
|
+
match result {
|
|
403
|
+
Ok(_) => 0,
|
|
404
|
+
Err(err) => {
|
|
405
|
+
err.restore(py);
|
|
406
|
+
-1
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
unsafe extern "C" fn quantity_repr_to_object_impl(repr: QuantityRepr) -> *mut ffi::PyObject {
|
|
412
|
+
let py = Python::assume_attached();
|
|
413
|
+
let result: PyResult<Py<PyAny>> = (|| {
|
|
414
|
+
quantity_from_repr(repr)
|
|
415
|
+
.map_err(PyValueError::new_err)?
|
|
416
|
+
.to_pyobject(py)
|
|
417
|
+
})();
|
|
418
|
+
match result {
|
|
419
|
+
Ok(obj) => obj.into_ptr(),
|
|
420
|
+
Err(err) => {
|
|
421
|
+
err.restore(py);
|
|
422
|
+
std::ptr::null_mut()
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
unsafe extern "C" fn extract_unit_repr_impl(
|
|
428
|
+
obj: *mut ffi::PyObject,
|
|
429
|
+
out: *mut UnitRepr,
|
|
430
|
+
) -> c_int {
|
|
431
|
+
let py = Python::assume_attached();
|
|
432
|
+
let result: PyResult<()> = (|| {
|
|
433
|
+
if out.is_null() {
|
|
434
|
+
return Err(PyValueError::new_err("output pointer for unit repr is null"));
|
|
435
|
+
}
|
|
436
|
+
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
437
|
+
let unit = Unit::from_py_any(&obj_bound).map_err(PyValueError::new_err)?;
|
|
438
|
+
unsafe {
|
|
439
|
+
*out = unit_to_repr(unit);
|
|
440
|
+
}
|
|
441
|
+
Ok(())
|
|
442
|
+
})();
|
|
443
|
+
|
|
444
|
+
match result {
|
|
445
|
+
Ok(_) => 0,
|
|
446
|
+
Err(err) => {
|
|
447
|
+
err.restore(py);
|
|
448
|
+
-1
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
unsafe extern "C" fn unit_repr_to_object_impl(repr: UnitRepr) -> *mut ffi::PyObject {
|
|
454
|
+
let py = Python::assume_attached();
|
|
455
|
+
let result: PyResult<Py<PyAny>> = (|| {
|
|
456
|
+
let unit = unit_from_repr(repr).map_err(PyValueError::new_err)?;
|
|
457
|
+
match unit {
|
|
458
|
+
Unit::NoUnit => {
|
|
459
|
+
Err(PyValueError::new_err("NoUnit cannot be converted into a Python object"))
|
|
460
|
+
}
|
|
461
|
+
// __UNIT_TO_PYOBJECT_MATCHES__
|
|
462
|
+
}
|
|
463
|
+
})();
|
|
464
|
+
match result {
|
|
465
|
+
Ok(obj) => obj.into_ptr(),
|
|
466
|
+
Err(err) => {
|
|
467
|
+
err.restore(py);
|
|
468
|
+
std::ptr::null_mut()
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
unsafe extern "C" fn extract_vector3_repr_impl(
|
|
474
|
+
obj: *mut ffi::PyObject,
|
|
475
|
+
out: *mut QuantityRepr,
|
|
476
|
+
) -> c_int {
|
|
477
|
+
let py = Python::assume_attached();
|
|
478
|
+
let result: PyResult<()> = (|| {
|
|
479
|
+
if out.is_null() {
|
|
480
|
+
return Err(PyValueError::new_err("output pointer for vector repr is null"));
|
|
481
|
+
}
|
|
482
|
+
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
483
|
+
let vec = obj_bound.extract::<Vector3Py>()?;
|
|
484
|
+
for i in 0..3 {
|
|
485
|
+
let repr = quantity_to_repr(vec.data[i]).map_err(PyValueError::new_err)?;
|
|
486
|
+
unsafe {
|
|
487
|
+
*out.add(i) = repr;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
Ok(())
|
|
491
|
+
})();
|
|
492
|
+
match result {
|
|
493
|
+
Ok(_) => 0,
|
|
494
|
+
Err(err) => {
|
|
495
|
+
err.restore(py);
|
|
496
|
+
-1
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
unsafe extern "C" fn vector3_repr_to_object_impl(
|
|
502
|
+
data: *const QuantityRepr,
|
|
503
|
+
) -> *mut ffi::PyObject {
|
|
504
|
+
let py = Python::assume_attached();
|
|
505
|
+
let result: PyResult<Py<PyAny>> = (|| {
|
|
506
|
+
if data.is_null() {
|
|
507
|
+
return Err(PyValueError::new_err("input pointer for vector repr is null"));
|
|
508
|
+
}
|
|
509
|
+
let mut quantity_data = [Quantity::FloatQuantity(0.0); 3];
|
|
510
|
+
for i in 0..3 {
|
|
511
|
+
let repr = unsafe { *data.add(i) };
|
|
512
|
+
quantity_data[i] = quantity_from_repr(repr).map_err(PyValueError::new_err)?;
|
|
513
|
+
}
|
|
514
|
+
let vec = Vector3Py { data: quantity_data };
|
|
515
|
+
Ok(vec.into_pyobject(py)?.into_any().unbind())
|
|
516
|
+
})();
|
|
517
|
+
|
|
518
|
+
match result {
|
|
519
|
+
Ok(obj) => obj.into_ptr(),
|
|
520
|
+
Err(err) => {
|
|
521
|
+
err.restore(py);
|
|
522
|
+
std::ptr::null_mut()
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
unsafe extern "C" fn extract_matrix3_repr_impl(
|
|
528
|
+
obj: *mut ffi::PyObject,
|
|
529
|
+
out: *mut QuantityRepr,
|
|
530
|
+
) -> c_int {
|
|
531
|
+
let py = Python::assume_attached();
|
|
532
|
+
let result: PyResult<()> = (|| {
|
|
533
|
+
if out.is_null() {
|
|
534
|
+
return Err(PyValueError::new_err("output pointer for matrix repr is null"));
|
|
535
|
+
}
|
|
536
|
+
let obj_bound = Bound::from_borrowed_ptr(py, obj);
|
|
537
|
+
let mat = obj_bound.extract::<PyRef<Matrix3Py>>()?;
|
|
538
|
+
for i in 0..3 {
|
|
539
|
+
for j in 0..3 {
|
|
540
|
+
let repr =
|
|
541
|
+
quantity_to_repr(mat.data[i][j]).map_err(PyValueError::new_err)?;
|
|
542
|
+
unsafe {
|
|
543
|
+
*out.add(i * 3 + j) = repr;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
Ok(())
|
|
548
|
+
})();
|
|
549
|
+
match result {
|
|
550
|
+
Ok(_) => 0,
|
|
551
|
+
Err(err) => {
|
|
552
|
+
err.restore(py);
|
|
553
|
+
-1
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
unsafe extern "C" fn matrix3_repr_to_object_impl(
|
|
559
|
+
data: *const QuantityRepr,
|
|
560
|
+
) -> *mut ffi::PyObject {
|
|
561
|
+
let py = Python::assume_attached();
|
|
562
|
+
let result: PyResult<Py<PyAny>> = (|| {
|
|
563
|
+
if data.is_null() {
|
|
564
|
+
return Err(PyValueError::new_err("input pointer for matrix repr is null"));
|
|
565
|
+
}
|
|
566
|
+
let mut quantity_data = [[Quantity::FloatQuantity(0.0); 3]; 3];
|
|
567
|
+
for i in 0..3 {
|
|
568
|
+
for j in 0..3 {
|
|
569
|
+
let repr = unsafe { *data.add(i * 3 + j) };
|
|
570
|
+
quantity_data[i][j] =
|
|
571
|
+
quantity_from_repr(repr).map_err(PyValueError::new_err)?;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
let mat = Matrix3Py { data: quantity_data };
|
|
575
|
+
Ok(mat.into_pyobject(py)?.into_any().unbind())
|
|
576
|
+
})();
|
|
577
|
+
match result {
|
|
578
|
+
Ok(obj) => obj.into_ptr(),
|
|
579
|
+
Err(err) => {
|
|
580
|
+
err.restore(py);
|
|
581
|
+
std::ptr::null_mut()
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
pub fn register_unitforge_api(py: Python<'_>, m: &Bound<PyModule>) -> PyResult<()> {
|
|
587
|
+
let api = UnitforgeApiV1 {
|
|
588
|
+
version: UNITFORGE_API_VERSION,
|
|
589
|
+
extract_quantity_repr: extract_quantity_repr_impl,
|
|
590
|
+
quantity_repr_to_object: quantity_repr_to_object_impl,
|
|
591
|
+
extract_unit_repr: extract_unit_repr_impl,
|
|
592
|
+
unit_repr_to_object: unit_repr_to_object_impl,
|
|
593
|
+
extract_vector3_repr: extract_vector3_repr_impl,
|
|
594
|
+
vector3_repr_to_object: vector3_repr_to_object_impl,
|
|
595
|
+
extract_matrix3_repr: extract_matrix3_repr_impl,
|
|
596
|
+
matrix3_repr_to_object: matrix3_repr_to_object_impl,
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
let capsule = PyCapsule::new_with_destructor(
|
|
600
|
+
py,
|
|
601
|
+
api,
|
|
602
|
+
Some(CString::new(CAPSULE_NAME).unwrap()),
|
|
603
|
+
|_api, _ctx| {},
|
|
604
|
+
)?;
|
|
605
|
+
m.add(CAPSULE_NAME, capsule)?;
|
|
606
|
+
Ok(())
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
pub fn get_api<'py>(py: Python<'py>, module: &Bound<'py, PyModule>) -> PyResult<&'py UnitforgeApiV1> {
|
|
610
|
+
let capsule = module.getattr(CAPSULE_NAME)?.cast_into::<PyCapsule>()?;
|
|
611
|
+
if !capsule.is_valid() {
|
|
612
|
+
return Err(PyValueError::new_err(format!(
|
|
613
|
+
"Invalid capsule in unitforge module at attribute '{CAPSULE_NAME}'"
|
|
614
|
+
)));
|
|
615
|
+
}
|
|
616
|
+
let ptr = capsule.pointer();
|
|
617
|
+
if ptr.is_null() {
|
|
618
|
+
return Err(PyValueError::new_err(format!(
|
|
619
|
+
"Null pointer in unitforge capsule '{CAPSULE_NAME}'"
|
|
620
|
+
)));
|
|
621
|
+
}
|
|
622
|
+
unsafe { Ok(&*ptr.cast::<UnitforgeApiV1>()) }
|
|
623
|
+
}
|
|
624
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "unitforge_macros"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.13"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Dr.Q team <info@askdrq.com>", "Henrik Stromberg <henrik@askdrq.com>"]
|
|
6
6
|
description = "Proc macros for generating units, quantities, and Python bindings in unitforge"
|