rccn-gen 1.3.0__py3-none-any.whl → 1.3.1__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.
rccn_gen/utils.py CHANGED
@@ -6,6 +6,22 @@ from yamcs.pymdb import IntegerArgument, FloatArgument, BooleanArgument, Enumera
6
6
 
7
7
 
8
8
  def to_upper_camel_case(s):
9
+ """
10
+ Convert a string to UpperCamelCase (PascalCase).
11
+
12
+ Converts the first letter to uppercase and capitalizes each word
13
+ after spaces or underscores, removing all non-alphanumeric characters.
14
+
15
+ Parameters:
16
+ -----------
17
+ s : str
18
+ The input string to convert.
19
+
20
+ Returns:
21
+ --------
22
+ str
23
+ The converted string in UpperCamelCase.
24
+ """
9
25
  if s[0].islower():
10
26
  s = s[0].upper() + s[1:]
11
27
  if ' ' in s or '_' in s:
@@ -14,12 +30,48 @@ def to_upper_camel_case(s):
14
30
  return s
15
31
 
16
32
  def to_snake_case(s):
33
+ """
34
+ Convert a string to snake_case.
35
+
36
+ Inserts underscores before uppercase letters, converts the string to lowercase,
37
+ replaces non-alphanumeric characters with underscores, and eliminates consecutive underscores.
38
+
39
+ Parameters:
40
+ -----------
41
+ s : str
42
+ The input string to convert.
43
+
44
+ Returns:
45
+ --------
46
+ str
47
+ The converted string in snake_case.
48
+ """
17
49
  s = re.sub(r'(?<!^)(?=[A-Z])', '_', s).lower()
18
50
  s = re.sub(r'[^a-zA-Z0-9_]', '_', s)
19
51
  s = re.sub(r'__', '_', s)
20
52
  return s
21
53
 
22
54
  def replace_with_indentation(text, keyword, replacement):
55
+ """
56
+ Replace a keyword in text while preserving the original indentation.
57
+
58
+ This function finds the indentation level of the line containing the keyword
59
+ and applies the same indentation to each line of the replacement text.
60
+
61
+ Parameters:
62
+ -----------
63
+ text : str
64
+ The original text containing the keyword.
65
+ keyword : str
66
+ The keyword to replace.
67
+ replacement : str
68
+ The replacement text.
69
+
70
+ Returns:
71
+ --------
72
+ str
73
+ The text with the keyword replaced by properly indented replacement text.
74
+ """
23
75
  lines = text.split('\n')
24
76
  indent = 0
25
77
  for line in lines:
@@ -29,6 +81,26 @@ def replace_with_indentation(text, keyword, replacement):
29
81
  return text.replace(keyword, replacement.replace('\n', ('\n'+(indent*' '))))
30
82
 
31
83
  def insert_before_with_indentation(text, keyword, replacement):
84
+ """
85
+ Insert text before a keyword while preserving the original indentation.
86
+
87
+ This function finds the indentation level of the line containing the keyword
88
+ and applies the same indentation to each line of the text to be inserted.
89
+
90
+ Parameters:
91
+ -----------
92
+ text : str
93
+ The original text containing the keyword.
94
+ keyword : str
95
+ The keyword before which to insert text.
96
+ replacement : str
97
+ The text to insert before the keyword.
98
+
99
+ Returns:
100
+ --------
101
+ str
102
+ The text with the replacement inserted before the keyword with proper indentation.
103
+ """
32
104
  lines = text.split('\n')
33
105
  indent = 0
34
106
  for line in lines:
@@ -37,34 +109,146 @@ def insert_before_with_indentation(text, keyword, replacement):
37
109
  return text.replace(keyword, (replacement.replace('\n', ('\n'+(indent*' ')))+keyword))
38
110
 
39
111
  def get_keywords(text):
112
+ """
113
+ Extract all keywords from a text.
114
+
115
+ Keywords are identified as text enclosed between double angle brackets.
116
+ For example: <<KEYWORD>>
117
+
118
+ Parameters:
119
+ -----------
120
+ text : str
121
+ The text to search for keywords.
122
+
123
+ Returns:
124
+ --------
125
+ list
126
+ A list of all keywords found in the text.
127
+ """
40
128
  pattern = r'<<.*?>>'
41
129
  return re.findall(pattern, text)
42
130
 
43
131
  def get_var_keywords(text):
132
+ """
133
+ Extract variable keywords from a text.
134
+
135
+ Variable keywords are identified as text starting with <<VAR_ and ending with >>.
136
+ For example: <<VAR_NAME>>
137
+
138
+ Parameters:
139
+ -----------
140
+ text : str
141
+ The text to search for variable keywords.
142
+
143
+ Returns:
144
+ --------
145
+ list
146
+ A list of all variable keywords found in the text.
147
+ """
44
148
  pattern = r'<<VAR_.*?>>'
45
149
  return re.findall(pattern, text)
46
150
 
47
151
  def get_service_module_keywords(text):
152
+ """
153
+ Extract service module keywords from a text.
154
+
155
+ Service module keywords are identified as text starting with <<SERVICE_MODULE_ and ending with >>.
156
+ For example: <<SERVICE_MODULE_NAME>>
157
+
158
+ Parameters:
159
+ -----------
160
+ text : str
161
+ The text to search for service module keywords.
162
+
163
+ Returns:
164
+ --------
165
+ list
166
+ A list of all service module keywords found in the text.
167
+ """
48
168
  pattern = r'<<SERVICE_MODULE_.*?>>'
49
169
  return re.findall(pattern, text)
50
170
 
51
171
  def get_command_module_keywords(text):
172
+ """
173
+ Extract command module keywords from a text.
174
+
175
+ Command module keywords are identified as text starting with <<COMMAND_MODULE_ and ending with >>.
176
+ For example: <<COMMAND_MODULE_NAME>>
177
+
178
+ Parameters:
179
+ -----------
180
+ text : str
181
+ The text to search for command module keywords.
182
+
183
+ Returns:
184
+ --------
185
+ list
186
+ A list of all command module keywords found in the text.
187
+ """
52
188
  pattern = r'<<COMMAND_MODULE_.*?>>'
53
189
  return re.findall(pattern, text)
54
190
 
55
191
  def delete_all_keywords(text):
56
- keywords = get_keywords(text)
57
- for keyword in keywords:
58
- text = text.replace(keyword, '')
59
- return text
192
+ """
193
+ Remove all keywords from a text.
194
+
195
+ Finds all text enclosed in double angle brackets and removes them.
196
+
197
+ Parameters:
198
+ -----------
199
+ text : str
200
+ The text containing keywords to be removed.
201
+
202
+ Returns:
203
+ --------
204
+ str
205
+ The text with all keywords removed.
206
+ """
207
+ keywords = get_keywords(text)
208
+ for keyword in keywords:
209
+ text = text.replace(keyword, '')
210
+ return text
60
211
 
61
212
  def delete_all_command_module_keywords(text):
62
- keywords = get_command_module_keywords(text)
63
- for keyword in keywords:
64
- text = text.replace(keyword, '')
65
- return text
213
+ """
214
+ Remove all command module keywords from a text.
215
+
216
+ Finds all text starting with <<COMMAND_MODULE_ and enclosed in double angle brackets,
217
+ then removes them.
218
+
219
+ Parameters:
220
+ -----------
221
+ text : str
222
+ The text containing command module keywords to be removed.
223
+
224
+ Returns:
225
+ --------
226
+ str
227
+ The text with all command module keywords removed.
228
+ """
229
+ keywords = get_command_module_keywords(text)
230
+ for keyword in keywords:
231
+ text = text.replace(keyword, '')
232
+ return text
66
233
 
67
234
  def arg_type_to_rust(arg, bit_number_str='32'):
235
+ """
236
+ Convert a pymdb argument type to its corresponding Rust type.
237
+
238
+ Maps different argument types from pymdb to their equivalent Rust data types.
239
+
240
+ Parameters:
241
+ -----------
242
+ arg : object
243
+ A pymdb argument object (IntegerArgument, FloatArgument, etc.)
244
+ bit_number_str : str, optional
245
+ Bit width for numeric types. Default is '32'.
246
+
247
+ Returns:
248
+ --------
249
+ str
250
+ The corresponding Rust type name, or None if the type is not supported.
251
+ """
68
252
  if isinstance(arg, IntegerArgument):
69
253
  if arg.signed:
70
254
  return 'i'+bit_number_str
@@ -83,6 +267,26 @@ def arg_type_to_rust(arg, bit_number_str='32'):
83
267
  return None
84
268
 
85
269
  def arg_enum_rust_definition(arg):
270
+ """
271
+ Generate Rust enum definition from an EnumeratedArgument.
272
+
273
+ Creates a Rust enum definition with all choices from the enumerated argument.
274
+
275
+ Parameters:
276
+ -----------
277
+ arg : EnumeratedArgument
278
+ The enumerated argument to convert to a Rust enum definition.
279
+
280
+ Returns:
281
+ --------
282
+ str
283
+ A string containing the complete Rust enum definition.
284
+
285
+ Raises:
286
+ -------
287
+ ValueError
288
+ If the provided argument is not an EnumeratedArgument.
289
+ """
86
290
  if not isinstance(arg, EnumeratedArgument):
87
291
  raise ValueError('Provided Argument is not of type EnumeratedArgument.')
88
292
  definition_text = 'pub enum '+arg.name+' {\n'
@@ -92,6 +296,26 @@ def arg_enum_rust_definition(arg):
92
296
  return definition_text
93
297
 
94
298
  def engineering_bit_number(raw_bit_number):
299
+ """
300
+ Calculate an appropriate engineering bit width based on a raw bit width.
301
+
302
+ Rounds up the raw bit width to the next power of 2, with a minimum of 8 bits.
303
+
304
+ Parameters:
305
+ -----------
306
+ raw_bit_number : int
307
+ The raw bit width (1-128).
308
+
309
+ Returns:
310
+ --------
311
+ int
312
+ The calculated engineering bit width (a power of 2, minimum 8).
313
+
314
+ Raises:
315
+ -------
316
+ ValueError
317
+ If raw_bit_number is not between 1 and 128.
318
+ """
95
319
  if raw_bit_number < 1 or raw_bit_number > 128:
96
320
  raise ValueError("raw_bit_number must be between 1 and 128")
97
321
  power = 1
@@ -103,14 +327,43 @@ def engineering_bit_number(raw_bit_number):
103
327
  return (bit_number)
104
328
 
105
329
  def get_data_type(parent_classes):
106
- """Extract the data type from a list of parent class names.
107
- Returns the class name that ends with 'DataType' or None if not found."""
330
+ """
331
+ Extract the data type from a list of parent class names.
332
+
333
+ Searches through a list of class names to find one ending with 'DataType'.
334
+
335
+ Parameters:
336
+ -----------
337
+ parent_classes : list
338
+ A list of class names to search through.
339
+
340
+ Returns:
341
+ --------
342
+ str or None
343
+ The name of the class ending with 'DataType', or None if none is found.
344
+ """
108
345
  for class_name in parent_classes:
109
346
  if class_name.endswith('DataType'):
110
347
  return class_name
111
348
  return None
112
349
 
113
350
  def get_base_type(parent_classes):
351
+ """
352
+ Determine the base type from a list of parent class names.
353
+
354
+ Checks if the parent classes include common base types like Argument, Member,
355
+ Parameter, or DataType.
356
+
357
+ Parameters:
358
+ -----------
359
+ parent_classes : list
360
+ A list of class names to search through.
361
+
362
+ Returns:
363
+ --------
364
+ str or None
365
+ The base type name if found, or None if none of the target base types are found.
366
+ """
114
367
  for base_type in ["Argument", "Member", "Parameter"]:
115
368
  if base_type in parent_classes:
116
369
  return base_type
@@ -119,6 +372,32 @@ def get_base_type(parent_classes):
119
372
  return None
120
373
 
121
374
  def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
375
+ """
376
+ Generate Rust type definition code from a pymdb data instance.
377
+
378
+ This is the main function for converting pymdb data types to Rust code.
379
+ It analyzes the pymdb instance, determines its data type, and generates
380
+ appropriate Rust code including struct fields and necessary type definitions.
381
+
382
+ Parameters:
383
+ -----------
384
+ pymdb_data_instance : object
385
+ A pymdb data instance (Parameter, Argument, Member, etc.).
386
+ parent_name : str, optional
387
+ The name of the parent struct, used for inferring unnamed elements. Default is "MyStruct".
388
+
389
+ Returns:
390
+ --------
391
+ list
392
+ A list with two elements:
393
+ - [0]: The struct field definition including attributes
394
+ - [1]: Any supporting type definitions needed (like enums)
395
+
396
+ Raises:
397
+ -------
398
+ ValueError
399
+ If the data type cannot be determined or is not supported.
400
+ """
122
401
  parent_classes = list(map(lambda type: type.__name__, type(pymdb_data_instance).mro()))
123
402
  data_type = get_data_type(parent_classes)
124
403
  base_type = get_base_type(parent_classes)
@@ -138,6 +417,8 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
138
417
  definition_text = ["",""]
139
418
  if pymdb_data_instance.short_description is not None:
140
419
  definition_text[0] += ("\n\t/// "+str(pymdb_data_instance.short_description)+"\n")
420
+
421
+ # Handle IntegerDataType
141
422
  if data_type == 'IntegerDataType':
142
423
  if pymdb_data_instance.encoding is None or pymdb_data_instance.encoding.bits is None:
143
424
  raw_bit_number = 8
@@ -153,12 +434,15 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
153
434
  else:
154
435
  definition_text[0] += ("\tpub "+sc_instance_name+": u"+eng_bit_number_str+",\n")
155
436
 
437
+ # Handle BooleanDataType
156
438
  elif data_type == 'BooleanDataType':
157
439
  definition_text[0] += ("\t#[bits(1)]\n\tpub "+sc_instance_name+": bool,\n")
158
440
 
441
+ # Handle StringDataType
159
442
  elif data_type == 'StringDataType':
160
443
  definition_text[0] += "\t#[null_terminated]\n\tpub "+sc_instance_name+": String,\n"
161
444
 
445
+ # Handle ArrayDataType
162
446
  elif data_type == 'ArrayDataType':
163
447
  definition_text = rust_type_definition(pymdb_data_instance.data_type, parent_name=pymdb_data_instance.name)
164
448
  definition_text[0] = definition_text[0].replace(': ', ': Vec<').replace(',\n', '>,\n')
@@ -167,6 +451,7 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
167
451
  if pymdb_data_instance.long_description is not None:
168
452
  definition_text[1] = "/// "+pymdb_data_instance.long_description+"\n" + definition_text[1]
169
453
 
454
+ # Handle EnumeratedDataType
170
455
  elif data_type == 'EnumeratedDataType':
171
456
  definition_text[0] += "\t#[bits("+str(pymdb_data_instance.encoding.bits)+")]\n"
172
457
  definition_text[0] += "\tpub "+pymdb_data_instance.name+": "+pascalcase(pymdb_data_instance.name)+",\n"
@@ -177,6 +462,7 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
177
462
  if pymdb_data_instance.long_description is not None:
178
463
  definition_text[1] = "/// "+pymdb_data_instance.long_description+"\n" + definition_text[1]
179
464
 
465
+ # Handle AggregateDataType
180
466
  elif data_type == 'AggregateDataType':
181
467
  struct_name = pascalcase(pymdb_data_instance.name)
182
468
  definition_text[0] += "\tpub "+sc_instance_name+": "+struct_name+",\n"
@@ -193,6 +479,7 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
193
479
  definition_text[1] += "}\n\n"
194
480
  definition_text[1] += append
195
481
 
482
+ # Handle FloatDataType
196
483
  elif data_type == 'FloatDataType':
197
484
  if pymdb_data_instance.encoding is None or pymdb_data_instance.encoding.bits is None:
198
485
  raw_bit_number = 32
@@ -210,6 +497,9 @@ def rust_type_definition(pymdb_data_instance, parent_name="MyStruct"):
210
497
  eng_bit_number_str = str(eng_bit_number)
211
498
  definition_text[0] += "\t#[bits("+raw_bit_number_str+")]\n"
212
499
  definition_text[0] += ("\tpub "+sc_instance_name+": f"+eng_bit_number_str+",\n")
500
+
501
+ # Handle unsupported data types
213
502
  else:
214
503
  definition_text = ["\t// Please implement datatype "+data_type+" here.\n", ""]
504
+
215
505
  return definition_text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rccn_gen
3
- Version: 1.3.0
3
+ Version: 1.3.1
4
4
  Summary: A python based generator for RACCOON OS source files in Rust from yamcs-pymdb config files.
5
5
  Project-URL: Homepage, https://gitlab.com/rccn/pymdb_code_generation
6
6
  Project-URL: Issues, https://gitlab.com/rccn/pymdb_code_generation/issues
@@ -1,7 +1,7 @@
1
1
  rccn_gen/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
2
2
  rccn_gen/__init__.py,sha256=rBnqIw3uQk-uBbRh9VnungoTRSr2V0Bqos32xFZ44Eo,168
3
3
  rccn_gen/systems.py,sha256=NewVHJVITt3svMBkAigD23Wl3_-srjNBbiMsOqUstg4,83907
4
- rccn_gen/utils.py,sha256=qcAYBjQ_-YfUQY4QtcRX9ebHoTVXyy7xWb57vpKO6vQ,9314
4
+ rccn_gen/utils.py,sha256=q5YSmyc3qADNYcycxQJBvrG6Df8CJelL4lhXF-dN_Ms,17016
5
5
  rccn_gen/text_modules/cargo_toml/cargo.txt,sha256=AYjSo3WJE7lhOcJaiNgXP9Y-DXHDIFIt6p42rDTVNVE,427
6
6
  rccn_gen/text_modules/command/command.txt,sha256=8Y-uJilhFLoinftIbn7uKfia9LLMZno2LkoDJ-4Y-9M,345
7
7
  rccn_gen/text_modules/command/command_module_enum.txt,sha256=35sBlAV_CzQw95Uf2dNynrYOxVD2tT2XWfEvS4Zx_KY,121
@@ -14,6 +14,6 @@ rccn_gen/text_modules/mod/mod.txt,sha256=BF8LablBE4ddutdl5m0prvpvLdBRejueVOujkyr
14
14
  rccn_gen/text_modules/service/command_module_match_cmd.txt,sha256=eVGo6ltuerG37rVxpXtL-JYuLyLW4c0i6NXb5g1_U-A,89
15
15
  rccn_gen/text_modules/service/service.txt,sha256=qTxoOD5i7wH4yFiDn13rOJW9hIZyACA8W3m6UABe22U,695
16
16
  rccn_gen/text_modules/telemetry/telemetry.txt,sha256=Re1d3BfpyXT_CEe7jJzLF3MARik0-J-K98K85iPOE40,193
17
- rccn_gen-1.3.0.dist-info/METADATA,sha256=EOwcq4lhWocClSHmjO0M12G--1sPtJPfpVHY6ahdPsw,9911
18
- rccn_gen-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- rccn_gen-1.3.0.dist-info/RECORD,,
17
+ rccn_gen-1.3.1.dist-info/METADATA,sha256=xYy5MKpPdYKRgTCLHeabF2vOG_laMSP1NidCw5FiAeM,9911
18
+ rccn_gen-1.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ rccn_gen-1.3.1.dist-info/RECORD,,