cu-sensor-payloads 1.0.1

Those are standardized payloads for the Copper sensors. Feel free to contribute your own.
Documentation
#[cfg(target_os = "linux")]
mod linux_only {
    use std::process::Command;
    use std::thread;

    use bincode::de::Decoder;
    use bincode::error::DecodeError;
    use bincode::{Decode, Encode};
    use cu_sensor_payloads::PointCloudSoaHandle;
    use cu29::prelude::{
        CopperList, CuMsgMetadata, CuStampedData, ErasedCuStampedData, ErasedCuStampedDataSet,
        MatchingTasks,
    };
    use serde::{Deserialize, Serialize};

    const HELPER_ENV: &str = "CU_SENSOR_POINTCLOUD_EXPORT_STACK_HELPER";
    const LARGE_POINTS: usize = 100_000;
    const SMALL_STACK_BYTES: usize = 256 * 1024;

    #[derive(Debug, Default, Encode, Serialize, Deserialize)]
    struct PointCloudMsgs(CuStampedData<PointCloudSoaHandle<LARGE_POINTS>, CuMsgMetadata>);

    impl Decode<()> for PointCloudMsgs {
        fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
            Ok(Self(Decode::decode(decoder)?))
        }
    }

    impl ErasedCuStampedDataSet for PointCloudMsgs {
        fn cumsgs(&self) -> Vec<&dyn ErasedCuStampedData> {
            vec![&self.0]
        }
    }

    impl MatchingTasks for PointCloudMsgs {
        fn get_all_task_ids() -> &'static [&'static str] {
            &["points"]
        }
    }

    fn encoded_copperlist() -> Vec<u8> {
        let mut payload = PointCloudSoaHandle::<LARGE_POINTS>::default();
        payload.with_inner_mut(|cloud| {
            cloud.len = LARGE_POINTS;
            cloud.return_order[0] = 1;
            cloud.return_order[LARGE_POINTS - 1] = 2;
        });

        let mut msgs = PointCloudMsgs::default();
        msgs.0.set_payload(payload);
        let cl = CopperList::new(7, msgs);

        bincode::encode_to_vec(cl, bincode::config::standard()).expect("encode CopperList")
    }

    fn run_json_export_on_small_stack() {
        let encoded = encoded_copperlist();
        thread::Builder::new()
            .stack_size(SMALL_STACK_BYTES)
            .spawn(move || {
                let (entry, read): (CopperList<PointCloudMsgs>, usize) =
                    bincode::decode_from_slice(&encoded, bincode::config::standard())
                        .expect("decode CopperList");
                assert_eq!(read, encoded.len());

                let json = serde_json::to_vec(&entry).expect("serialize CopperList JSON");
                assert!(!json.is_empty());
            })
            .expect("thread spawn should succeed")
            .join()
            .expect("JSON export thread should not panic");
    }

    fn run_self(test_name: &str, helper_mode: &str) -> std::process::ExitStatus {
        Command::new(std::env::current_exe().expect("current_exe should succeed"))
            .arg("--exact")
            .arg(test_name)
            .env(HELPER_ENV, helper_mode)
            .status()
            .expect("child test process should launch")
    }

    #[test]
    fn pointcloud_handle_json_export_survives_small_stack() {
        if std::env::var(HELPER_ENV).ok().as_deref() == Some("json") {
            run_json_export_on_small_stack();
            return;
        }

        let status = run_self(
            "linux_only::pointcloud_handle_json_export_survives_small_stack",
            "json",
        );
        assert!(
            status.success(),
            "JSON export child should succeed, got {status:?}"
        );
    }
}