#[cfg(target_arch = "wasm32")]
extern crate console_error_panic_hook;
use super::token::{RangeValue, SocketPlug, Value};
use std::fmt;
#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(target_arch = "wasm32")]
use serde::{self, Serialize};
#[cfg(not(feature = "std"))]
use alloc::{
borrow::Cow,
boxed::Box,
string::{String, ToString},
vec::Vec,
};
pub type Span = (usize, usize, usize);
#[derive(Default, Debug, PartialEq, Clone)]
#[doc(hidden)]
pub struct Comments<'a>(pub Vec<&'a str>);
impl<'a> Comments<'a> {
fn any_non_newline(&self) -> bool {
self.0.iter().any(|c| *c != "\n")
}
fn all_newline(&self) -> bool {
self.0.iter().all(|c| *c == "\n")
}
}
impl<'a> fmt::Display for Comments<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.all_newline() {
return write!(f, "");
}
let mut comment_str = String::new();
for comment in &self.0 {
if *comment == "\n" {
comment_str.push_str("\n")
} else {
comment_str.push_str(&format!(";{}\n", comment));
}
}
write!(f, "{}", comment_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Default, Debug, PartialEq)]
pub struct CDDL<'a> {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub rules: Vec<Rule<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments: Option<Comments<'a>>,
}
impl<'a> fmt::Display for CDDL<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(target_arch = "wasm32")]
console_error_panic_hook::set_once();
let mut cddl_output = String::new();
if let Some(comments) = &self.comments {
cddl_output.push_str(&comments.to_string());
}
let mut previous_single_line_type = false;
let mut previous_comments_after_rule = false;
for (idx, rule) in self.rules.iter().enumerate() {
if rule.has_comments_after_rule() {
cddl_output.push_str(&rule.to_string());
previous_comments_after_rule = true;
} else if idx == self.rules.len() - 1 || rule.has_single_line_type() {
cddl_output.push_str(&format!("{}\n", rule.to_string().trim_end()));
previous_single_line_type = true;
previous_comments_after_rule = false;
} else if previous_single_line_type && !previous_comments_after_rule {
cddl_output.push_str(&format!("\n{}\n\n", rule.to_string().trim_end()));
previous_single_line_type = false;
previous_comments_after_rule = false;
} else {
cddl_output.push_str(&format!("{}\n\n", rule.to_string().trim_end()));
previous_comments_after_rule = false;
}
}
write!(f, "{}", cddl_output)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq, Clone)]
pub struct Identifier<'a> {
pub ident: &'a str,
pub socket: Option<SocketPlug>,
pub span: Span,
}
impl<'a> fmt::Display for Identifier<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(sp) = &self.socket {
return write!(f, "{}{}", sp, self.ident);
}
write!(f, "{}", self.ident)
}
}
impl<'a> From<&'static str> for Identifier<'a> {
fn from(ident: &'static str) -> Self {
let mut socket = ident.chars().take(2);
if let Some(c) = socket.next() {
if c == '$' {
if let Some(c) = socket.next() {
if c == '$' {
return Identifier {
ident,
socket: Some(SocketPlug::GROUP),
span: (0, 0, 0),
};
}
}
return Identifier {
ident,
socket: Some(SocketPlug::TYPE),
span: (0, 0, 0),
};
}
}
Identifier {
ident,
socket: None,
span: (0, 0, 0),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq)]
pub enum Rule<'a> {
Type {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
rule: TypeRule<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_rule: Option<Comments<'a>>,
},
Group {
rule: Box<GroupRule<'a>>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_rule: Option<Comments<'a>>,
},
}
impl<'a> Rule<'a> {
pub fn span(&self) -> Span {
match self {
Rule::Type { span, .. } => *span,
Rule::Group { span, .. } => *span,
}
}
fn has_comments_after_rule(&self) -> bool {
match self {
Rule::Type {
comments_after_rule: Some(comments),
..
}
| Rule::Group {
comments_after_rule: Some(comments),
..
} if comments.any_non_newline() => true,
_ => false,
}
}
fn has_single_line_type(&self) -> bool {
if let Rule::Type {
rule: TypeRule {
value: Type { type_choices, .. },
..
},
..
} = self
{
if type_choices.len() <= 2
&& type_choices.iter().all(|tc| match tc.type1.type2 {
Type2::Typename { .. }
| Type2::FloatValue { .. }
| Type2::IntValue { .. }
| Type2::UintValue { .. }
| Type2::TextValue { .. }
| Type2::B16ByteString { .. }
| Type2::B64ByteString { .. } => true,
_ => false,
})
{
return true;
}
}
false
}
}
impl<'a> fmt::Display for Rule<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Rule::Type {
rule,
comments_after_rule,
..
} => {
let mut rule_str = String::new();
rule_str.push_str(&format!("{}", rule));
if let Some(comments) = comments_after_rule {
if comments.any_non_newline() {
if let Some(&"\n") = comments.0.first() {
rule_str.push_str(&format!("{}", comments.to_string()));
} else {
rule_str.push_str(&format!(" {}", comments.to_string()));
}
}
}
write!(f, "{}", rule_str)
}
Rule::Group {
rule,
comments_after_rule,
..
} => {
let mut rule_str = String::new();
rule_str.push_str(&format!("{} ", rule.to_string()));
if let Some(comments) = comments_after_rule {
if comments.any_non_newline() {
if let Some(&"\n") = comments.0.first() {
rule_str.push_str(&format!("{}", comments.to_string()));
} else {
rule_str.push_str(&format!(" {}", comments.to_string()));
}
}
}
write!(f, "{}", rule_str)
}
}
}
}
impl<'a> Rule<'a> {
pub fn name(&self) -> String {
match self {
Rule::Type { rule, .. } => rule.name.to_string(),
Rule::Group { rule, .. } => rule.name.to_string(),
}
}
pub fn is_choice_alternate(&self) -> bool {
match self {
Rule::Type { rule, .. } => rule.is_type_choice_alternate,
Rule::Group { rule, .. } => rule.is_group_choice_alternate,
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq)]
pub struct TypeRule<'a> {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub name: Identifier<'a>,
pub generic_params: Option<GenericParams<'a>>,
pub is_type_choice_alternate: bool,
pub value: Type<'a>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_assignt: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_assignt: Option<Comments<'a>>,
}
impl<'a> fmt::Display for TypeRule<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut tr_output = self.name.to_string();
if let Some(gp) = &self.generic_params {
tr_output.push_str(&gp.to_string());
}
if let Some(comments) = &self.comments_before_assignt {
tr_output.push_str(&comments.to_string());
}
if self.is_type_choice_alternate {
tr_output.push_str(" /= ");
} else {
tr_output.push_str(" = ");
}
if let Some(comments) = &self.comments_after_assignt {
tr_output.push_str(&comments.to_string());
}
tr_output.push_str(&self.value.to_string());
write!(f, "{}", tr_output)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq)]
pub struct GroupRule<'a> {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub name: Identifier<'a>,
pub generic_params: Option<GenericParams<'a>>,
pub is_group_choice_alternate: bool,
pub entry: GroupEntry<'a>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_assigng: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_assigng: Option<Comments<'a>>,
}
impl<'a> fmt::Display for GroupRule<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut gr_output = self.name.to_string();
if let Some(gp) = &self.generic_params {
gr_output.push_str(&gp.to_string());
}
if let Some(comments) = &self.comments_before_assigng {
gr_output.push_str(&comments.to_string());
}
if self.is_group_choice_alternate {
gr_output.push_str(" //= ");
} else {
gr_output.push_str(" = ");
}
gr_output.push_str(&self.entry.to_string());
if let Some(comments) = &self.comments_after_assigng {
gr_output.push_str(&comments.to_string());
}
write!(f, "{}", gr_output)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq)]
pub struct GenericParams<'a> {
pub params: Vec<GenericParam<'a>>,
pub span: Span,
}
impl<'a> Default for GenericParams<'a> {
fn default() -> Self {
GenericParams {
params: Vec::new(),
span: (0, 0, 0),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq)]
pub struct GenericParam<'a> {
pub param: Identifier<'a>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_ident: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_ident: Option<Comments<'a>>,
}
impl<'a> fmt::Display for GenericParams<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut gp = String::from("<");
for (idx, parm) in self.params.iter().enumerate() {
if idx != 0 {
gp.push_str(", ");
}
if let Some(comments) = &parm.comments_before_ident {
gp.push_str(&comments.to_string());
}
gp.push_str(&parm.param.to_string());
if let Some(comments) = &parm.comments_after_ident {
gp.push_str(&comments.to_string());
}
}
gp.push('>');
write!(f, "{}", gp)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct GenericArgs<'a> {
pub args: Vec<GenericArg<'a>>,
pub span: Span,
}
impl<'a> GenericArgs<'a> {
pub fn default() -> Self {
GenericArgs {
args: Vec::new(),
span: (0, 0, 0),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct GenericArg<'a> {
pub arg: Box<Type1<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_type: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_type: Option<Comments<'a>>,
}
impl<'a> fmt::Display for GenericArgs<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut ga = String::from("<");
for (idx, arg) in self.args.iter().enumerate() {
if idx != 0 {
ga.push_str(", ");
}
if let Some(comments) = &arg.comments_before_type {
ga.push_str(&comments.to_string());
}
ga.push_str(&arg.arg.to_string());
if let Some(comments) = &arg.comments_after_type {
ga.push_str(&comments.to_string());
}
}
ga.push('>');
write!(f, "{}", ga)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Type<'a> {
pub type_choices: Vec<TypeChoice<'a>>,
pub span: Span,
}
impl<'a> Type<'a> {
#[doc(hidden)]
pub fn comments_after_type(&mut self) -> Option<Comments<'a>> {
if let Some(TypeChoice {
type1: Type1 {
comments_after_type,
..
},
..
}) = self.type_choices.last_mut()
{
if let Some(comments) = comments_after_type {
if comments.any_non_newline() {
return comments_after_type.take();
}
}
}
None
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct TypeChoice<'a> {
pub type1: Type1<'a>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_type: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_type: Option<Comments<'a>>,
}
impl<'a> fmt::Display for Type<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut type_str = String::new();
for (idx, tc) in self.type_choices.iter().enumerate() {
if idx == 0 {
type_str.push_str(&tc.type1.to_string());
if let Some(comments) = &tc.comments_after_type {
type_str.push_str(&comments.to_string().trim_end());
}
continue;
}
if let Some(comments) = &tc.comments_before_type {
type_str.push_str(&comments.to_string());
}
if self.type_choices.len() > 2 {
type_str.push_str(&format!("\n\t/ {}", tc.type1.to_string()));
} else {
type_str.push_str(&format!(" / {}", tc.type1.to_string()));
}
if let Some(comments) = &tc.comments_after_type {
type_str.push_str(&comments.to_string());
}
}
write!(f, "{}", type_str)
}
}
impl<'a> Type<'a> {
#[allow(clippy::type_complexity)]
pub fn groupname_entry(&self) -> Option<(Identifier<'a>, Option<GenericArgs<'a>>, Span)> {
if self.type_choices.len() == 1 {
if let Some(tc) = self.type_choices.first() {
if tc.type1.operator.is_none() {
if let Type2::Typename {
ident,
generic_args,
span,
} = &tc.type1.type2
{
return Some((ident.clone(), generic_args.clone(), *span));
}
}
}
}
None
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Type1<'a> {
pub type2: Type2<'a>,
pub operator: Option<Operator<'a>>,
pub span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_type: Option<Comments<'a>>,
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Operator<'a> {
pub operator: RangeCtlOp<'a>,
pub type2: Type2<'a>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_operator: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_after_operator: Option<Comments<'a>>,
}
impl<'a> fmt::Display for Type1<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut t1_str = String::new();
t1_str.push_str(&self.type2.to_string());
if let Type2::Typename { .. } = self.type2 {
if self.operator.is_some() {
t1_str.push_str(" ");
}
}
if let Some(o) = &self.operator {
if let Some(comments) = &o.comments_before_operator {
t1_str.push_str(&comments.to_string());
}
t1_str.push_str(&o.operator.to_string());
if let Some(comments) = &o.comments_after_operator {
t1_str.push_str(&comments.to_string());
}
if let Type2::Typename { .. } = self.type2 {
t1_str.push_str(" ");
}
t1_str.push_str(&o.type2.to_string());
} else if let Some(comments) = &self.comments_after_type {
if comments.any_non_newline() {
t1_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", t1_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, PartialEq, Clone)]
pub enum RangeCtlOp<'a> {
RangeOp {
is_inclusive: bool,
span: Span,
},
CtlOp {
ctrl: &'a str,
span: Span,
},
}
impl<'a> fmt::Display for RangeCtlOp<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RangeCtlOp::RangeOp {
is_inclusive: false,
..
} => write!(f, "..."),
RangeCtlOp::RangeOp {
is_inclusive: true, ..
} => write!(f, ".."),
RangeCtlOp::CtlOp { ctrl, .. } => write!(f, "{}", ctrl),
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum Type2<'a> {
IntValue {
value: isize,
span: Span,
},
UintValue {
value: usize,
span: Span,
},
FloatValue {
value: f64,
span: Span,
},
TextValue {
value: &'a str,
span: Span,
},
UTF8ByteString {
value: Cow<'a, [u8]>,
span: Span,
},
B16ByteString {
value: Cow<'a, [u8]>,
span: Span,
},
B64ByteString {
value: Cow<'a, [u8]>,
span: Span,
},
Typename {
ident: Identifier<'a>,
generic_args: Option<GenericArgs<'a>>,
span: Span,
},
ParenthesizedType {
pt: Type<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_type: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_type: Option<Comments<'a>>,
},
Map {
group: Group<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_group: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_group: Option<Comments<'a>>,
},
Array {
group: Group<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_group: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_group: Option<Comments<'a>>,
},
Unwrap {
ident: Identifier<'a>,
generic_args: Option<GenericArgs<'a>>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments: Option<Comments<'a>>,
},
ChoiceFromInlineGroup {
group: Group<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_group: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_group: Option<Comments<'a>>,
},
ChoiceFromGroup {
ident: Identifier<'a>,
generic_args: Option<GenericArgs<'a>>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments: Option<Comments<'a>>,
},
TaggedData {
tag: Option<usize>,
t: Type<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_type: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_type: Option<Comments<'a>>,
},
TaggedDataMajorType {
mt: u8,
constraint: Option<usize>,
span: Span,
},
Any(Span),
}
#[allow(clippy::cognitive_complexity)]
impl<'a> fmt::Display for Type2<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Type2::IntValue { value, .. } => write!(f, "{}", value),
Type2::UintValue { value, .. } => write!(f, "{}", value),
Type2::FloatValue { value, .. } => write!(f, "{}", value),
Type2::TextValue { value, .. } => write!(f, "\"{}\"", value),
Type2::UTF8ByteString { value, .. } => write!(
f,
"'{}'",
std::str::from_utf8(value).map_err(|_| fmt::Error)?
),
Type2::B16ByteString { value, .. } => {
write!(f, "{}", std::str::from_utf8(value).map_err(|_| fmt::Error)?)
}
Type2::B64ByteString { value, .. } => {
write!(f, "{}", std::str::from_utf8(value).map_err(|_| fmt::Error)?)
}
Type2::Typename {
ident,
generic_args,
..
} => {
if let Some(args) = generic_args {
return write!(f, "{}{}", ident, args);
}
write!(f, "{}", ident)
}
Type2::ParenthesizedType {
comments_before_type,
pt,
comments_after_type,
..
} => {
let mut pt_str = String::from("(");
if let Some(comments) = comments_before_type {
if comments.any_non_newline() {
pt_str.push_str(&format!(" {}\t", comments));
pt_str.push_str(&pt.to_string().trim_start().to_string());
} else {
pt_str.push_str(&pt.to_string());
}
} else {
pt_str.push_str(&pt.to_string());
}
if let Some(comments) = comments_after_type {
pt_str.push_str(&comments.to_string());
}
pt_str.push_str(")");
write!(f, "{}", pt_str)
}
Type2::Map {
comments_before_group,
group,
comments_after_group,
..
} => {
let mut t2_str = String::from("{");
let mut non_newline_comments_before_group = false;
if let Some(comments) = comments_before_group {
if comments.any_non_newline() {
non_newline_comments_before_group = true;
t2_str.push_str(&format!(" {}\t", comments));
t2_str.push_str(&group.to_string().trim_start().to_string());
} else {
t2_str.push_str(&group.to_string());
}
} else {
t2_str.push_str(&group.to_string());
}
if let Some(comments) = comments_after_group {
t2_str.push_str(&comments.to_string());
}
if non_newline_comments_before_group
&& !group
.group_choices
.iter()
.any(|gc| gc.has_entries_with_trailing_comments())
{
t2_str.push_str("\n");
}
t2_str.push_str("}");
write!(f, "{}", t2_str)
}
Type2::Array {
comments_before_group,
group,
comments_after_group,
..
} => {
let mut t2_str = String::from("[");
let mut non_newline_comments_before_group = false;
if let Some(comments) = comments_before_group {
if comments.any_non_newline() {
non_newline_comments_before_group = true;
t2_str.push_str(&format!(" {}\t", comments));
t2_str.push_str(&group.to_string().trim_start().to_string());
} else {
t2_str.push_str(&group.to_string());
}
} else {
t2_str.push_str(&group.to_string());
}
if let Some(comments) = comments_after_group {
t2_str.push_str(&comments.to_string());
}
if non_newline_comments_before_group
&& !group
.group_choices
.iter()
.any(|gc| gc.has_entries_with_trailing_comments())
{
t2_str.push_str("\n");
}
t2_str.push_str("]");
write!(f, "{}", t2_str)
}
Type2::Unwrap {
comments,
ident,
generic_args,
..
} => {
let mut t2_str = String::new();
if let Some(comments) = comments {
t2_str.push_str(&comments.to_string());
}
if let Some(args) = generic_args {
t2_str.push_str(&format!("{}{}", ident, args));
} else {
t2_str.push_str(&ident.to_string());
}
write!(f, "{}", t2_str)
}
Type2::ChoiceFromInlineGroup {
comments,
comments_before_group,
group,
comments_after_group,
..
} => {
let mut t2_str = String::from("&");
if let Some(comments) = comments {
t2_str.push_str(&comments.to_string());
}
t2_str.push_str("(");
if let Some(comments) = comments_before_group {
t2_str.push_str(&comments.to_string());
}
t2_str.push_str(&group.to_string());
if let Some(comments) = comments_after_group {
t2_str.push_str(&comments.to_string());
}
if group.group_choices.len() == 1 && group.group_choices[0].group_entries.len() == 1 {
t2_str.push_str(" )");
} else {
t2_str.push_str(")");
}
write!(f, "{}", t2_str)
}
Type2::ChoiceFromGroup {
comments,
ident,
generic_args,
..
} => {
let mut t2_str = String::from("&");
if let Some(comments) = comments {
t2_str.push_str(&comments.to_string());
}
if let Some(ga) = generic_args {
t2_str.push_str(&format!("{}{}", ident, ga));
} else {
t2_str.push_str(&ident.to_string());
}
write!(f, "{}", t2_str)
}
Type2::TaggedData {
tag,
comments_before_type,
t,
comments_after_type,
..
} => {
let mut t2_str = String::from("#6");
if let Some(tag_uint) = tag {
t2_str.push_str(&format!(".{}", tag_uint));
}
t2_str.push_str("(");
if let Some(comments) = comments_before_type {
if comments.any_non_newline() {
t2_str.push_str(&format!(" {}", comments));
}
}
t2_str.push_str(&t.to_string());
if let Some(comments) = comments_after_type {
if comments.any_non_newline() {
t2_str.push_str(&format!(" {}", comments));
}
}
t2_str.push_str(")");
write!(f, "{}", t2_str)
}
Type2::TaggedDataMajorType { mt, constraint, .. } => {
if let Some(c) = constraint {
return write!(f, "{}.{}", mt, c);
}
write!(f, "{}", mt)
}
Type2::Any(_) => write!(f, "#"),
}
}
}
impl<'a> From<RangeValue<'a>> for Type2<'a> {
fn from(rv: RangeValue<'a>) -> Self {
let span = (0, 0, 0);
match rv {
RangeValue::IDENT(ident) => Type2::Typename {
ident: Identifier {
ident: ident.0,
socket: ident.1,
span,
},
generic_args: None,
span,
},
RangeValue::INT(value) => Type2::IntValue { value, span },
RangeValue::UINT(value) => Type2::UintValue { value, span },
RangeValue::FLOAT(value) => Type2::FloatValue { value, span },
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Group<'a> {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub group_choices: Vec<GroupChoice<'a>>,
pub span: Span,
}
impl<'a> fmt::Display for Group<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut group_str = String::new();
for (idx, gc) in self.group_choices.iter().enumerate() {
let mut gc_str = gc.to_string();
if self.group_choices.len() > 2
&& gc.group_entries.len() <= 3
&& !gc.has_entries_with_comments_before_comma()
{
gc_str = gc_str.replace('\n', "");
}
if idx == 0 {
if self.group_choices.len() > 2 && gc.group_entries.len() <= 3 {
group_str.push_str("\n\t");
if gc_str.ends_with(' ') {
gc_str.pop();
}
if self.group_choices.len() > 2 && gc.has_entries_with_comments_before_comma() {
gc_str = gc_str.replace('\n', "\n\t\t");
group_str.push_str(gc_str.trim());
} else {
group_str.push_str(gc_str.trim_start());
}
} else {
group_str.push_str(&gc.to_string());
}
if self.group_choices.len() > 2 && gc.group_entries.len() <= 3 {
group_str.push_str("\n");
}
continue;
}
gc_str = gc_str.trim().to_string();
if self.group_choices.len() > 2 && gc.has_entries_with_comments_before_comma() {
gc_str = gc_str.replace('\n', "\n\t\t");
}
if self.group_choices.len() <= 2 {
group_str.push_str(&format!("// {} ", gc_str));
} else {
group_str.push_str(&format!("\t// {}\n", gc_str));
}
}
write!(f, "{}", group_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct GroupChoice<'a> {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub group_entries: Vec<(GroupEntry<'a>, OptionalComma<'a>)>,
pub span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments_before_grpchoice: Option<Comments<'a>>,
}
impl<'a> GroupChoice<'a> {
fn has_entries_with_comments_before_comma(&self) -> bool {
for ge in self.group_entries.iter() {
if let GroupEntry::ValueMemberKey { ge: vmke, .. } = &ge.0 {
if vmke
.entry_type
.type_choices
.iter()
.any(|tc| tc.type1.comments_after_type.is_some())
&& ge.1.optional_comma
{
return true;
}
}
if let GroupEntry::TypeGroupname {
trailing_comments, ..
} = &ge.0
{
if trailing_comments.is_some() && ge.1.optional_comma {
return true;
}
}
}
false
}
fn has_entries_with_trailing_comments(&self) -> bool {
self
.group_entries
.iter()
.any(|ge| ge.0.has_trailing_comments())
}
}
impl<'a> fmt::Display for GroupChoice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut gc_str = String::new();
if self.group_entries.len() == 1 {
gc_str.push_str(&format!(
" {}{}",
self.group_entries[0].0, self.group_entries[0].1
));
if self.group_entries[0].1.trailing_comments.is_none() {
gc_str.push_str(" ");
}
return write!(f, "{}", gc_str);
}
if let Some(comments) = &self.comments_before_grpchoice {
gc_str.push_str(&comments.to_string());
}
let mut entries_with_comment_before_comma: Vec<(usize, bool)> = Vec::new();
for (idx, ge) in self.group_entries.iter().enumerate() {
if let GroupEntry::ValueMemberKey {
trailing_comments: Some(comments),
..
} = &ge.0
{
if comments.any_non_newline() && ge.1.optional_comma {
entries_with_comment_before_comma.push((idx, true));
continue;
}
}
if let GroupEntry::TypeGroupname {
trailing_comments: Some(comments),
..
} = &ge.0
{
if comments.any_non_newline() && ge.1.optional_comma {
entries_with_comment_before_comma.push((idx, true));
continue;
}
}
entries_with_comment_before_comma.push((idx, false));
}
if self.group_entries.len() > 3 {
gc_str.push_str("\n");
} else {
gc_str.push_str(" ");
}
for (idx, ge) in self.group_entries.iter().enumerate() {
if self.group_entries.len() > 3 {
gc_str.push_str("\t");
}
if entries_with_comment_before_comma.iter().any(|e| e.1) {
if idx == 0 {
if entries_with_comment_before_comma[idx].1 {
gc_str.push_str(&format!("{}", ge.0));
} else {
gc_str.push_str(&format!("{}\n", ge.0));
}
} else if entries_with_comment_before_comma[idx].1 {
gc_str.push_str(&format!(", {}", ge.0));
} else if idx != self.group_entries.len() - 1 {
gc_str.push_str(&format!(", {}\n", ge.0.to_string().trim_end()));
} else {
gc_str.push_str(&format!(", {}", ge.0));
}
} else {
gc_str.push_str(&ge.0.to_string().trim_end().to_string());
if idx != self.group_entries.len() - 1 {
gc_str.push_str(",");
}
if self.group_entries.len() <= 3 {
gc_str.push_str(" ");
}
}
if (self.group_entries.len() > 3 && entries_with_comment_before_comma.iter().all(|e| !e.1))
|| (self.group_entries.len() > 3
&& idx == self.group_entries.len() - 1
&& !ge.0.has_trailing_comments())
{
gc_str.push_str("\n");
}
}
write!(f, "{}", gc_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum GroupEntry<'a> {
ValueMemberKey {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
ge: Box<ValueMemberKeyEntry<'a>>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
leading_comments: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
trailing_comments: Option<Comments<'a>>,
},
TypeGroupname {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
ge: TypeGroupnameEntry<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
leading_comments: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
trailing_comments: Option<Comments<'a>>,
},
InlineGroup {
occur: Option<Occurrence<'a>>,
group: Group<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_group: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_group: Option<Comments<'a>>,
},
}
impl<'a> GroupEntry<'a> {
fn has_trailing_comments(&self) -> bool {
match self {
GroupEntry::ValueMemberKey {
trailing_comments: Some(comments),
..
}
| GroupEntry::TypeGroupname {
trailing_comments: Some(comments),
..
} if comments.any_non_newline() => true,
_ => false,
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct OptionalComma<'a> {
pub optional_comma: bool,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub trailing_comments: Option<Comments<'a>>,
}
impl<'a> fmt::Display for OptionalComma<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut optcomma_str = String::new();
if self.optional_comma {
optcomma_str.push_str(",");
}
if let Some(comments) = &self.trailing_comments {
optcomma_str.push_str(&comments.to_string());
}
write!(f, "{}", optcomma_str)
}
}
impl<'a> fmt::Display for GroupEntry<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GroupEntry::ValueMemberKey {
ge,
leading_comments,
trailing_comments,
..
} => {
let mut ge_str = String::new();
if let Some(comments) = leading_comments {
ge_str.push_str(&comments.to_string());
}
ge_str.push_str(&ge.to_string());
if let Some(comments) = trailing_comments {
if comments.any_non_newline() {
ge_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", ge_str)
}
GroupEntry::TypeGroupname {
ge,
leading_comments,
trailing_comments,
..
} => {
let mut ge_str = String::new();
if let Some(comments) = leading_comments {
ge_str.push_str(&comments.to_string());
}
ge_str.push_str(&ge.to_string());
if let Some(comments) = trailing_comments {
if comments.any_non_newline() {
ge_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", ge_str)
}
GroupEntry::InlineGroup {
occur,
group,
comments_before_group,
comments_after_group,
..
} => {
let mut ge_str = String::new();
if let Some(o) = occur {
ge_str.push_str(&format!("{} ", o.occur.to_string()));
if let Some(comments) = &o.comments {
ge_str.push_str(&comments.to_string());
}
}
ge_str.push_str("(");
let mut non_newline_comments_before_group = false;
if let Some(comments) = comments_before_group {
if comments.any_non_newline() {
non_newline_comments_before_group = true;
ge_str.push_str(&format!(" {}", comments));
ge_str.push_str(&format!("\t{}", group.to_string().trim_start()));
} else {
ge_str.push_str(&group.to_string());
}
} else {
ge_str.push_str(&group.to_string());
}
if let Some(comments) = comments_after_group {
ge_str.push_str(&comments.to_string());
}
if non_newline_comments_before_group
&& !group
.group_choices
.iter()
.any(|gc| gc.has_entries_with_trailing_comments())
{
ge_str.push_str("\n");
}
ge_str.push_str(")");
write!(f, "{}", ge_str)
}
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Occurrence<'a> {
pub occur: Occur,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
pub comments: Option<Comments<'a>>,
}
impl<'a> fmt::Display for Occurrence<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut occur_str = self.occur.to_string();
if let Some(comments) = &self.comments {
occur_str.push_str(&comments.to_string());
}
write!(f, "{}", occur_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct ValueMemberKeyEntry<'a> {
pub occur: Option<Occurrence<'a>>,
pub member_key: Option<MemberKey<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub entry_type: Type<'a>,
}
impl<'a> fmt::Display for ValueMemberKeyEntry<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut vmke_str = String::new();
if let Some(o) = &self.occur {
vmke_str.push_str(&format!("{} ", o.to_string()));
}
if let Some(mk) = &self.member_key {
vmke_str.push_str(&format!("{} ", mk.to_string()));
}
vmke_str.push_str(&self.entry_type.to_string());
write!(f, "{}", vmke_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct TypeGroupnameEntry<'a> {
pub occur: Option<Occurrence<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
pub name: Identifier<'a>,
pub generic_args: Option<GenericArgs<'a>>,
}
impl<'a> fmt::Display for TypeGroupnameEntry<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut tge_str = String::new();
if let Some(o) = &self.occur {
tge_str.push_str(&format!("{} ", o.to_string()));
}
tge_str.push_str(&self.name.to_string());
if let Some(ga) = &self.generic_args {
tge_str.push_str(&ga.to_string());
}
write!(f, "{}", tge_str)
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum MemberKey<'a> {
Type1 {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
t1: Box<Type1<'a>>,
is_cut: bool,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_before_cut: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_cut: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_arrowmap: Option<Comments<'a>>,
},
Bareword {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
ident: Identifier<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_colon: Option<Comments<'a>>,
},
Value {
#[cfg_attr(target_arch = "wasm32", serde(borrow))]
value: Value<'a>,
span: Span,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments: Option<Comments<'a>>,
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
comments_after_colon: Option<Comments<'a>>,
},
#[cfg_attr(target_arch = "wasm32", serde(skip))]
#[doc(hidden)]
NonMemberKey {
non_member_key: NonMemberKey<'a>,
comments_before_type_or_group: Option<Comments<'a>>,
comments_after_type_or_group: Option<Comments<'a>>,
},
}
#[derive(Debug, Clone, PartialEq)]
#[doc(hidden)]
pub enum NonMemberKey<'a> {
Group(Group<'a>),
Type(Type<'a>),
}
impl<'a> fmt::Display for MemberKey<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MemberKey::Type1 {
t1,
comments_before_cut,
is_cut,
comments_after_cut,
comments_after_arrowmap,
..
} => {
let mut mk_str = format!("{} ", t1);
if let Some(comments) = comments_before_cut {
if comments.any_non_newline() {
mk_str.push_str(&comments.to_string());
}
}
if *is_cut {
mk_str.push_str("^ ");
}
if let Some(comments) = comments_after_cut {
if comments.any_non_newline() {
mk_str.push_str(&comments.to_string());
}
}
mk_str.push_str("=>");
if let Some(comments) = comments_after_arrowmap {
if comments.any_non_newline() {
mk_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", mk_str)
}
MemberKey::Bareword {
ident,
comments,
comments_after_colon,
..
} => {
let mut mk_str = format!("{}", ident);
if let Some(comments) = comments {
if comments.any_non_newline() {
mk_str.push_str(&format!(" {}", comments));
}
}
mk_str.push_str(":");
if let Some(comments) = comments_after_colon {
if comments.any_non_newline() {
mk_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", mk_str)
}
MemberKey::Value {
value,
comments,
comments_after_colon,
..
} => {
let mut mk_str = format!("{}", value);
if let Some(comments) = comments {
if comments.any_non_newline() {
mk_str.push_str(&format!(" {}", comments));
}
}
mk_str.push_str(":");
if let Some(comments) = comments_after_colon {
if comments.any_non_newline() {
mk_str.push_str(&format!(" {}", comments));
}
}
write!(f, "{}", mk_str)
}
MemberKey::NonMemberKey {
non_member_key: NonMemberKey::Group(g),
comments_before_type_or_group,
comments_after_type_or_group,
} => {
let mut nmk_str = String::new();
if let Some(comments) = comments_before_type_or_group {
nmk_str.push_str(&comments.to_string());
}
nmk_str.push_str(&g.to_string());
if let Some(comments) = comments_after_type_or_group {
nmk_str.push_str(&comments.to_string());
}
write!(f, "{}", nmk_str)
}
MemberKey::NonMemberKey {
non_member_key: NonMemberKey::Type(t),
comments_before_type_or_group,
comments_after_type_or_group,
} => {
let mut nmk_str = String::new();
if let Some(comments) = comments_before_type_or_group {
nmk_str.push_str(&comments.to_string());
}
nmk_str.push_str(&t.to_string());
if let Some(comments) = comments_after_type_or_group {
nmk_str.push_str(&comments.to_string());
}
write!(f, "{}", nmk_str)
}
}
}
}
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum Occur {
Exact {
lower: Option<usize>,
upper: Option<usize>,
span: Span,
},
ZeroOrMore(Span),
OneOrMore(Span),
Optional(Span),
}
impl fmt::Display for Occur {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Occur::ZeroOrMore(_) => write!(f, "*"),
Occur::Exact { lower, upper, .. } => {
if let Some(li) = lower {
if let Some(ui) = upper {
return write!(f, "{}*{}", li, ui);
}
return write!(f, "{}*", li);
}
if let Some(ui) = upper {
return write!(f, "*{}", ui);
}
write!(f, "*")
}
Occur::OneOrMore(_) => write!(f, "+"),
Occur::Optional(_) => write!(f, "?"),
}
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn verify_groupentry_output() {
assert_eq!(
GroupEntry::TypeGroupname {
ge: TypeGroupnameEntry {
occur: None,
name: Identifier::from("entry1"),
generic_args: None,
},
leading_comments: None,
trailing_comments: None,
span: (0, 0, 0),
}
.to_string(),
"entry1".to_string()
)
}
#[test]
fn verify_group_output() {
assert_eq!(
Group {
group_choices: vec![GroupChoice {
group_entries: vec![
(
GroupEntry::ValueMemberKey {
ge: Box::from(ValueMemberKeyEntry {
occur: None,
member_key: Some(MemberKey::Bareword {
ident: "key1".into(),
comments: None,
comments_after_colon: None,
span: (0, 0, 0),
}),
entry_type: Type {
type_choices: vec![TypeChoice {
type1: Type1 {
type2: Type2::TextValue {
value: "value1".into(),
span: (0, 0, 0),
},
operator: None,
comments_after_type: None,
span: (0, 0, 0),
},
comments_before_type: None,
comments_after_type: None,
}],
span: (0, 0, 0),
},
}),
leading_comments: None,
trailing_comments: None,
span: (0, 0, 0),
},
OptionalComma {
optional_comma: true,
trailing_comments: None,
}
),
(
GroupEntry::ValueMemberKey {
ge: Box::from(ValueMemberKeyEntry {
occur: None,
member_key: Some(MemberKey::Bareword {
ident: "key2".into(),
comments: None,
comments_after_colon: None,
span: (0, 0, 0),
}),
entry_type: Type {
type_choices: vec![TypeChoice {
type1: Type1 {
type2: Type2::TextValue {
value: "value2".into(),
span: (0, 0, 0),
},
operator: None,
comments_after_type: None,
span: (0, 0, 0),
},
comments_before_type: None,
comments_after_type: None,
}],
span: (0, 0, 0),
},
}),
leading_comments: None,
trailing_comments: None,
span: (0, 0, 0),
},
OptionalComma {
optional_comma: true,
trailing_comments: None,
}
),
],
comments_before_grpchoice: None,
span: (0, 0, 0),
}],
span: (0, 0, 0),
}
.to_string(),
" key1: \"value1\", key2: \"value2\" ".to_string()
)
}
}