diff --git a/Cargo.lock b/Cargo.lock index a5ac49c1..d61f0f77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,14 @@ name = "adler32" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "aho-corasick" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "android_glue" version = "0.2.3" @@ -653,6 +661,14 @@ dependencies = [ "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itertools" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.3.4" @@ -753,8 +769,10 @@ dependencies = [ "gfx_device_gl 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_window_glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", "lyon 0.10.0", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -828,6 +846,14 @@ dependencies = [ "libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memchr" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memmap" version = "0.6.2" @@ -958,6 +984,26 @@ dependencies = [ "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "regex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rustc-demangle" version = "0.1.7" @@ -1208,11 +1254,25 @@ dependencies = [ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thread_local" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "token_store" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-segmentation" version = "1.2.1" @@ -1228,6 +1288,14 @@ name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "user32-sys" version = "0.1.3" @@ -1252,11 +1320,21 @@ dependencies = [ "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf8-ranges" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vec_map" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "wayland-client" version = "0.9.10" @@ -1468,6 +1546,7 @@ dependencies = [ [metadata] "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" +"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" "checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" @@ -1533,6 +1612,7 @@ dependencies = [ "checksum glutin 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3069192081fef59b783f0fbf824a9d2320169cb435f31fb2c91df88dc18f11ae" "checksum glutin 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "247f825056c99961b6e6e0baef17ca586a50109ab4bba2b370babe1c5d702943" "checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" +"checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" "checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ef23fcc4059260c5936f638c9805ebfc87cb172fa6661d130cba7f97d58f55" @@ -1546,6 +1626,7 @@ dependencies = [ "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum lyon_geom 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41984eddfac220b69d12c10d8666544755b21b25b633d42a2eb623ef65aa4537" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" @@ -1564,6 +1645,8 @@ dependencies = [ "checksum rctree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1817e0f0056f95bce0d6ab1a5be62ca24bd756b5547c20637ef47cc9a2065f4b" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75ecf88252dce580404a22444fc7d626c01815debba56a7f4f536772a5ff19d3" +"checksum regex-syntax 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1ac0f60d675cc6cf13a20ec076568254472551051ad5dd050364d70671bf6b" "checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" @@ -1592,13 +1675,18 @@ dependencies = [ "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum tess2-sys 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8690740ac57b47ae3e73668f7f84e0ecb1287a11344df878ae46a4771e9016d9" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum token_store 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a686838375fc11103b9c1529c6508320b7bd5e2401cd62831ca51b3e82e61849" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum user32-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6b719983b952c04198829b51653c06af36f0e44c967fcc1a2bb397ceafbf80a" "checksum usvg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29533ee2aa0d153260ddc8048a9f85c27b3a41f8feab24583a12e1a64d7aa496" +"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wayland-client 0.12.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2b90adf943117ee4930d7944fe103dcb6f36ba05421f46521cb5adbf6bf0fbc8" "checksum wayland-client 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)" = "9b10f2880f3dedaa496609a0aa7117bc6824490a48309dfbbf26258e5acc5a9d" "checksum wayland-kbd 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe0fb1c9917da9529d781659e456d84a693d74fe873d1658109758444616f76" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 102f57f1..464111f4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -17,3 +17,5 @@ gfx = "0.17.1" gfx_device_gl = "0.15.0" gfx_window_glutin = "0.20.0" glutin = "0.12.0" +itertools = "0.7.8" +regex = "1.0.0" diff --git a/cli/README.md b/cli/README.md index bbde531c..2c7c5b81 100644 --- a/cli/README.md +++ b/cli/README.md @@ -26,7 +26,7 @@ Tessellates an SVG path. The output is a vertex buffer and an index buffer in te Run ```$> lyon tessellate --help``` for more details. -### examples +### example ``` $> lyon tessellate "M 0 0 L 10 0 10 10 L 0 10 z" @@ -58,6 +58,48 @@ indices: 6 triangles: 2 ``` +To specify the output format use ```--format ```. + +There are 3 format markers, one for *vertices*, *indices* and for *triangles* (triplets of indices). Each format marker starts with ```@```, has a separator block (```{sep=...}```), a format block (```{fmt=...}``` and it ends with ```@```. In the blocks ```{``` and ```}``` characters have to be escaped! + + +#### list of format variables +- ```@vertices``` + * ```{position.x}``` or ```{pos.x}``` + * ```{position.y}``` or ```{pos.y}``` +- ```@indices``` + * ```{index}``` or ```{i}``` +- ```@triangles``` + * ```{index0}``` or ```{i0}``` + * ```{index1}``` or ```{i1}``` + * ```{index2}``` or ```{i2}``` + +#### examples + +``` +$> lyon tessellate --format "vertices: [@vertices{sep=, }{fmt=({position.x}, {position.y})}@]" "M 0 0 L 10 0 10 10 L 0 10 z" +vertices: [(0, 0), (10, 0), (0, 10), (10, 10)] + +$> lyon tessellate --format "@indices{sep=, }{fmt=[{index}]}@" "M 0 0 L 10 0 10 10 L 0 10 z" +[0], [1], [2], [2], [1], [3] + +$> lyon tessellate --format '\{\n "triangles": \{\n@triangles{sep=,\n}{fmt= "triangle": \{\n "{i0}",\n "{i1}",\n "{i2}"\n \}}@\n \}\n\}' "M 0 0 L 10 0 10 10 L 0 10 z" +{ + "triangles": { + "triangle": { + "1", + "0", + "2" + }, + "triangle": { + "1", + "2", + "3" + } + } +} +``` + ## ```show``` Opens a window with an interactive path viewer. diff --git a/cli/src/main.rs b/cli/src/main.rs index a76b7712..f9da4b9a 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -6,6 +6,8 @@ extern crate gfx; extern crate gfx_window_glutin; extern crate gfx_device_gl; extern crate glutin; +extern crate regex; +extern crate itertools; mod commands; mod tessellate; @@ -56,6 +58,13 @@ fn main() { .takes_value(true) .required(false) ) + .arg(Arg::with_name("FORMAT") + .long("format") + .help("Prints the output with the specified format") + .value_name("FORMAT_STRING") + .takes_value(true) + .required(false) + ) ) .subcommand( declare_input_path(SubCommand::with_name("path")) @@ -147,6 +156,7 @@ fn main() { Ok(Ok(buffers)) => { tessellate::write_output(buffers, command.is_present("COUNT"), + command.value_of("FORMAT"), float_precision, output).unwrap(); } diff --git a/cli/src/tessellate/format.rs b/cli/src/tessellate/format.rs new file mode 100644 index 00000000..e0aee030 --- /dev/null +++ b/cli/src/tessellate/format.rs @@ -0,0 +1,158 @@ +use itertools::{Itertools, Tuples}; +use lyon::math::Point; +use lyon::tessellation::geometry_builder::VertexBuffers; +use regex::Regex; +use std; + +const DEFAULT_FMT: &str = "vertices: [@vertices{sep=, }{fmt=({position.x}, {position.y})}@]\\nindices: [@indices{sep=, }{fmt={index}}@]"; + +pub fn format_output( + fmt_string: Option<&str>, + precision: Option, + buffers: &VertexBuffers, +) -> String { + let fmt = fmt_string.unwrap_or(DEFAULT_FMT).split('@'); + let extract = Regex::new(r"^(.*)\{sep=(.+?)\}\{fmt=(.*)\}$").unwrap(); + + let mut output = String::with_capacity(buffers.vertices.len() + buffers.indices.len()); + for section in fmt { + if let Some(capture) = extract.captures(section) { + let itername = capture.get(1).map(|m| m.as_str()).unwrap(); + let sep = capture.get(2).map(|m| m.as_str()).unwrap(); + let pattern = capture.get(3).map(|m| m.as_str()).unwrap(); + + match itername { + "vertices" => { + output.push_str(&format_iter(buffers.vertices.iter(), sep, pattern, |x| { + format_float(x, precision) + })); + } + "indices" => { + output.push_str(&format_iter(buffers.indices.iter(), sep, pattern, |x| { + format!("{}", x) + })); + } + "triangles" => { + let triangles: Tuples<_, (_, _, _)> = buffers.indices.iter().tuples(); + output.push_str(&format_iter(triangles, sep, pattern, |x| format!("{}", x))); + } + invalid => { + eprintln!("ERROR: `@{}...@` does not name an expansion", invalid); + std::process::exit(1); + } + } + } else { + output.push_str(section); + } + } + escape_specials(&output) +} + +fn format_iter(iter: I, sep: &str, pattern: &str, value_fmt: F) -> String +where + M: MatchVariable, + F: Fn(M::Value) -> String, + I: Iterator, +{ + let mut fmt_items: Vec = Vec::new(); + let extract = Regex::new(r"(\\\{.*?)*(\{.*?\})(\\\})*").unwrap(); + + for item in iter { + let mut buf = String::from(pattern); + for var in extract.captures_iter(pattern) { + let var = &var[2]; + if let Some(val) = item.match_var(var) { + let value = value_fmt(val); + let value = &value[..]; + let replace = Regex::new(®ex_escape_brackets(var)).unwrap(); + buf = replace.replace_all(&buf, value).to_string(); + } else { + eprintln!("ERROR: `{}` does not name a variable", var); + std::process::exit(1) + } + } + fmt_items.push(buf) + } + fmt_items.iter().join(sep) +} + +fn format_float(value: f32, precision: Option) -> String { + if let Some(p) = precision { + format!("{0:.1$}", value, p) + } else { + format!("{}", value) + } +} + +fn regex_escape_brackets(s: &str) -> String { + let mut buf = String::new(); + for c in s.chars() { + match c { + '{' | '}' => { + buf.push('\\'); + } + _ => {} + }; + buf.push(c) + } + buf +} + +fn escape_specials(s: &str) -> String { + s.chars() + .coalesce(|prev, cur| { + if prev == '\\' { + match cur { + 'n' => Ok('\n'), + 't' => Ok('\t'), + '{' => Ok('{'), + '}' => Ok('}'), + _ => Err((prev, cur)), + } + } else { + Err((prev, cur)) + } + }) + .collect::() +} + +trait MatchVariable { + type Value; + fn match_var(&self, key: &str) -> Option; +} + +impl<'a> MatchVariable for &'a Point { + type Value = f32; + + fn match_var(&self, key: &str) -> Option { + match key { + "{position.x}" | "{pos.x}" => Some(self.x), + "{position.y}" | "{pos.y}" => Some(self.y), + _ => None, + } + } +} + +impl<'a> MatchVariable for &'a u16 { + type Value = u16; + + fn match_var(&self, key: &str) -> Option { + match key { + "{index}" | "{i}" => Some(**self), + _ => None, + } + } +} + +impl<'a> MatchVariable for (&'a u16, &'a u16, &'a u16) { + type Value = u16; + + fn match_var(&self, key: &str) -> Option { + match key { + "{index0}" | "{i0}" => Some(*self.0), + "{index1}" | "{i1}" => Some(*self.1), + "{index2}" | "{i2}" => Some(*self.2), + _ => None, + } + } +} diff --git a/cli/src/tessellate.rs b/cli/src/tessellate/mod.rs similarity index 67% rename from cli/src/tessellate.rs rename to cli/src/tessellate/mod.rs index d0caee83..edc091c6 100644 --- a/cli/src/tessellate.rs +++ b/cli/src/tessellate/mod.rs @@ -8,6 +8,9 @@ use lyon::tessellation::{ use lyon::tess2; use std::io; +mod format; +use self::format::format_output; + #[derive(Debug)] pub enum TessError { Io(io::Error), @@ -18,14 +21,6 @@ impl ::std::convert::From<::std::io::Error> for TessError { fn from(err: io::Error) -> Self { TessError::Io(err) } } -fn format_float(value: f32, precision: Option) -> String { - if let Some(p) = precision { - format!("{0:.1$}", value, p) - } else { - format!("{}", value) - } -} - pub fn tessellate_path(cmd: TessellateCmd) -> Result, TessError> { let mut buffers: VertexBuffers = VertexBuffers::new(); @@ -69,41 +64,20 @@ pub fn tessellate_path(cmd: TessellateCmd) -> Result, TessE pub fn write_output( buffers: VertexBuffers, count: bool, + fmt_string: Option<&str>, float_precision: Option, mut output: Box ) -> Result<(), io::Error> { if count { - try!{ writeln!(&mut *output, "vertices: {}", buffers.vertices.len()) }; - try!{ writeln!(&mut *output, "indices: {}", buffers.indices.len()) }; - try!{ writeln!(&mut *output, "triangles: {}", buffers.indices.len() / 3) }; + writeln!(&mut *output, "vertices: {}", buffers.vertices.len())?; + writeln!(&mut *output, "indices: {}", buffers.indices.len())?; + writeln!(&mut *output, "triangles: {}", buffers.indices.len() / 3)?; return Ok(()); } - try!{ write!(&mut *output, "vertices: [") }; - let mut is_first = true; - for vertex in &buffers.vertices { - if !is_first { - try!{ write!(&mut *output, ", ") }; - } - try!{ write!(&mut *output, "({}, {})", format_float(vertex.x, float_precision), - format_float(vertex.y, float_precision)) }; - is_first = false; - } - try!{ writeln!(&mut *output, "]") }; - - try!{ write!(&mut *output, "indices: [") }; - let mut is_first = true; - for index in &buffers.indices { - if !is_first { - try!{ write!(&mut *output, ", ") }; - } - try!{ write!(&mut *output, "{}", index) }; - is_first = false; - } - try!{ writeln!(&mut *output, "]") }; - + writeln!(&mut *output, "{}", format_output(fmt_string, float_precision, &buffers))?; Ok(()) }