Skip to main content

panproto_parse/
layout_policy.rs

1//! Runtime `LayoutPolicy` for the parse / decorate / emit lens.
2//!
3//! `LayoutPolicy` is a renamed re-export of
4//! [`FormatPolicy`](crate::emit_pretty::FormatPolicy) — the same
5//! configuration that the de-novo emitter consumes. Naming aligns
6//! with the put-direction terminology of the parse / decorate / emit
7//! lens: parsing erases layout; `decorate` puts it back; the policy
8//! is the put-direction complement that pins down what whitespace
9//! and indentation the put step uses when the parse-side fingerprint
10//! is absent.
11//!
12//! Its [`LayoutPolicySpec`](panproto_gat::LayoutPolicySpec) projection
13//! is the wire-serialisable form embedded in
14//! [`TheoryTransform::AddEnrichment`](panproto_gat::TheoryTransform::AddEnrichment).
15
16use panproto_gat::LayoutPolicySpec;
17
18use crate::emit_pretty::FormatPolicy;
19
20/// Runtime layout policy for `decorate` and `pretty_with_protocol`.
21///
22/// Aliased to [`FormatPolicy`]: the
23/// emitter's own policy struct is exactly the put-direction
24/// complement of the parse/emit lens, so the two are one type.
25pub type LayoutPolicy = FormatPolicy;
26
27/// Project a [`LayoutPolicy`] to its wire-serialisable form for
28/// embedding in [`TheoryTransform::AddEnrichment`](panproto_gat::TheoryTransform::AddEnrichment).
29#[must_use]
30pub fn policy_to_spec(policy: &LayoutPolicy) -> LayoutPolicySpec {
31    LayoutPolicySpec {
32        indent_width: policy.indent_width,
33        separator: policy.separator.clone(),
34        newline: policy.newline.clone(),
35        line_break_after: policy.line_break_after.clone(),
36        indent_open: policy.indent_open.clone(),
37        indent_close: policy.indent_close.clone(),
38    }
39}
40
41/// Recover a runtime [`LayoutPolicy`] from its wire-serialisable
42/// [`LayoutPolicySpec`].
43#[must_use]
44pub fn policy_from_spec(spec: &LayoutPolicySpec) -> LayoutPolicy {
45    FormatPolicy {
46        indent_width: spec.indent_width,
47        separator: spec.separator.clone(),
48        newline: spec.newline.clone(),
49        line_break_after: spec.line_break_after.clone(),
50        indent_open: spec.indent_open.clone(),
51        indent_close: spec.indent_close.clone(),
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn round_trip_through_spec() {
61        let p = LayoutPolicy::default();
62        let q = policy_from_spec(&policy_to_spec(&p));
63        assert_eq!(p.indent_width, q.indent_width);
64        assert_eq!(p.separator, q.separator);
65        assert_eq!(p.newline, q.newline);
66        assert_eq!(p.line_break_after, q.line_break_after);
67    }
68
69    #[test]
70    fn non_default_separator_round_trips() {
71        let p = LayoutPolicy {
72            separator: "\t".to_owned(),
73            newline: "\r\n".to_owned(),
74            indent_width: 4,
75            ..LayoutPolicy::default()
76        };
77        let q = policy_from_spec(&policy_to_spec(&p));
78        assert_eq!(q.separator, "\t");
79        assert_eq!(q.newline, "\r\n");
80        assert_eq!(q.indent_width, 4);
81    }
82}