Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(types): solid content key, step entity newtypes

Lewis: May this revision serve well! <lu5a@proton.me>

author
Lewis
date (May 30, 2026, 11:45 PM +0300) commit 4359e457 parent 412575ad change-id nxxxzmto
+158 -7
+99
crates/bone-types/src/content.rs
··· 1 + use serde::de::{Error as _, Unexpected}; 2 + use serde::{Deserialize, Deserializer, Serialize, Serializer}; 3 + 4 + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 5 + pub struct SolidKey([u8; 16]); 6 + 7 + impl SolidKey { 8 + #[must_use] 9 + pub const fn from_bytes(bytes: [u8; 16]) -> Self { 10 + Self(bytes) 11 + } 12 + 13 + #[must_use] 14 + pub const fn bytes(self) -> [u8; 16] { 15 + self.0 16 + } 17 + 18 + #[must_use] 19 + pub fn hex(self) -> String { 20 + self.0 21 + .iter() 22 + .fold(String::with_capacity(32), |mut acc, &byte| { 23 + acc.push(nibble(byte >> 4)); 24 + acc.push(nibble(byte & 0x0f)); 25 + acc 26 + }) 27 + } 28 + 29 + fn parse_hex(text: &str) -> Option<Self> { 30 + let bytes = text.as_bytes(); 31 + if bytes.len() != 32 { 32 + return None; 33 + } 34 + (0..16) 35 + .try_fold([0u8; 16], |mut acc, index| { 36 + let hi = from_nibble(bytes[index * 2])?; 37 + let lo = from_nibble(bytes[index * 2 + 1])?; 38 + acc[index] = (hi << 4) | lo; 39 + Some(acc) 40 + }) 41 + .map(Self) 42 + } 43 + } 44 + 45 + const fn nibble(value: u8) -> char { 46 + match value { 47 + 0..=9 => (b'0' + value) as char, 48 + _ => (b'a' + value - 10) as char, 49 + } 50 + } 51 + 52 + const fn from_nibble(byte: u8) -> Option<u8> { 53 + match byte { 54 + b'0'..=b'9' => Some(byte - b'0'), 55 + b'a'..=b'f' => Some(byte - b'a' + 10), 56 + _ => None, 57 + } 58 + } 59 + 60 + impl core::fmt::Display for SolidKey { 61 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 62 + f.write_str(&self.hex()) 63 + } 64 + } 65 + 66 + impl Serialize for SolidKey { 67 + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { 68 + serializer.serialize_str(&self.hex()) 69 + } 70 + } 71 + 72 + impl<'de> Deserialize<'de> for SolidKey { 73 + fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { 74 + let text = String::deserialize(deserializer)?; 75 + Self::parse_hex(&text) 76 + .ok_or_else(|| D::Error::invalid_value(Unexpected::Str(&text), &"a 32-char hex digest")) 77 + } 78 + } 79 + 80 + #[cfg(test)] 81 + mod tests { 82 + use super::SolidKey; 83 + 84 + #[test] 85 + fn hex_round_trips() { 86 + let key = SolidKey::from_bytes([ 87 + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 88 + 0xee, 0xff, 89 + ]); 90 + assert_eq!(key.hex(), "00112233445566778899aabbccddeeff"); 91 + assert_eq!(SolidKey::parse_hex(&key.hex()), Some(key)); 92 + } 93 + 94 + #[test] 95 + fn parse_rejects_wrong_length_and_non_hex() { 96 + assert_eq!(SolidKey::parse_hex("abcd"), None); 97 + assert_eq!(SolidKey::parse_hex(&"z".repeat(32)), None); 98 + } 99 + }
+25 -7
crates/bone-types/src/lib.rs
··· 4 4 pub use uom::si::length::millimeter; 5 5 6 6 pub mod camera; 7 + pub mod content; 7 8 pub mod dimensioned_serde; 8 9 pub mod display; 9 10 pub mod label; ··· 13 14 pub mod step; 14 15 15 16 pub use camera::{Camera3, OrbitState, Projection, ProjectionKind, StandardView, ZoomFactor}; 17 + pub use content::SolidKey; 16 18 pub use display::{DisplayMode, ShadingModel}; 17 19 pub use label::{ 18 20 EdgeLabel, EdgeRole, FaceLabel, FaceRole, ImportOrdinal, LoopIndex, SideKind, VertexLabel, ··· 28 30 Vec2, Vec3, 29 31 }; 30 32 pub use step::{ 31 - StepEntityId, StepFileHeader, StepFileName, StepOrganization, StepOriginatingSystem, StepSchema, 33 + StepEntityId, StepEntityKind, StepFileHeader, StepFileName, StepOrganization, 34 + StepOriginatingSystem, StepSchema, 32 35 }; 33 36 34 37 #[cfg(feature = "testing")] ··· 283 286 Aabb3, Angle, AngleTolerance, AxisAngle, BodyId, BrepEdgeId, BrepFaceId, BrepLoopId, 284 287 BrepShellId, BrepVertexId, Camera3, ChordHeightTolerance, CreaseAngle, DegreesOfFreedom, 285 288 DisplayMode, DocumentId, EdgeId, EdgeLabel, EdgeRole, ExtrudeId, FaceId, FaceLabel, 286 - FaceRole, FeatureId, 287 - ImportOrdinal, Length, LoopId, LoopIndex, MeshGeneration, NodeId, OrbitState, OrientedBox3, 288 - Parameter, Plane3, Point2, Point3, PositiveLength, Projection, ProjectionKind, 289 - ShadingModel, ShellId, SideKind, SketchDimensionId, SketchEntityId, SketchId, 290 - SketchParameterId, SketchPlaneBasis, SketchRelationId, SolidId, SolverResidual, 291 - StandardView, StepEntityId, StepFileHeader, StepFileName, StepOrganization, 289 + FaceRole, FeatureId, ImportOrdinal, Length, LoopId, LoopIndex, MeshGeneration, NodeId, 290 + OrbitState, OrientedBox3, Parameter, Plane3, Point2, Point3, PositiveLength, Projection, 291 + ProjectionKind, ShadingModel, ShellId, SideKind, SketchDimensionId, SketchEntityId, 292 + SketchId, SketchParameterId, SketchPlaneBasis, SketchRelationId, SolidId, SolverResidual, 293 + StandardView, StepEntityId, StepEntityKind, StepFileHeader, StepFileName, StepOrganization, 292 294 StepOriginatingSystem, StepSchema, Tolerance, UnitVec2, UnitVec3, Vec2, Vec3, VertexId, 293 295 VertexLabel, VertexRole, WireId, ZoomFactor, degree, millimeter, radian, 294 296 }; ··· 965 967 fn step_schema_labels() { 966 968 assert_eq!(StepSchema::Ap214.label(), "AP214"); 967 969 assert_eq!(StepSchema::Ap242E2.label(), "AP242E2"); 970 + } 971 + 972 + #[test] 973 + fn step_entity_kind_labels_and_round_trip() { 974 + assert_eq!( 975 + StepEntityKind::CylindricalSurface.label(), 976 + "a cylindrical surface" 977 + ); 978 + assert_eq!(StepEntityKind::ConicCurve.label(), "a conic curve"); 979 + let Ok(text) = ron::to_string(&StepEntityKind::ToroidalSurface) else { 980 + panic!("serialize step entity kind"); 981 + }; 982 + let Ok(back) = ron::from_str::<StepEntityKind>(&text) else { 983 + panic!("deserialize step entity kind"); 984 + }; 985 + assert_eq!(StepEntityKind::ToroidalSurface, back); 968 986 } 969 987 970 988 #[test]
+34
crates/bone-types/src/step.rs
··· 61 61 } 62 62 } 63 63 64 + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] 65 + pub enum StepEntityKind { 66 + ConicCurve, 67 + PolylineCurve, 68 + ParametricCurve, 69 + SphericalSurface, 70 + CylindricalSurface, 71 + ConicalSurface, 72 + ToroidalSurface, 73 + SweptSurface, 74 + } 75 + 76 + impl StepEntityKind { 77 + #[must_use] 78 + pub const fn label(self) -> &'static str { 79 + match self { 80 + Self::ConicCurve => "a conic curve", 81 + Self::PolylineCurve => "a polyline curve", 82 + Self::ParametricCurve => "a parametric curve", 83 + Self::SphericalSurface => "a spherical surface", 84 + Self::CylindricalSurface => "a cylindrical surface", 85 + Self::ConicalSurface => "a conical surface", 86 + Self::ToroidalSurface => "a toroidal surface", 87 + Self::SweptSurface => "a swept surface", 88 + } 89 + } 90 + } 91 + 92 + impl core::fmt::Display for StepEntityKind { 93 + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 94 + f.write_str(self.label()) 95 + } 96 + } 97 + 64 98 macro_rules! string_newtype { 65 99 ($($name:ident),+ $(,)?) => { 66 100 $(