Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
strategy:
fail-fast: false
matrix:
dialect: ["ardupilotmega", "asluav", "matrixpilot", "minimal", "paparazzi", "python_array_test", "standard", "test", "ualberta", "uavionix", "icarous", "common", "storm32", "csairlink", "loweheiser"]
dialect: ["ardupilotmega", "asluav", "matrixpilot", "minimal", "paparazzi", "python_array_test", "standard", "test", "ualberta", "uavionix", "icarous", "common", "storm32", "csairlink", "loweheiser", "marsh", "stemstudios"]
signing: ["", "--features signing"]
steps:
- uses: actions/checkout@v6
Expand Down
81 changes: 78 additions & 3 deletions mavlink-bindgen/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::cmp::Ordering;
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, HashSet};
use std::default::Default;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufReader, Write};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -638,7 +639,7 @@ impl MavEnum {
{
quote!(
pub fn as_bool(&self) -> bool {
self.contains(Self::MAV_BOOL_TRUE)
*self == Self::MAV_BOOL_TRUE
Comment on lines -641 to +642
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not right as self implements bitflags.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need some input on how to solve this.
The problem is that self does not implement bitflags if the dialect defines MAV_BOOL but does not use it in any messages (e.g. the standard dialect).

I think the best solution would be to default to either u8 or i8 if the type of MAV_BOOL can't be deduced from how it is used in the dialect. Then MAV_BOOL would always implement bitflags and self.contains(Self::MAV_BOOL_TRUE) would always be valid.

The only way I could see that this could cause problems is if a dialect that currently defines but not use MAV_BOOL starts using MAV_BOOL in a way that does not agree with the default.
However, I'm not sure if the current solution (not implementing bitflags at all) would be any better in that case.

I'm also open to other suggestions.

}
)
} else {
Expand Down Expand Up @@ -1499,12 +1500,29 @@ impl MavType {
}
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub enum MavDeprecationType {
#[default]
Deprecated,
Superseded,
}

impl Display for MavDeprecationType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Deprecated => f.write_str("Deprecated"),
Self::Superseded => f.write_str("Superseded"),
}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MavDeprecation {
// YYYY-MM
pub since: String,
pub replaced_by: Option<String>,
pub deprecation_type: MavDeprecationType,
pub note: Option<String>,
}

Expand All @@ -1521,11 +1539,44 @@ impl MavDeprecation {
Some(str) => format!("See `{str}`"),
None => String::new(),
};
let message = format!("{note} {replaced_by} (Deprecated since {since})");
let message = format!(
"{note} {replaced_by} ({} since {since})",
self.deprecation_type
);
quote!(#[deprecated = #message])
}
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MavSuperseded {
// YYYY-MM
pub since: String,
// maybe empty, may be encapuslated in `` and contain a wildcard
pub replaced_by: String,
pub note: Option<String>,
}

impl MavSuperseded {
pub fn emit_tokens(&self) -> TokenStream {
let since = &self.since;
let note = match &self.note {
Some(str) if str.is_empty() || str.ends_with(".") => str.clone(),
Some(str) => format!("{str}."),
None => String::new(),
};
let replaced_by = if self.replaced_by.starts_with("`") {
format!("See {}", self.replaced_by)
} else if self.replaced_by.is_empty() {
String::new()
} else {
format!("See `{}`", self.replaced_by)
};
let message = format!("{note} {replaced_by} (Superseded since {since})");
quote!(#[superseded = #message])
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type"))]
Expand All @@ -1545,6 +1596,7 @@ pub enum MavXmlElement {
Deprecated,
Wip,
Extensions,
Superseded,
}

const fn identify_element(s: &[u8]) -> Option<MavXmlElement> {
Expand All @@ -1565,6 +1617,7 @@ const fn identify_element(s: &[u8]) -> Option<MavXmlElement> {
b"deprecated" => Some(Deprecated),
b"wip" => Some(Wip),
b"extensions" => Some(Extensions),
b"superseded" => Some(Superseded),
_ => None,
}
}
Expand All @@ -1587,6 +1640,7 @@ fn is_valid_parent(p: Option<MavXmlElement>, s: MavXmlElement) -> bool {
Deprecated => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
Wip => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
Extensions => p == Some(Message),
Superseded => p == Some(Entry) || p == Some(Message) || p == Some(Enum),
}
}

Expand Down Expand Up @@ -1693,6 +1747,15 @@ pub fn parse_profile(
deprecated = Some(MavDeprecation {
replaced_by: None,
since: String::new(),
deprecation_type: MavDeprecationType::Deprecated,
note: None,
});
}
MavXmlElement::Superseded => {
deprecated = Some(MavDeprecation {
replaced_by: Some(String::new()),
since: String::new(),
deprecation_type: MavDeprecationType::Superseded,
note: None,
});
}
Expand Down Expand Up @@ -1862,6 +1925,17 @@ pub fn parse_profile(
}
_ => (),
},
Some(&MavXmlElement::Superseded) => match attr.key.into_inner() {
b"since" => {
deprecated.as_mut().unwrap().since =
String::from_utf8_lossy(&attr.value).to_string();
}
b"replaced_by" => {
deprecated.as_mut().unwrap().replaced_by =
Some(String::from_utf8_lossy(&attr.value).to_string());
}
_ => (),
},
_ => (),
}
}
Expand All @@ -1879,7 +1953,8 @@ pub fn parse_profile(
| (Some(&Version), Some(&Mavlink))
| (Some(&Dialect), Some(&Mavlink))
| (Some(&Param), Some(&Entry))
| (Some(Deprecated), _) => {
| (Some(Deprecated), _)
| (Some(Superseded), _) => {
text = Some(text.map(|t| t + s.as_ref()).unwrap_or(s.to_string()));
}
data => {
Expand Down
59 changes: 59 additions & 0 deletions mavlink-bindgen/tests/definitions/superseded.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<mavlink>
<enums>
<enum name="MAV_ROI">
<superseded since="2018-01" replaced_by="`MAV_CMD_DO_SET_ROI_*`"/>
<description>The ROI (region of interest) for the vehicle. This can be
be used by the vehicle for camera/vehicle attitude alignment (see
MAV_CMD_NAV_ROI).</description>
<entry value="0" name="MAV_ROI_NONE">
<description>No region of interest.</description>
</entry>
<entry value="1" name="MAV_ROI_WPNEXT">
<description>Point toward next waypoint, with optional pitch/roll/yaw offset.</description>
</entry>
<entry value="2" name="MAV_ROI_WPINDEX">
<description>Point toward given waypoint.</description>
</entry>
<entry value="3" name="MAV_ROI_LOCATION">
<description>Point toward fixed location.</description>
</entry>
<entry value="4" name="MAV_ROI_TARGET">
<description>Point toward of given id.</description>
</entry>
</enum>
<enum name="MAV_FRAME">
<description>Coordinate frames used by MAVLink. Not all frames are supported by all commands, messages, or vehicles.
</description>
<!-- ... -->
<entry value="5" name="MAV_FRAME_GLOBAL_INT">
<superseded since="2024-03" replaced_by="MAV_FRAME_GLOBAL">Use MAV_FRAME_GLOBAL in COMMAND_INT (and elsewhere) as a synonymous replacement.</superseded>
<description>Global (WGS84) coordinate frame (scaled) + altitude relative to mean sea level (MSL).</description>
</entry>
<!-- ... -->
</enum>
</enums>
<messages>
<message id="370" name="SMART_BATTERY_INFO">
<superseded since="2024-02" replaced_by="BATTERY_INFO">The BATTERY_INFO message is better aligned with UAVCAN messages, and in any case is useful even if a battery is not "smart".</superseded>
<description>Smart Battery information (static/infrequent update). Use for updates from: smart battery to flight stack, flight stack to GCS. Use BATTERY_STATUS for the frequent battery updates.</description>
<field type="uint8_t" name="id" instance="true">Battery ID</field>
<field type="uint8_t" name="battery_function" enum="MAV_BATTERY_FUNCTION">Function of the battery</field>
<field type="uint8_t" name="type" enum="MAV_BATTERY_TYPE">Type (chemistry) of the battery</field>
<field type="int32_t" name="capacity_full_specification" units="mAh" invalid="-1">Capacity when full according to manufacturer, -1: field not provided.</field>
<field type="int32_t" name="capacity_full" units="mAh" invalid="-1">Capacity when full (accounting for battery degradation), -1: field not provided.</field>
<field type="uint16_t" name="cycle_count" invalid="UINT16_MAX">Charge/discharge cycle count. UINT16_MAX: field not provided.</field>
<field type="char[16]" name="serial_number" invalid="[0]">Serial number in ASCII characters, 0 terminated. All 0: field not provided.</field>
<field type="char[50]" name="device_name" invalid="[0]">Static device name in ASCII characters, 0 terminated. All 0: field not provided. Encode as manufacturer name then product name separated using an underscore.</field>
<field type="uint16_t" name="weight" units="g" invalid="0">Battery weight. 0: field not provided.</field>
<field type="uint16_t" name="discharge_minimum_voltage" units="mV" invalid="UINT16_MAX">Minimum per-cell voltage when discharging. If not supplied set to UINT16_MAX value.</field>
<field type="uint16_t" name="charging_minimum_voltage" units="mV" invalid="UINT16_MAX">Minimum per-cell voltage when charging. If not supplied set to UINT16_MAX value.</field>
<field type="uint16_t" name="resting_minimum_voltage" units="mV" invalid="UINT16_MAX">Minimum per-cell voltage when resting. If not supplied set to UINT16_MAX value.</field>
<extensions/>
<field type="uint16_t" name="charging_maximum_voltage" units="mV" invalid="0">Maximum per-cell voltage when charged. 0: field not provided.</field>
<field type="uint8_t" name="cells_in_series" invalid="0">Number of battery cells in series. 0: field not provided.</field>
<field type="uint32_t" name="discharge_maximum_current" units="mA" invalid="0">Maximum pack discharge current. 0: field not provided.</field>
<field type="uint32_t" name="discharge_maximum_burst_current" units="mA" invalid="0">Maximum pack discharge burst current. 0: field not provided.</field>
<field type="char[11]" name="manufacture_date" invalid="[0]">Manufacture date (DD/MM/YYYY) in ASCII characters, 0 terminated. All 0: field not provided.</field>
</message>
</messages>
</mavlink>
5 changes: 5 additions & 0 deletions mavlink-bindgen/tests/e2e_snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ fn snapshot_deprecated() {
run_snapshot("deprecated.xml");
}

#[test]
fn snapshot_superseded() {
run_snapshot("superseded.xml");
}

#[test]
fn snapshot_no_field_description() {
run_snapshot("no_field_description.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ bitflags! { # [cfg_attr (feature = "ts" , derive (TS))] # [cfg_attr (feature = "
impl MavBool {
pub const DEFAULT: Self = Self::MAV_BOOL_FALSE;
pub fn as_bool(&self) -> bool {
self.contains(Self::MAV_BOOL_TRUE)
*self == Self::MAV_BOOL_TRUE
}
}
impl Default for MavBool {
Expand Down
14 changes: 14 additions & 0 deletions mavlink-bindgen/tests/snapshots/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: mavlink-bindgen/tests/e2e_snapshots.rs
assertion_line: 26
expression: contents
---
#[allow(non_camel_case_types)]
#[allow(clippy::derive_partial_eq_without_eq)]
#[allow(clippy::field_reassign_with_default)]
#[allow(non_snake_case)]
#[allow(clippy::unnecessary_cast)]
#[allow(clippy::bad_bit_mask)]
#[allow(clippy::suspicious_else_formatting)]
#[cfg(feature = "superseded")]
pub mod superseded;
Loading