feat(proto): GroupId, Orient, n-ary LayoutNode with external-tagged serde
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,3 +16,12 @@ impl std::fmt::Display for WorkspaceId {
|
|||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct GroupId(pub String);
|
||||||
|
|
||||||
|
impl std::fmt::Display for GroupId {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use crate::ids::SurfaceId;
|
||||||
|
|
||||||
|
/// Split orientation. `H` lays children left-to-right; `V` top-to-bottom.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum Orient {
|
||||||
|
H,
|
||||||
|
V,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursive n-ary layout tree. Externally tagged so JSON reads
|
||||||
|
/// `{ "leaf": { "surface_id": "s_1" } }` / `{ "split": { ... } }`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum LayoutNode {
|
||||||
|
Leaf { surface_id: SurfaceId },
|
||||||
|
Split {
|
||||||
|
orient: Orient,
|
||||||
|
ratios: Vec<f32>,
|
||||||
|
children: Vec<LayoutNode>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutNode {
|
||||||
|
pub fn leaf(id: SurfaceId) -> Self {
|
||||||
|
LayoutNode::Leaf { surface_id: id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn leaf_serializes_externally_tagged() {
|
||||||
|
let n = LayoutNode::leaf(SurfaceId("s_1".into()));
|
||||||
|
let j = serde_json::to_string(&n).unwrap();
|
||||||
|
assert_eq!(j, r#"{"leaf":{"surface_id":"s_1"}}"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_round_trips() {
|
||||||
|
let n = LayoutNode::Split {
|
||||||
|
orient: Orient::V,
|
||||||
|
ratios: vec![0.5, 0.5],
|
||||||
|
children: vec![
|
||||||
|
LayoutNode::leaf(SurfaceId("s_1".into())),
|
||||||
|
LayoutNode::leaf(SurfaceId("s_2".into())),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let j = serde_json::to_string(&n).unwrap();
|
||||||
|
assert!(j.contains(r#""split""#));
|
||||||
|
assert!(j.contains(r#""orient":"v""#));
|
||||||
|
let back: LayoutNode = serde_json::from_str(&j).unwrap();
|
||||||
|
assert_eq!(back, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod ids;
|
pub mod ids;
|
||||||
|
pub mod layout;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
|
|
||||||
pub use ids::{SurfaceId, WorkspaceId};
|
pub use ids::{GroupId, SurfaceId, WorkspaceId};
|
||||||
|
pub use layout::{LayoutNode, Orient};
|
||||||
pub use message::{Cmd, Envelope, ErrorBody, Evt};
|
pub use message::{Cmd, Envelope, ErrorBody, Evt};
|
||||||
|
|||||||
Reference in New Issue
Block a user