linkml 1.9.4rc1__py3-none-any.whl → 1.9.5rc1__py3-none-any.whl

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.
Files changed (77) hide show
  1. linkml/cli/main.py +4 -0
  2. linkml/generators/__init__.py +2 -0
  3. linkml/generators/common/build.py +5 -20
  4. linkml/generators/common/template.py +289 -3
  5. linkml/generators/docgen.py +55 -10
  6. linkml/generators/erdiagramgen.py +9 -5
  7. linkml/generators/graphqlgen.py +32 -6
  8. linkml/generators/jsonldcontextgen.py +78 -12
  9. linkml/generators/jsonschemagen.py +29 -12
  10. linkml/generators/mermaidclassdiagramgen.py +21 -3
  11. linkml/generators/owlgen.py +4 -1
  12. linkml/generators/panderagen/dataframe_class.py +13 -0
  13. linkml/generators/panderagen/dataframe_field.py +50 -0
  14. linkml/generators/panderagen/linkml_pandera_validator.py +186 -0
  15. linkml/generators/panderagen/panderagen.py +22 -5
  16. linkml/generators/panderagen/panderagen_class_based/class.jinja2 +70 -13
  17. linkml/generators/panderagen/panderagen_class_based/custom_checks.jinja2 +27 -0
  18. linkml/generators/panderagen/panderagen_class_based/enums.jinja2 +3 -3
  19. linkml/generators/panderagen/panderagen_class_based/pandera.jinja2 +12 -2
  20. linkml/generators/panderagen/panderagen_class_based/slots.jinja2 +19 -17
  21. linkml/generators/panderagen/slot_generator_mixin.py +143 -16
  22. linkml/generators/panderagen/transforms/__init__.py +19 -0
  23. linkml/generators/panderagen/transforms/collection_dict_model_transform.py +62 -0
  24. linkml/generators/panderagen/transforms/list_dict_model_transform.py +66 -0
  25. linkml/generators/panderagen/transforms/model_transform.py +8 -0
  26. linkml/generators/panderagen/transforms/nested_struct_model_transform.py +27 -0
  27. linkml/generators/panderagen/transforms/simple_dict_model_transform.py +86 -0
  28. linkml/generators/plantumlgen.py +17 -11
  29. linkml/generators/pydanticgen/pydanticgen.py +53 -2
  30. linkml/generators/pydanticgen/template.py +45 -233
  31. linkml/generators/pydanticgen/templates/attribute.py.jinja +1 -0
  32. linkml/generators/pydanticgen/templates/base_model.py.jinja +16 -2
  33. linkml/generators/pydanticgen/templates/imports.py.jinja +1 -1
  34. linkml/generators/rdfgen.py +11 -2
  35. linkml/generators/rustgen/__init__.py +3 -0
  36. linkml/generators/rustgen/build.py +94 -0
  37. linkml/generators/rustgen/cli.py +65 -0
  38. linkml/generators/rustgen/rustgen.py +1038 -0
  39. linkml/generators/rustgen/template.py +865 -0
  40. linkml/generators/rustgen/templates/Cargo.toml.jinja +42 -0
  41. linkml/generators/rustgen/templates/anything.rs.jinja +142 -0
  42. linkml/generators/rustgen/templates/as_key_value.rs.jinja +56 -0
  43. linkml/generators/rustgen/templates/class_module.rs.jinja +8 -0
  44. linkml/generators/rustgen/templates/enum.rs.jinja +54 -0
  45. linkml/generators/rustgen/templates/file.rs.jinja +62 -0
  46. linkml/generators/rustgen/templates/import.rs.jinja +4 -0
  47. linkml/generators/rustgen/templates/imports.rs.jinja +8 -0
  48. linkml/generators/rustgen/templates/poly.rs.jinja +9 -0
  49. linkml/generators/rustgen/templates/poly_containers.rs.jinja +439 -0
  50. linkml/generators/rustgen/templates/poly_trait.rs.jinja +15 -0
  51. linkml/generators/rustgen/templates/poly_trait_impl.rs.jinja +5 -0
  52. linkml/generators/rustgen/templates/poly_trait_impl_orsubtype.rs.jinja +5 -0
  53. linkml/generators/rustgen/templates/poly_trait_property.rs.jinja +8 -0
  54. linkml/generators/rustgen/templates/poly_trait_property_impl.rs.jinja +132 -0
  55. linkml/generators/rustgen/templates/poly_trait_property_match.rs.jinja +10 -0
  56. linkml/generators/rustgen/templates/property.rs.jinja +19 -0
  57. linkml/generators/rustgen/templates/pyproject.toml.jinja +10 -0
  58. linkml/generators/rustgen/templates/serde_utils.rs.jinja +310 -0
  59. linkml/generators/rustgen/templates/slot_range_as_union.rs.jinja +61 -0
  60. linkml/generators/rustgen/templates/struct.rs.jinja +75 -0
  61. linkml/generators/rustgen/templates/struct_or_subtype_enum.rs.jinja +108 -0
  62. linkml/generators/rustgen/templates/typealias.rs.jinja +13 -0
  63. linkml/generators/sqltablegen.py +18 -16
  64. linkml/generators/yarrrmlgen.py +157 -0
  65. linkml/linter/config/datamodel/config.py +160 -293
  66. linkml/linter/config/datamodel/config.yaml +34 -26
  67. linkml/linter/config/default.yaml +4 -0
  68. linkml/linter/config/recommended.yaml +4 -0
  69. linkml/linter/linter.py +1 -2
  70. linkml/linter/rules.py +37 -0
  71. linkml/utils/schemaloader.py +55 -3
  72. {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/METADATA +2 -2
  73. {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/RECORD +76 -38
  74. {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/entry_points.txt +1 -0
  75. linkml/generators/panderagen/panderagen_class_based/mixins.jinja2 +0 -26
  76. {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/WHEEL +0 -0
  77. {linkml-1.9.4rc1.dist-info → linkml-1.9.5rc1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,310 @@
1
+ #[cfg(feature = "serde")]
2
+ use serde::{de::{DeserializeOwned, IntoDeserializer}, Deserialize, Deserializer};
3
+ #[cfg(feature = "serde")]
4
+ use serde::de::Error;
5
+ #[cfg(feature = "serde")]
6
+ use serde_value::{Value, ValueDeserializer};
7
+ #[cfg(feature = "serde")]
8
+ use std::collections::{BTreeMap, HashMap};
9
+ #[cfg(all(feature = "serde", feature = "pyo3"))]
10
+ use pyo3::prelude::*;
11
+ #[cfg(all(feature = "serde", feature = "pyo3"))]
12
+ use pyo3::types::{PyAny, PyDict, PyList, PyTuple};
13
+
14
+ #[cfg(feature = "serde")]
15
+ pub trait InlinedPair: Sized {
16
+ type Key: std::hash::Hash + Eq + serde::de::DeserializeOwned + Clone + Ord;
17
+ type Value: serde::de::DeserializeOwned;
18
+ type Error: std::fmt::Display;
19
+
20
+ fn from_pair_mapping(k: Self::Key, v: Value) -> Result<Self,Self::Error>;
21
+ fn from_pair_simple(k: Self::Key, v: Value) -> Result<Self,Self::Error>;
22
+ fn extract_key(&self) -> &Self::Key;
23
+ }
24
+
25
+ #[cfg(feature = "serde")]
26
+ impl<T> InlinedPair for Box<T>
27
+ where
28
+ T: InlinedPair + ?Sized, // allow unsized boxes too
29
+ {
30
+ type Key = T::Key;
31
+ type Value = T::Value;
32
+ type Error = T::Error;
33
+
34
+ #[inline]
35
+ fn from_pair_mapping(k: Self::Key, v: Value) -> Result<Self, Self::Error> {
36
+ T::from_pair_mapping(k, v).map(|x| Box::new(x))
37
+ }
38
+
39
+ #[inline]
40
+ fn from_pair_simple(k: Self::Key, v: Value) -> Result<Self, Self::Error> {
41
+ T::from_pair_simple(k, v).map(|x| Box::new(x))
42
+ }
43
+
44
+ #[inline]
45
+ fn extract_key(&self) -> &Self::Key {
46
+ T::extract_key(self)
47
+ }
48
+
49
+ }
50
+
51
+ #[cfg(feature = "serde")]
52
+ #[allow(dead_code)]
53
+ pub fn deserialize_inlined_dict_list<'de, D, T>(de: D) -> Result<Vec<T>, D::Error>
54
+ where
55
+ D: Deserializer<'de>,
56
+ T: InlinedPair,
57
+ {
58
+ let raw: BTreeMap<T::Key, Value> = BTreeMap::deserialize(de)?;
59
+ raw.into_iter().map(|(k, v)| {
60
+ let obj = T::from_pair_mapping(k.clone(), v).map_err(D::Error::custom)?;
61
+ Ok(obj)
62
+ }).collect()
63
+
64
+ }
65
+
66
+ #[cfg(feature = "serde")]
67
+ pub fn deserialize_inlined_dict_map<'de, D, T>(
68
+ de: D,
69
+ ) -> Result<HashMap<T::Key, T>, D::Error>
70
+ where
71
+ D: Deserializer<'de>,
72
+ T: InlinedPair + Deserialize<'de>,
73
+ {
74
+
75
+ // Parse into a generic AST once
76
+ let ast: Value = Value::deserialize(de)?;
77
+
78
+ match ast {
79
+ // ---------- { key : value } form ----------
80
+ Value::Map(m) => {
81
+ let mut out = HashMap::with_capacity(m.len());
82
+ for (k_ast, v_ast) in m {
83
+ // 1) convert key and value separately
84
+ let key: T::Key = Deserialize::deserialize(
85
+ ValueDeserializer::<D::Error>::new(k_ast)
86
+ ).map_err(D::Error::custom)?;
87
+
88
+
89
+ // ----------------- decide by the *value* shape
90
+ let obj = match v_ast {
91
+ // (1) full object (mapping) -> deserialize directly
92
+ Value::Map(_) => {
93
+ let m: Value = Deserialize::deserialize(
94
+ ValueDeserializer::<D::Error>::new(v_ast)
95
+ ).map_err(D::Error::custom)?;
96
+ T::from_pair_mapping(key.clone(), m).map_err(D::Error::custom)?
97
+ }
98
+ other => {
99
+ T::from_pair_simple(key.clone(), other).map_err(D::Error::custom)?
100
+ }
101
+ };
102
+
103
+
104
+ out.insert(key, obj);
105
+ }
106
+ Ok(out)
107
+ }
108
+
109
+ // ---------- [ value, ... ] form -------------
110
+ Value::Seq(seq) => {
111
+ let mut out = HashMap::with_capacity(seq.len());
112
+ for v_ast in seq {
113
+ let val: T = Deserialize::deserialize(
114
+ ValueDeserializer::<D::Error>::new(v_ast)
115
+ ).map_err(D::Error::custom)?;
116
+
117
+ let key = val.extract_key().clone();
118
+ if out.insert(key, val).is_some() {
119
+ return Err(D::Error::custom("duplicate key"));
120
+ }
121
+ }
122
+ Ok(out)
123
+ }
124
+
125
+ _ => Err(D::Error::custom("expected mapping or sequence")),
126
+ }
127
+ }
128
+
129
+ #[cfg(feature = "serde")]
130
+ pub fn deserialize_inlined_dict_map_optional<'de, D, T>(
131
+ de: D,
132
+ ) -> Result<Option<HashMap<T::Key, T>>, D::Error>
133
+ where
134
+ D: Deserializer<'de>,
135
+ T: InlinedPair + Deserialize<'de>,
136
+ {
137
+ let ast: Value = Value::deserialize(de)?;
138
+ match ast {
139
+ Value::Unit => Ok(None),
140
+ Value::Map(_) | Value::Seq(_) => {
141
+ let map = deserialize_inlined_dict_map(ValueDeserializer::<D::Error>::new(ast))?;
142
+ Ok(Some(map))
143
+ }
144
+ _ => Err(D::Error::custom("expected mapping, sequence, or unit")),
145
+ }
146
+ }
147
+
148
+
149
+ #[cfg(feature = "serde")]
150
+ #[allow(dead_code)]
151
+ pub fn deserialize_inlined_dict_list_optional<'de, D, T>(
152
+ de: D,
153
+ ) -> Result<Option<Vec<T>>, D::Error>
154
+ where
155
+ D: Deserializer<'de>,
156
+ T: InlinedPair + Deserialize<'de>,
157
+ {
158
+ let ast: Value = Value::deserialize(de)?;
159
+ match ast {
160
+ Value::Unit => Ok(None),
161
+ Value::Map(_) => {
162
+ let list = deserialize_inlined_dict_list(ValueDeserializer::<D::Error>::new(ast))?;
163
+ Ok(Some(list))
164
+ }
165
+ Value::Seq(seq) => {
166
+ let mut out = Vec::with_capacity(seq.len());
167
+ for v_ast in seq {
168
+ let val: T = Deserialize::deserialize(ValueDeserializer::<D::Error>::new(v_ast))
169
+ .map_err(D::Error::custom)?;
170
+ out.push(val);
171
+ }
172
+ Ok(Some(out))
173
+ }
174
+ _ => Err(D::Error::custom("expected mapping, sequence, or unit")),
175
+ }
176
+ }
177
+
178
+ pub fn deserialize_primitive_list_or_single_value<'de, D, T>(
179
+ deserializer: D
180
+ ) -> Result<Vec<T>, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> {
181
+ let ast: Value = Value::deserialize(deserializer)?;
182
+ match ast {
183
+ Value::Seq(seq) => {
184
+ seq.into_iter()
185
+ .map(|v| T::deserialize(ValueDeserializer::<D::Error>::new(v)))
186
+ .collect()
187
+ }
188
+ Value::Unit => Ok(vec![]),
189
+ other => {
190
+ let single_value: T = Deserialize::deserialize(
191
+ ValueDeserializer::<D::Error>::new(other)
192
+ ).map_err(D::Error::custom)?;
193
+ Ok(vec![single_value])
194
+ }
195
+ }
196
+ }
197
+
198
+
199
+ pub fn deserialize_primitive_list_or_single_value_optional<'de, D, T>(
200
+ deserializer: D
201
+ ) -> Result<Option<Vec<T>>, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> {
202
+ let ast: Value = Value::deserialize(deserializer)?;
203
+ match ast {
204
+ Value::Unit => Ok(None),
205
+ _ => {
206
+ let d = deserialize_primitive_list_or_single_value(ValueDeserializer::<D::Error>::new(ast))?;
207
+ Ok(Some(d))
208
+ }
209
+ }
210
+ }
211
+
212
+ #[cfg(all(feature = "serde", feature = "pyo3"))]
213
+ fn py_any_to_value(bound: &Bound<'_, PyAny>) -> PyResult<Value> {
214
+ if bound.is_none() {
215
+ return Ok(Value::Unit);
216
+ }
217
+
218
+ if let Ok(b) = bound.extract::<bool>() {
219
+ return Ok(Value::Bool(b));
220
+ }
221
+
222
+ if let Ok(i) = bound.extract::<i64>() {
223
+ return Ok(Value::I64(i));
224
+ }
225
+
226
+ if let Ok(f) = bound.extract::<f64>() {
227
+ return Ok(Value::F64(f));
228
+ }
229
+
230
+ if let Ok(s) = bound.extract::<String>() {
231
+ return Ok(Value::String(s));
232
+ }
233
+
234
+ if let Ok(dict_obj) = bound.getattr("__dict__") {
235
+ if let Ok(dict) = dict_obj.downcast::<PyDict>() {
236
+ let mut map: BTreeMap<Value, Value> = BTreeMap::new();
237
+ for (k, v) in dict.iter() {
238
+ let key_str: String = k.extract()?;
239
+ let val = py_any_to_value(&v)?;
240
+ map.insert(Value::String(key_str), val);
241
+ }
242
+ return Ok(Value::Map(map));
243
+ }
244
+ }
245
+
246
+ if let Ok(dict) = bound.downcast::<PyDict>() {
247
+ let mut map: BTreeMap<Value, Value> = BTreeMap::new();
248
+ for (k, v) in dict.iter() {
249
+ let key_str: String = k.extract()?;
250
+ let val = py_any_to_value(&v)?;
251
+ map.insert(Value::String(key_str), val);
252
+ }
253
+ return Ok(Value::Map(map));
254
+ }
255
+
256
+ if let Ok(list) = bound.downcast::<PyList>() {
257
+ let mut items = Vec::with_capacity(list.len());
258
+ for item in list.iter() {
259
+ items.push(py_any_to_value(&item)?);
260
+ }
261
+ return Ok(Value::Seq(items));
262
+ }
263
+
264
+ if let Ok(tuple) = bound.downcast::<PyTuple>() {
265
+ let mut items = Vec::with_capacity(tuple.len());
266
+ for item in tuple.iter() {
267
+ items.push(py_any_to_value(&item)?);
268
+ }
269
+ return Ok(Value::Seq(items));
270
+ }
271
+
272
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
273
+ "unsupported value type for conversion",
274
+ ))
275
+ }
276
+
277
+ #[cfg(all(feature = "serde", feature = "pyo3"))]
278
+ pub fn deserialize_py_any<'py, T>(bound: &Bound<'py, PyAny>) -> PyResult<T>
279
+ where
280
+ T: DeserializeOwned,
281
+ {
282
+ let value = py_any_to_value(bound)?;
283
+ let de = value.into_deserializer();
284
+ match serde_path_to_error::deserialize(de) {
285
+ Ok(ok) => Ok(ok),
286
+ Err(err) => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
287
+ format!("at `{}`: {}", err.path(), err.inner()),
288
+ )),
289
+ }
290
+ }
291
+
292
+ #[cfg(feature = "pyo3")]
293
+ pub struct PyValue<T>(pub T);
294
+
295
+ #[cfg(feature = "pyo3")]
296
+ impl<T> PyValue<T> {
297
+ pub fn into_inner(self) -> T {
298
+ self.0
299
+ }
300
+ }
301
+
302
+ #[cfg(all(feature = "pyo3", feature = "serde"))]
303
+ impl<'py, T> FromPyObject<'py> for PyValue<T>
304
+ where
305
+ T: DeserializeOwned,
306
+ {
307
+ fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
308
+ deserialize_py_any(ob).map(PyValue)
309
+ }
310
+ }
@@ -0,0 +1,61 @@
1
+ #[derive(Debug, Clone, PartialEq)]
2
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3
+ pub enum {{ slot_name }}_range {
4
+ {% for r in ranges %}
5
+ {{ r }}({{ r }}){% if not loop.last %},
6
+ {% endif %}
7
+ {% endfor %}
8
+ }
9
+
10
+ #[cfg(feature = "pyo3")]
11
+ impl<'py> FromPyObject<'py> for {{ slot_name }}_range {
12
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
13
+ {% for t in ranges %}
14
+ if let Ok(val) = ob.extract::<{{ t }}>() {
15
+ return Ok({{ slot_name }}_range::{{ t }}(val));
16
+ }
17
+ {%- endfor -%}
18
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
19
+ "invalid {{ slot_name }}",
20
+ ))
21
+ }
22
+ }
23
+
24
+ #[cfg(feature = "pyo3")]
25
+ impl<'py> IntoPyObject<'py> for {{ slot_name }}_range {
26
+ type Target = PyAny;
27
+ type Output = Bound<'py, Self::Target>;
28
+ type Error = PyErr;
29
+
30
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
31
+ match self {
32
+ {% for t in ranges %}
33
+ {{ slot_name }}_range::{{ t }}(val) => Ok(val.into_pyobject(py).map(move |b| <pyo3::Bound<'_, _> as Clone>::clone(&b).into_any())?),
34
+ {% endfor %}
35
+ }
36
+ }
37
+ }
38
+
39
+
40
+ #[cfg(feature = "pyo3")]
41
+ impl<'py> IntoPyObject<'py> for Box<{{ slot_name }}_range>
42
+ {
43
+ type Target = PyAny;
44
+ type Output = Bound<'py, Self::Target>;
45
+ type Error = PyErr;
46
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
47
+ (*self).into_pyobject(py).map(move |x| x.into_any())
48
+ }
49
+ }
50
+
51
+ #[cfg(feature = "pyo3")]
52
+ impl<'py> FromPyObject<'py> for Box<{{ slot_name }}_range> {
53
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
54
+ if let Ok(val) = ob.extract::<{{ slot_name }}_range>() {
55
+ return Ok(Box::new(val));
56
+ }
57
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
58
+ "invalid {{ slot_name }}",
59
+ ))
60
+ }
61
+ }
@@ -0,0 +1,75 @@
1
+ {% if special_case_enabled and name == 'Anything' %}
2
+ {% include 'anything.rs.jinja' %}
3
+ {% elif special_case_enabled and name == 'AnyValue' %}
4
+ pub type AnyValue = Anything;
5
+ {% else %}
6
+ {% if class_module %}
7
+ {{ class_module }}
8
+ {% endif %}
9
+ #[derive(Debug, Clone, PartialEq)]
10
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11
+ #[cfg_attr(feature = "pyo3", pyclass(subclass, get_all, set_all))]
12
+ {% if generate_merge %}
13
+ #[derive(Merge)]
14
+ {% endif %}
15
+ pub struct {{ name }} {
16
+ {% for property in properties %}
17
+ {% filter indent(width=4, first=True, blank=False) %}
18
+ {{ property }}
19
+ {%- if not loop.last -%},{{ '\n' }}{% else %}{{ '\n' }}{%- endif -%}
20
+ {% endfilter %}
21
+ {% endfor %}
22
+ }
23
+ {% if properties | length > 0 %}
24
+ #[cfg(feature = "pyo3")]
25
+ #[pymethods]
26
+ impl {{ name }} {
27
+ #[new]
28
+ #[pyo3(signature = ({{ constructor_signature }}))]
29
+ pub fn new({% for (n, t) in property_names_and_types %}{{ n }}: {{ t }}{% if not loop.last %}, {% endif %}{% endfor %}) -> Self {
30
+ {% for name, expr in constructor_conversions %}
31
+ let {{ name }} = {{ expr }};
32
+ {% endfor %}
33
+ {{ name }}{{ "{" }}{% for (p, _) in property_names_and_types %}{{ p }}{% if not loop.last %}, {% endif %}{% endfor %}{{ "}" }}
34
+ }
35
+ }
36
+
37
+ #[cfg(feature = "pyo3")]
38
+ impl<'py> IntoPyObject<'py> for Box<{{ name }}>
39
+ {
40
+ type Target = PyAny;
41
+ type Output = Bound<'py, Self::Target>;
42
+ type Error = PyErr;
43
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
44
+ (*self).into_pyobject(py).map(move |x| x.into_any())
45
+ }
46
+ }
47
+
48
+ #[cfg(feature = "pyo3")]
49
+ impl<'py> FromPyObject<'py> for Box<{{ name }}> {
50
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
51
+ if let Ok(val) = ob.extract::<{{ name }}>() {
52
+ return Ok(Box::new(val));
53
+ }
54
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
55
+ "invalid {{ name}}",
56
+ ))
57
+ }
58
+ }
59
+
60
+ {% if generate_merge %}
61
+ impl {{ name }} {
62
+ pub fn merge_with(&mut self, other: &{{ name }}) {
63
+ self.merge(other.clone())
64
+ }
65
+ }
66
+ {% endif %}
67
+
68
+ {% endif %}
69
+ {% if as_key_value %}
70
+ {{ as_key_value }}
71
+ {% endif %}
72
+ {% if struct_or_subtype_enum %}
73
+ {{ struct_or_subtype_enum }}
74
+ {% endif %}
75
+ {% endif %}
@@ -0,0 +1,108 @@
1
+ #[derive(Debug, Clone, PartialEq)]
2
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
3
+ {% if type_designator_field %}
4
+ #[cfg_attr(feature="serde", serde(tag = "{{ type_designator_field }}"))]
5
+ {% else %}
6
+ #[cfg_attr(feature="serde", serde(untagged))]
7
+ {% endif %}
8
+ pub enum {{ enum_name }} {
9
+ {%- for t in struct_names -%}
10
+ {% if type_designator_field %}
11
+ {% set tds = type_designators[t] %}
12
+ #[serde(rename = "{{ tds[0] }}", {% for t in tds[1:] %} alias="{{ t }}", {% endfor %} )]
13
+ {% endif %}
14
+ {{ t }}({{ t }}){% if not loop.last %}, {% endif %}
15
+ {%- endfor -%}
16
+ }
17
+
18
+ {% for t in struct_names %}
19
+ impl From<{{ t }}> for {{ enum_name }} { fn from(x: {{ t }}) -> Self { Self::{{ t }}(x) } }
20
+ {% endfor %}
21
+
22
+ #[cfg(feature = "pyo3")]
23
+ impl<'py> FromPyObject<'py> for {{ enum_name }} {
24
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
25
+ {% for t in struct_names %}
26
+ if let Ok(val) = ob.extract::<{{ t }}>() {
27
+ return Ok({{ enum_name }}::{{ t }}(val));
28
+ }
29
+ {%- endfor -%}
30
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
31
+ "invalid {{ enum_name }}",
32
+ ))
33
+ }
34
+ }
35
+
36
+ #[cfg(feature = "pyo3")]
37
+ impl<'py> IntoPyObject<'py> for {{ enum_name }} {
38
+ type Target = PyAny;
39
+ type Output = Bound<'py, Self::Target>;
40
+ type Error = PyErr;
41
+
42
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
43
+ match self {
44
+ {% for t in struct_names %}
45
+ {{ enum_name }}::{{ t }}(val) => val.into_pyobject(py).map(move |b| b.into_any()),
46
+ {% endfor %}
47
+ }
48
+ }
49
+ }
50
+
51
+
52
+ #[cfg(feature = "pyo3")]
53
+ impl<'py> IntoPyObject<'py> for Box<{{ enum_name }}>
54
+ {
55
+ type Target = PyAny;
56
+ type Output = Bound<'py, Self::Target>;
57
+ type Error = PyErr;
58
+ fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
59
+ (*self).into_pyobject(py).map(move |x| x.into_any())
60
+ }
61
+ }
62
+
63
+ #[cfg(feature = "pyo3")]
64
+ impl<'py> FromPyObject<'py> for Box<{{ enum_name }}> {
65
+ fn extract_bound(ob: &pyo3::Bound<'py, pyo3::types::PyAny>) -> pyo3::PyResult<Self> {
66
+ if let Ok(val) = ob.extract::<{{ enum_name }}>() {
67
+ return Ok(Box::new(val));
68
+ }
69
+ Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
70
+ "invalid {{ enum_name }}",
71
+ ))
72
+ }
73
+ }
74
+
75
+ {% if as_key_value %}
76
+ #[cfg(feature = "serde")]
77
+ impl serde_utils::InlinedPair for {{enum_name}} {
78
+ type Key = String;
79
+ type Value = serde_value::Value;
80
+ type Error = String;
81
+
82
+ fn from_pair_mapping(k: Self::Key, v: Self::Value) -> Result<Self, Self::Error> {
83
+ {% for t in struct_names %}
84
+ if let Ok(x) = {{t}}::from_pair_mapping(k.clone(), v.clone()) {
85
+ return Ok({{enum_name}}::{{t}}(x));
86
+ }
87
+ {% endfor %}
88
+ Err("none of the variants matched the mapping form".into())
89
+ }
90
+
91
+ fn from_pair_simple(k: Self::Key, v: Self::Value) -> Result<Self, Self::Error> {
92
+ {% for t in struct_names %}
93
+ if let Ok(x) = {{t}}::from_pair_simple(k.clone(), v.clone()) {
94
+ return Ok({{enum_name}}::{{t}}(x));
95
+ }
96
+ {% endfor %}
97
+ Err("none of the variants support the primitive form".into())
98
+ }
99
+
100
+ fn extract_key(&self) -> &Self::Key {
101
+ match self {
102
+ {% for t in struct_names %}
103
+ {{enum_name}}::{{t}}(inner) => inner.extract_key(),
104
+ {% endfor %}
105
+ }
106
+ }
107
+ }
108
+ {% endif %}
@@ -0,0 +1,13 @@
1
+ {% if attributes or description %}
2
+ #[
3
+ {%- if description -%}doc = r" {{ description | escape }}", {%- endif -%}
4
+ {%- for key, val in attributes.items() -%}
5
+ {{ key }} = "{{ val }}"{% if not loop.last %}, {% endif %}
6
+ {%- endfor -%}
7
+ ]
8
+ {%- endif -%}
9
+ pub type {{ name }} = {%- if multivalued %} Vec<{{ type_ }}>
10
+ {%- else %} {{ type_ }}{%- endif -%};
11
+ {%- if slot_range_as_union %}
12
+ {{ slot_range_as_union }}
13
+ {% endif -%}
@@ -149,8 +149,7 @@ class SQLTableGenerator(Generator):
149
149
  relative_slot_num: bool = False
150
150
  default_length_oracle: int = ORACLE_MAX_VARCHAR_LENGTH
151
151
  generate_abstract_class_ddl: bool = True
152
- autogenerate_pk_index: bool = True
153
- autogenerate_fk_index: bool = True
152
+ autogenerate_index: bool = True
154
153
 
155
154
  def serialize(self, **kwargs: dict[str, Any]) -> str:
156
155
  return self.generate_ddl(**kwargs)
@@ -235,8 +234,8 @@ class SQLTableGenerator(Generator):
235
234
  fk = sql_name(self.get_id_or_key(s.range, sv))
236
235
  args = [ForeignKey(fk)]
237
236
  field_type = self.get_sql_range(s, schema)
238
- fk_index_cond = (s.key or s.identifier) and self.autogenerate_fk_index
239
- pk_index_cond = is_pk and self.autogenerate_pk_index
237
+ fk_index_cond = (s.key or s.identifier) and self.autogenerate_index
238
+ pk_index_cond = is_pk and self.autogenerate_index
240
239
  is_index = fk_index_cond or pk_index_cond
241
240
  col = Column(
242
241
  sql_name(sn),
@@ -266,18 +265,21 @@ class SQLTableGenerator(Generator):
266
265
  sql_uc = UniqueConstraint(*sql_names)
267
266
  cols.append(sql_uc)
268
267
  # Anything that has a unique constraint should have an associated index with it
269
- uc_index_name = sql_name(cn)
270
- for name in sql_names:
271
- uc_index_name = uc_index_name + "_" + name
272
- uc_index_name = uc_index_name + "_idx"
273
- is_duplicate = self.check_duplicate_entry_names(autogenerated_item_names, sql_name(uc_index_name))
274
- if not is_duplicate:
275
- sql_names = [sql_name(uc_index_name)] + sql_names
276
- uc_index = Index(*sql_names)
277
- cols.append(uc_index)
278
- autogenerated_item_names.append(sql_name(uc_index_name))
268
+ if self.autogenerate_index:
269
+ uc_index_name = sql_name(cn)
270
+ for name in sql_names:
271
+ uc_index_name = uc_index_name + "_" + name
272
+ uc_index_name = uc_index_name + "_idx"
273
+ is_duplicate = self.check_duplicate_entry_names(
274
+ autogenerated_item_names, sql_name(uc_index_name)
275
+ )
276
+ if not is_duplicate:
277
+ sql_names = [sql_name(uc_index_name)] + sql_names
278
+ uc_index = Index(*sql_names)
279
+ cols.append(uc_index)
280
+ autogenerated_item_names.append(sql_name(uc_index_name))
279
281
  if not c.abstract or (c.abstract and self.generate_abstract_class_ddl):
280
- for tag, annotation in c.annotations.items():
282
+ for tag, annotation in sorted(c.annotations.items()):
281
283
  if tag == "index":
282
284
  value_dict = {k: annotation for k, annotation in annotation.value._items()}
283
285
  for key, value in value_dict.items():
@@ -424,7 +426,7 @@ class SQLTableGenerator(Generator):
424
426
  )
425
427
  @click.option(
426
428
  "--autogenerate_index",
427
- default=False,
429
+ default=True,
428
430
  show_default=True,
429
431
  help="Enable the creation of indexes on all columns generated",
430
432
  )