Skip to content

Commit

Permalink
refactor(all): implement proposal toml-rs#19
Browse files Browse the repository at this point in the history
ordian committed Aug 28, 2017
1 parent a40526d commit 80ba274
Showing 17 changed files with 384 additions and 752 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -9,9 +9,16 @@


This crate allows you to parse and modify toml
documents, while preserving comments, spaces and
documents, while *mostly* preserving comments, spaces and
relative order or items.

Things it does not preserve include:
1. Spaces in headers, e.g. `[ ' a ' . b ]` will be represented as [' a '.b]
2. Children tables before parent table (tables are reordered).
3. Scattered array of tables (tables are reordered).

`toml_edit` is primarily tailored for [cargo-edit](https://github.com/killercup/cargo-edit/) needs.

## Example

```rust
@@ -20,20 +27,16 @@ extern crate toml_edit;
use toml_edit::Document;

fn main() {
let toml = r#"hello = 'toml!' # comment"#;
let toml = r#"
"hello" = 'toml!' # comment
['a'.b]
"#;
let doc = toml.parse::<Document>();
assert!(doc.is_ok());
assert_eq!(doc.unwrap().to_string(), toml);
}
```

## Caveats

Due to internal design, `mem::swap`ping two `Table`s or `ArrayOfTables` is considered harmful
(it won't do what you'd expect, see [test_safety](./tests/test_safety.rs)).

Swapping `Value`s is fine though (but you can lose formatting, e.g. comments).

## License

Licensed under either of
6 changes: 5 additions & 1 deletion fuzz/parse_document.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,11 @@ fuzz_target!(|data| {
if let Ok(data) = std::str::from_utf8(data) {
let doc = data.parse::<Document>();
if let Ok(doc) = doc {
assert_eq!(doc.to_string(), data);
let toml = doc.to_string();
let doc = toml.parse::<Document>();
assert!(doc.is_ok());
let doc = doc.unwrap();
assert_eq!(doc.to_string(), toml);
}
}
});
100 changes: 18 additions & 82 deletions src/array_of_tables.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,43 @@
use decor::*;
use table::{remove_table_recursive, Header, HeaderKind, Table};
use document::DocumentInner;
use std::fmt;
use table::Table;

/// Type representing a TOML array of tables
#[derive(Clone, Debug, Default)]
pub struct ArrayOfTables {
pub(crate) values: Vec<*mut Table>,
// Example: in [["a". b]] key is "\"a\". b"
// we need the key in order to support
// insertion into an empty array.
key: InternalString,
// We need this pointer to support
// table insertion and deletion.
doc: *mut DocumentInner,
pub(crate) values: Vec<Table>,
}

impl ArrayOfTables {
pub(crate) fn new(key: &str, doc: *mut DocumentInner) -> Self {
Self {
key: key.into(),
doc: doc,
values: Default::default(),
}
}
/// Returns an optional reference to the table
pub fn get(&self, index: usize) -> Option<&Table> {
// safe, all pointer are valid
self.values
.get(index)
.map(|t| unsafe { (*t).as_ref().unwrap() })
pub fn new() -> Self {
Default::default()
}

/// Returns an iterator over tables
pub fn iter<'a>(&'a self) -> Box<Iterator<Item = &'a Table> + 'a> {
Box::new(self.values.iter().map(|t| unsafe { &**t }))
Box::new(self.values.iter())
}

/// Returns an optional reference to the table
pub fn get(&self, index: usize) -> Option<&Table> {
self.values.get(index)
}

/// Returns an optional mutable reference to the table
pub fn get_mut(&mut self, index: usize) -> Option<&mut Table> {
// safe, all pointer are valid
self.values
.get_mut(index)
.map(|t| unsafe { (*t).as_mut().unwrap() })
self.values.get_mut(index)
}

/// Appends a new table after the last array's child,
/// or, if the array is empty, after the document.
pub fn append(&mut self) -> &mut Table {
let header = Header {
repr: Repr::new("\n", &self.key, "\n"),
kind: HeaderKind::Array,
};
let after_ptr = self.values
.iter()
.last()
.map(|p| *p as *const Table)
.unwrap_or(self.doc_mut().list.back().get().unwrap() as *const Table);
self.append_with_header(header, after_ptr)
pub fn append(&mut self, table: Table) -> &mut Table {
self.values.push(table);
let i = self.len() - 1;
self.get_mut(i).unwrap()
}

/// Removes the table at the given index.
///
/// # Panics
///
/// If `index >= self.len()`
pub fn remove(&mut self, index: usize) {
let ptr = self.values[index];
remove_table_recursive(ptr, self.doc_mut());
self.values.remove(index);
}

/// Removes all the tables
pub fn remove_all(&mut self) {
let n = self.len();
for i in (0..n).rev() {
self.remove(i);
}
pub fn clear(&mut self) {
self.values.clear()
}

pub fn len(&self) -> usize {
@@ -85,30 +47,4 @@ impl ArrayOfTables {
pub fn is_empty(&self) -> bool {
self.len() == 0
}

fn doc_mut(&mut self) -> &mut DocumentInner {
// safe, doc pointer is always valid
unsafe { self.doc.as_mut().unwrap() }
}

pub(crate) fn append_with_header(
&mut self,
header: Header,
after_ptr: *const Table,
) -> &mut Table {
let table = Table::new(header, self.doc);
let ptr = self.doc_mut().insert(table, after_ptr);
self.values.push(ptr);
let i = self.len() - 1;
self.get_mut(i).unwrap()
}
}

impl fmt::Debug for ArrayOfTables {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ArrayOfTables")
.field("key", &self.key)
.field("values", &self.values)
.finish()
}
}
79 changes: 46 additions & 33 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::fmt::{Display, Formatter, Result};
use value::{Array, DateTime, InlineTable, KeyValue, KeyValuePairs, Value};
use value::{Array, DateTime, InlineTable, KeyValue, Value};
use decor::{Formatted, Repr};
use document::Document;
use table::{Header, HeaderKind, Table};
use table::{Container, Table};
use std::cell::{Cell, RefCell};

impl Display for Repr {
fn fmt(&self, f: &mut Formatter) -> Result {
@@ -74,43 +75,59 @@ impl Display for InlineTable {
}
}

impl Display for Header {
fn fmt(&self, f: &mut Formatter) -> Result {
if self.kind == HeaderKind::Implicit {
return Ok(());
}
let brackets = if self.kind == HeaderKind::Standard {
["[", "]"]
} else {
["[[", "]]"]
};
write!(f, "{}{}", self.repr.decor.prefix, brackets[0])?;
write!(f, "{}", self.repr.raw_value)?;
write!(f, "{}{}", brackets[1], self.repr.decor.suffix)
}
struct TableFormatState<'t> {
path: RefCell<Vec<&'t str>>,
table: Cell<&'t Table>,
}

/// **Note**: It only displays Key/Value Pairs
impl Display for Table {
impl<'a> Display for TableFormatState<'a> {
fn fmt(&self, f: &mut Formatter) -> Result {
if self.header.kind != HeaderKind::Implicit {
write!(f, "{}", self.header)?;
display_table_values(f, &self.key_value_pairs)?;
let table = self.table.get();

join(f, table.key_value_pairs.values(), "\n")?;
if !table.key_value_pairs.is_empty() {
write!(f, "\n")?;
}

for &(ref name, ref c) in table.containers.values() {
match *c {
Container::Table(ref t) => {
self.path.borrow_mut().push(name);
self.table.set(t);
if !t.implicit {
write!(f, "{}[", t.decor.prefix)?;
write!(f, "{}", self.path.borrow().join("."))?;
write!(f, "]{}\n", t.decor.suffix)?;
}
write!(f, "{}", self)?;
self.table.set(table);
self.path.borrow_mut().pop();
}
Container::Array(ref a) => {
self.path.borrow_mut().push(name);
for t in &a.values {
self.table.set(t);
write!(f, "{}[[", t.decor.prefix)?;
write!(f, "{}", self.path.borrow().join("."))?;
write!(f, "]]{}\n", t.decor.suffix)?;
write!(f, "{}", self)?;
}
self.table.set(table);
self.path.borrow_mut().pop();
}
}
}
Ok(())
}
}

impl Display for Document {
fn fmt(&self, f: &mut Formatter) -> Result {
for (i, c) in self.inner.list.iter().enumerate() {
if i > 0 {
write!(f, "{}", c)?;
} else {
// root table
display_table_values(f, &c.key_value_pairs)?;
}
}
let state = TableFormatState {
path: RefCell::new(Vec::new()),
table: Cell::new(&self.root),
};
write!(f, "{}", state)?;
write!(f, "{}", self.trailing)
}
}
@@ -128,7 +145,3 @@ where
}
Ok(())
}

fn display_table_values(f: &mut Formatter, key_value_pairs: &KeyValuePairs) -> Result {
join(f, key_value_pairs.values(), "")
}
Loading
Oops, something went wrong.

0 comments on commit 80ba274

Please sign in to comment.