#![deny(missing_docs)]
use {
crate::{error::*, formatter::*, options::*, parser::*},
std::cell::{Ref, RefCell, RefMut},
std::cmp::Ordering,
std::rc::Rc,
};
pub struct ParsedDocument {
owned_buffer: Option<String>,
filename: Option<String>,
pub content: Array,
}
impl ParsedDocument {
pub fn from_str(buffer: &str, filename: Option<String>) -> Result<Self, Error> {
Self::from_str_with_nesting_limit(buffer, filename, Parser::DEFAULT_NESTING_LIMIT)
}
pub fn from_str_with_nesting_limit(
buffer: &str,
filename: Option<String>,
nesting_limit: usize,
) -> Result<Self, Error> {
let mut parser = Parser::new(&filename);
parser.set_nesting_limit(nesting_limit);
let content = parser.parse(&buffer)?;
Ok(Self { owned_buffer: None, filename, content })
}
pub fn from_string(buffer: String, filename: Option<String>) -> Result<Self, Error> {
let mut parser = Parser::new(&filename);
let content = parser.parse(&buffer)?;
Ok(Self { owned_buffer: Some(buffer), filename, content })
}
pub fn filename(&self) -> &Option<String> {
&self.filename
}
pub fn input_buffer(&self) -> &Option<String> {
&self.owned_buffer
}
}
#[derive(Debug, Clone)]
pub enum Comment {
Block {
lines: Vec<String>,
align: bool,
},
Line(String),
Break,
}
impl Comment {
pub fn is_block(&self) -> bool {
match self {
Comment::Block { .. } => true,
_ => false,
}
}
#[allow(dead_code)] pub fn is_line(&self) -> bool {
match self {
Comment::Line(..) => true,
_ => false,
}
}
#[allow(dead_code)] pub fn is_break(&self) -> bool {
match self {
Comment::Break => true,
_ => false,
}
}
pub(crate) fn format<'a>(
&self,
formatter: &'a mut Formatter,
) -> Result<&'a mut Formatter, Error> {
match self {
Comment::Block { lines, align } => {
let len = lines.len();
for (index, line) in lines.iter().enumerate() {
let is_first = index == 0;
let is_last = index == len - 1;
if is_first {
formatter.append(&format!("/*{}", line))?;
} else if line.len() > 0 {
formatter.append(&format!("{}", line))?;
}
if !is_last {
if *align {
formatter.start_next_line()?;
} else {
formatter.append_newline()?;
}
}
}
formatter.append("*/")
}
Comment::Line(comment) => formatter.append(&format!("//{}", comment)),
Comment::Break => Ok(&mut *formatter), }?;
formatter.start_next_line()
}
}
#[derive(Clone)]
pub struct Comments {
before_value: Vec<Comment>,
end_of_line_comment: Option<String>,
}
impl Comments {
pub fn before_value(&self) -> &Vec<Comment> {
&self.before_value
}
pub fn append_end_of_line_comment(&mut self, comment: &str) -> Result<(), Error> {
let updated = match self.end_of_line_comment.take() {
None => comment.to_string(),
Some(current) => current + "\n" + comment,
};
self.end_of_line_comment = Some(updated);
Ok(())
}
pub fn end_of_line(&self) -> &Option<String> {
&self.end_of_line_comment
}
}
pub(crate) struct ContainedComments {
pending_comments: Vec<Comment>,
current_line_value: Option<Rc<RefCell<Value>>>,
end_of_line_comment_start_column: Option<usize>,
}
impl ContainedComments {
fn new() -> Self {
Self {
pending_comments: vec![],
current_line_value: None,
end_of_line_comment_start_column: None,
}
}
fn on_newline(&mut self) -> Result<(), Error> {
if self.end_of_line_comment_start_column.is_none() {
self.current_line_value = None;
}
Ok(())
}
fn add_line_comment(
&mut self,
content: &str,
start_column: usize,
pending_new_line_comment_block: bool,
) -> Result<bool, Error> {
if let Some(value_ref) = &mut self.current_line_value {
if start_column == *self.end_of_line_comment_start_column.get_or_insert(start_column) {
(*value_ref.borrow_mut()).comments_mut().append_end_of_line_comment(content)?;
return Ok(false); }
self.current_line_value = None;
}
if pending_new_line_comment_block {
self.pending_comments.push(Comment::Break);
}
self.pending_comments.push(Comment::Line(content.to_string()));
Ok(true)
}
fn add_block_comment(&mut self, comment: Comment) -> Result<(), Error> {
self.current_line_value = None;
self.pending_comments.push(comment);
Ok(())
}
fn has_pending_comments(&self) -> bool {
self.pending_comments.len() > 0
}
fn take_pending_comments(&mut self) -> Vec<Comment> {
self.pending_comments.drain(..).collect()
}
}
pub enum Value {
Primitive {
val: Primitive,
comments: Comments,
},
Array {
val: Array,
comments: Comments,
},
Object {
val: Object,
comments: Comments,
},
}
impl Value {
pub fn is_array(&self) -> bool {
match self {
Value::Array { .. } => true,
_ => false,
}
}
pub fn is_object(&self) -> bool {
match self {
Value::Object { .. } => true,
_ => false,
}
}
pub fn is_primitive(&self) -> bool {
match self {
Value::Primitive { .. } => true,
_ => false,
}
}
pub(crate) fn format<'a>(
&self,
formatter: &'a mut Formatter,
) -> Result<&'a mut Formatter, Error> {
use Value::*;
match self {
Primitive { val, .. } => val.format(formatter),
Array { val, .. } => val.format(formatter),
Object { val, .. } => val.format(formatter),
}
}
pub fn comments(&self) -> &Comments {
use Value::*;
match self {
Primitive { comments, .. } | Array { comments, .. } | Object { comments, .. } => {
comments
}
}
}
pub fn comments_mut(&mut self) -> &mut Comments {
use Value::*;
match self {
Primitive { comments, .. } | Array { comments, .. } | Object { comments, .. } => {
comments
}
}
}
pub fn has_comments(&mut self) -> bool {
let comments = self.comments();
comments.before_value().len() > 0 || comments.end_of_line().is_some()
}
}
impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Value::*;
match self {
Primitive { val, .. } => val.fmt(f),
Array { val, .. } => val.fmt(f),
Object { val, .. } => val.fmt(f),
}
}
}
pub struct Primitive {
value_string: String,
}
impl Primitive {
pub(crate) fn new(value_string: String, comments: Vec<Comment>) -> Value {
Value::Primitive {
val: Primitive { value_string },
comments: Comments { before_value: comments, end_of_line_comment: None },
}
}
#[inline]
pub fn as_str(&self) -> &str {
&self.value_string
}
fn format<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error> {
formatter.append(&self.value_string)
}
}
impl std::fmt::Debug for Primitive {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Primitive: {}", self.value_string)
}
}
pub(crate) trait Container {
fn add_value(&mut self, value: Rc<RefCell<Value>>, parser: &Parser<'_>) -> Result<(), Error>;
fn end_value(&mut self, _parser: &Parser<'_>) -> Result<(), Error>;
fn close(&mut self, _parser: &Parser<'_>) -> Result<(), Error>;
fn format_content<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error>;
fn contained_comments(&self) -> &ContainedComments;
fn contained_comments_mut(&mut self) -> &mut ContainedComments;
fn on_newline(&mut self) -> Result<(), Error> {
self.contained_comments_mut().on_newline()
}
fn add_line_comment(
&mut self,
content: &str,
start_column: usize,
pending_new_line_comment_block: bool,
) -> Result<bool, Error> {
self.contained_comments_mut().add_line_comment(
content,
start_column,
pending_new_line_comment_block,
)
}
fn add_block_comment(&mut self, comment: Comment) -> Result<(), Error> {
self.contained_comments_mut().add_block_comment(comment)
}
fn has_pending_comments(&self) -> bool {
self.contained_comments().has_pending_comments()
}
fn take_pending_comments(&mut self) -> Vec<Comment> {
self.contained_comments_mut().take_pending_comments()
}
}
pub struct Array {
items: Vec<Rc<RefCell<Value>>>,
is_parsing_value: bool,
contained_comments: ContainedComments,
}
impl Array {
pub(crate) fn new(comments: Vec<Comment>) -> Value {
Value::Array {
val: Array {
items: vec![],
is_parsing_value: false,
contained_comments: ContainedComments::new(),
},
comments: Comments { before_value: comments, end_of_line_comment: None },
}
}
pub fn items(&self) -> impl Iterator<Item = Ref<'_, Value>> {
self.items.iter().map(|rc| rc.borrow())
}
#[inline]
pub fn items_mut(&mut self) -> impl Iterator<Item = RefMut<'_, Value>> {
self.items.iter_mut().map(|rc| rc.borrow_mut())
}
#[inline]
pub fn trailing_comments(&self) -> &Vec<Comment> {
&self.contained_comments.pending_comments
}
#[inline]
pub fn trailing_comments_mut(&mut self) -> &mut Vec<Comment> {
&mut self.contained_comments.pending_comments
}
fn sort_items(&self, options: &FormatOptions) -> Vec<Rc<RefCell<Value>>> {
let mut items = self.items.clone();
if options.sort_array_items {
items.sort_by(|left, right| {
let left: &Value = &*left.borrow();
let right: &Value = &*right.borrow();
if let Value::Primitive { val: left_primitive, .. } = left {
if let Value::Primitive { val: right_primitive, .. } = right {
let mut ordering = left_primitive
.value_string
.to_lowercase()
.cmp(&right_primitive.value_string.to_lowercase());
if ordering == Ordering::Equal {
ordering =
left_primitive.value_string.cmp(&right_primitive.value_string);
}
ordering
} else {
Ordering::Equal
}
} else {
Ordering::Equal
}
});
}
items
}
fn format<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error> {
formatter.format_container("[", "]", |formatter| self.format_content(formatter))
}
}
impl Container for Array {
fn add_value(&mut self, value: Rc<RefCell<Value>>, parser: &Parser<'_>) -> Result<(), Error> {
if self.is_parsing_value {
Err(parser.error("Array items must be separated by a comma"))
} else {
self.is_parsing_value = true;
self.contained_comments.current_line_value = Some(value.clone());
self.contained_comments.end_of_line_comment_start_column = None;
self.items.push(value);
Ok(())
}
}
fn end_value(&mut self, parser: &Parser<'_>) -> Result<(), Error> {
if self.is_parsing_value {
self.is_parsing_value = false;
Ok(())
} else {
Err(parser.error("Unexpected comma without a preceding array item value"))
}
}
fn close(&mut self, _parser: &Parser<'_>) -> Result<(), Error> {
Ok(())
}
fn format_content<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error> {
let sorted_items = self.sort_items(&formatter.options_in_scope());
let len = sorted_items.len();
for (index, item) in sorted_items.iter().enumerate() {
let is_first = index == 0;
let is_last = index == len - 1;
formatter.format_item(
item,
is_first,
is_last,
self.contained_comments.has_pending_comments(),
)?;
}
formatter.format_trailing_comments(&self.contained_comments.pending_comments)
}
fn contained_comments(&self) -> &ContainedComments {
&self.contained_comments
}
fn contained_comments_mut(&mut self) -> &mut ContainedComments {
&mut self.contained_comments
}
}
impl std::fmt::Debug for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Array of {} item{}",
self.items.len(),
if self.items.len() == 1 { "" } else { "s" }
)
}
}
#[derive(Clone)]
pub struct Property {
pub(crate) name: String,
pub(crate) value: Rc<RefCell<Value>>,
}
impl Property {
pub(crate) fn new(name: String, value: Rc<RefCell<Value>>) -> Self {
Property { name, value }
}
#[inline]
pub fn name(&self) -> &str {
return &self.name;
}
#[inline]
pub fn value(&self) -> Ref<'_, Value> {
return self.value.borrow();
}
#[inline]
pub fn value_mut(&mut self) -> RefMut<'_, Value> {
return self.value.borrow_mut();
}
}
impl std::fmt::Debug for Property {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Property {}: {:?}", self.name, self.value)
}
}
pub struct Object {
pending_property_name: Option<String>,
properties: Vec<Property>,
is_parsing_property: bool,
contained_comments: ContainedComments,
}
impl Object {
pub(crate) fn new(comments: Vec<Comment>) -> Value {
Value::Object {
val: Object {
pending_property_name: None,
properties: vec![],
is_parsing_property: false,
contained_comments: ContainedComments::new(),
},
comments: Comments { before_value: comments, end_of_line_comment: None },
}
}
#[inline]
pub fn properties(&self) -> impl Iterator<Item = &Property> {
self.properties.iter()
}
#[inline]
pub fn properties_mut(&mut self) -> impl Iterator<Item = &mut Property> {
self.properties.iter_mut()
}
#[inline]
pub fn trailing_comments(&self) -> &Vec<Comment> {
&self.contained_comments.pending_comments
}
#[inline]
pub fn trailing_comments_mut(&mut self) -> &mut Vec<Comment> {
&mut self.contained_comments.pending_comments
}
pub(crate) fn set_pending_property(
&mut self,
name: String,
parser: &Parser<'_>,
) -> Result<(), Error> {
self.contained_comments.current_line_value = None;
if self.is_parsing_property {
Err(parser.error("Properties must be separated by a comma"))
} else {
self.is_parsing_property = true;
match &self.pending_property_name {
Some(property_name) => Err(Error::internal(
parser.location(),
format!(
"Unexpected property '{}' encountered before completing the previous \
property '{}'",
name, property_name
),
)),
None => {
self.pending_property_name = Some(name.to_string());
Ok(())
}
}
}
}
pub(crate) fn has_pending_property(&mut self) -> Result<bool, Error> {
Ok(self.pending_property_name.is_some())
}
fn sort_properties(&self, options: &SubpathOptions) -> Vec<Property> {
let mut properties = self.properties.clone();
properties.sort_by(|left, right| {
options
.get_property_priority(&left.name)
.cmp(&options.get_property_priority(&right.name))
});
properties
}
fn format<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error> {
formatter.format_container("{", "}", |formatter| self.format_content(formatter))
}
}
impl Container for Object {
fn add_value(&mut self, value: Rc<RefCell<Value>>, parser: &Parser<'_>) -> Result<(), Error> {
match self.pending_property_name.take() {
Some(name) => {
self.contained_comments.current_line_value = Some(value.clone());
self.contained_comments.end_of_line_comment_start_column = None;
self.properties.push(Property::new(name, value));
Ok(())
}
None => Err(parser.error("Object values require property names")),
}
}
fn end_value(&mut self, parser: &Parser<'_>) -> Result<(), Error> {
match &self.pending_property_name {
Some(property_name) => Err(parser.error(format!(
"Property '{}' must have a value before the next comma-separated property",
property_name
))),
None => {
if self.is_parsing_property {
self.is_parsing_property = false;
Ok(())
} else {
Err(parser.error("Unexpected comma without a preceding property"))
}
}
}
}
fn close(&mut self, parser: &Parser<'_>) -> Result<(), Error> {
match &self.pending_property_name {
Some(property_name) => Err(parser.error(format!(
"Property '{}' must have a value before closing an object",
property_name
))),
None => Ok(()),
}
}
fn format_content<'a>(&self, formatter: &'a mut Formatter) -> Result<&'a mut Formatter, Error> {
let sorted_properties = match formatter.get_current_subpath_options() {
Some(options) => Some(self.sort_properties(&*options.borrow())),
None => None,
};
let properties = match &sorted_properties {
Some(sorted_properties) => &sorted_properties,
None => &self.properties,
};
let len = properties.len();
for (index, property) in properties.iter().enumerate() {
let is_first = index == 0;
let is_last = index == len - 1;
formatter.format_property(
&property,
is_first,
is_last,
self.contained_comments.has_pending_comments(),
)?;
}
formatter.format_trailing_comments(&self.contained_comments.pending_comments)
}
fn contained_comments(&self) -> &ContainedComments {
&self.contained_comments
}
fn contained_comments_mut(&mut self) -> &mut ContainedComments {
&mut self.contained_comments
}
}
impl std::fmt::Debug for Object {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Object of {} propert{}",
self.properties.len(),
if self.properties.len() == 1 { "y" } else { "ies" }
)
}
}