diff --git a/src/lib.rs b/src/lib.rs index 58ea0ce4..9f167565 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -452,6 +452,8 @@ pub enum Format { MatchVariant(Expr, Vec<(Pattern, String, Format)>), /// Format generated dynamically Dynamic(DynFormat), + /// Format with human-readable description + Described(Box, String), } #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize)] @@ -462,6 +464,13 @@ pub enum DynFormat { impl Format { pub const EMPTY: Format = Format::Tuple(Vec::new()); + pub fn with_comment>(self, comment: Comment) -> Format { + Format::Described( + Box::new(self), + comment.into() + ) + } + pub fn alts>(fields: impl IntoIterator) -> Format { Format::Union( (fields.into_iter()) @@ -651,6 +660,9 @@ impl FormatModule { ("@value".to_string(), ValueType::U16), ]; Ok(ValueType::Record(ts)) + }, + Format::Described(a, _) => { + self.infer_format_type(scope, a) } } } @@ -1229,6 +1241,7 @@ impl Format { .reduce(Bounds::union) .unwrap(), Format::Dynamic(DynFormat::Huffman(_, _)) => Bounds::new(1, None), + Format::Described(f, _comment) => f.match_bounds(module), } } @@ -1263,6 +1276,7 @@ impl Format { branches.iter().any(|(_, _, f)| f.depends_on_next(module)) } Format::Dynamic(_) => false, + Format::Described(f, _) => f.depends_on_next(module), } } @@ -1457,6 +1471,7 @@ impl<'a> MatchTreeLevel<'a> { Format::Dynamic(DynFormat::Huffman(_, _)) => { self.accept(index) // FIXME } + Format::Described(f, _comment) => self.add(module, index, f, next), } } @@ -2027,6 +2042,7 @@ impl Decoder { Ok(Decoder::MatchVariant(head.clone(), branches)) } Format::Dynamic(d) => Ok(Decoder::Dynamic(d.clone())), + Format::Described(a, _comment) => Decoder::compile_next(compiler, a, next), } } diff --git a/src/output.rs b/src/output.rs index c12500d5..100133ae 100644 --- a/src/output.rs +++ b/src/output.rs @@ -233,3 +233,21 @@ impl Display for Fragment { } } } + +impl From<&'static str> for Fragment { + fn from(value: &'static str) -> Self { + Self::String(Cow::Borrowed(value)) + } +} + +impl From for Fragment { + fn from(value: String) -> Self { + Self::String(Cow::Owned(value)) + } +} + +impl From for Fragment { + fn from(value: char) -> Self { + Self::Char(value) + } +} diff --git a/src/output/flat.rs b/src/output/flat.rs index f45ac2dd..ef4c119e 100644 --- a/src/output/flat.rs +++ b/src/output/flat.rs @@ -165,6 +165,9 @@ fn check_covered( } } Format::Dynamic(_) => {} // FIXME + Format::Described(format, _comment) => { + check_covered(module, path, format)?; + } } Ok(()) } @@ -269,6 +272,11 @@ impl<'module, W: io::Write> Context<'module, W> { Ok(()) } Format::Dynamic(_) => Ok(()), // FIXME + Format::Described(f, comment) => { + self.write_flat(value, f)?; + write!(&mut self.writer, "/* {comment} */")?; + Ok(()) + } } } } diff --git a/src/output/tree.rs b/src/output/tree.rs index 6d054e00..6ddd89be 100644 --- a/src/output/tree.rs +++ b/src/output/tree.rs @@ -160,6 +160,11 @@ impl<'module, W: io::Write> Context<'module, W> { Ok(()) } Format::Dynamic(_) => self.write_value(value), + Format::Described(f, comment) => { + self.write_decoded_value(value, f)?; + write!(&mut self.writer, "/* {comment} */")?; + Ok(()) + } } } @@ -1050,6 +1055,12 @@ impl<'module> MonoidalPrinter<'module> { frag } Format::Dynamic(_) => self.compile_value(value), + Format::Described(f, comment) => { + self.compile_decoded_value(value, f) + .cat(Fragment::String("/* ".into())) + .cat(Fragment::String(comment.clone().into())) + .cat(Fragment::String(" */".into())) + } } } @@ -1748,11 +1759,18 @@ impl<'module> MonoidalPrinter<'module> { frags.finalize() } } - Format::Tuple(formats) if formats.is_empty() => Fragment::String("()".into()), - Format::Tuple(_) => Fragment::String("(...)".into()), + Format::Tuple(formats) if formats.is_empty() => Fragment::from("()"), + Format::Tuple(_) => Fragment::from("(...)"), - Format::Record(fields) if fields.is_empty() => Fragment::String("{}".into()), - Format::Record(_) => Fragment::String("{ ... }".into()), + Format::Record(fields) if fields.is_empty() => Fragment::from("{}"), + Format::Record(_) => Fragment::from("{ ... }"), + + Format::Described(f, comment) => { + self.compile_format(f, prec) + .cat("/* ".into()) + .cat(comment.clone().into()) + .cat(" */".into()) + } } } }