diff --git a/Cargo.lock b/Cargo.lock index af9cf74..0633e2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,7 @@ name = "asd" version = "0.1.0" dependencies = [ "argh", + "indicatif", ] [[package]] @@ -592,6 +593,14 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "by-hand" +version = "0.1.0" +dependencies = [ + "indicatif", + "macroquad", +] + [[package]] name = "bytemuck" version = "1.16.0" @@ -808,6 +817,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1035,6 +1057,14 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dual-nums" +version = "0.1.0" +dependencies = [ + "nalgebra 0.33.0", + "num-dual", +] + [[package]] name = "ecolor" version = "0.27.2" @@ -1182,6 +1212,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "enumflags2" version = "0.7.9" @@ -1329,7 +1365,7 @@ name = "fdg" version = "1.0.0" source = "git+https://github.com/grantshandy/fdg#50755f1dea20249d753600e7e7e51ca33e87b5a1" dependencies = [ - "nalgebra", + "nalgebra 0.32.5", "num-traits", "petgraph", "rand", @@ -1344,7 +1380,7 @@ dependencies = [ "asd", "fdg", "macroquad", - "nalgebra", + "nalgebra 0.32.5", "num-traits", "petgraph", "petgraph-gen", @@ -1561,6 +1597,12 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + [[package]] name = "glob" version = "0.3.1" @@ -1702,8 +1744,9 @@ version = "0.1.0" dependencies = [ "asd", "fdg", + "indicatif", "macroquad", - "nalgebra", + "nalgebra 0.32.5", "num-traits", "petgraph", "petgraph-gen", @@ -1719,7 +1762,7 @@ dependencies = [ "cust", "fdg", "macroquad", - "nalgebra", + "nalgebra 0.32.5", "num-traits", "petgraph", "petgraph-gen", @@ -1841,6 +1884,19 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + [[package]] name = "instant" version = "0.1.13" @@ -1924,6 +1980,12 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.155" @@ -2003,13 +2065,12 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "macroquad" -version = "0.4.6" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d983c03e9171099b2eb2067206c78cb37904ea071bb9ea4602c43ed45bb7097" +checksum = "81fef16b2d4de22ac372b5d7d76273c0ead0a31f9de9bc649af8f816816db8a8" dependencies = [ - "bumpalo", "fontdue", - "glam 0.21.3", + "glam 0.27.0", "image", "macroquad_macro", "miniquad", @@ -2019,9 +2080,9 @@ dependencies = [ [[package]] name = "macroquad_macro" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5cecfede1e530599c8686f7f2d609489101d3d63741a6dc423afc997ce3fcc8" +checksum = "64b1d96218903768c1ce078b657c0d5965465c95a60d2682fd97443c9d2483dd" [[package]] name = "malloc_buf" @@ -2092,9 +2153,9 @@ dependencies = [ [[package]] name = "miniquad" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17282b286b736c9ef6f79c630a5173669491a5e2f13a03fe556e59f7b577cd2" +checksum = "2124e5e6bab86d96f263d78dc763c29e0da4c4f7ff0e1bb33f1d3905d1121262" dependencies = [ "libc", "ndk-sys 0.2.2", @@ -2152,19 +2213,35 @@ dependencies = [ "num-traits", "rand", "rand_distr", - "simba", + "simba 0.8.1", + "typenum", +] + +[[package]] +name = "nalgebra" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c4b5f057b303842cf3262c27e465f4c303572e7f6b0648f60e16248ac3397f4" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba 0.9.0", "typenum", ] [[package]] name = "nalgebra-macros" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.65", ] [[package]] @@ -2222,6 +2299,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -2231,6 +2318,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-dual" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ba622fc86ea74f6682d06848487fb7444d010726ec70addf16737c6b7f1c20" +dependencies = [ + "approx", + "nalgebra 0.32.5", + "num-traits", + "simba 0.8.1", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -2246,6 +2345,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ + "num-bigint", "num-integer", "num-traits", ] @@ -2281,6 +2381,12 @@ dependencies = [ "syn 2.0.65", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "objc" version = "0.2.7" @@ -2610,6 +2716,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2992,6 +3104,19 @@ dependencies = [ "wide", ] +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index 8862ef0..051f988 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] argh = "0.1.12" +indicatif = "0.17.8" [workspace] members = ["examples/*"] diff --git a/examples/by-hand/Cargo.toml b/examples/by-hand/Cargo.toml new file mode 100644 index 0000000..009b6ce --- /dev/null +++ b/examples/by-hand/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "by-hand" +version = "0.1.0" +edition = "2021" + +[dependencies] +indicatif = "0.17.8" +macroquad = "0.4.13" diff --git a/examples/by-hand/src/main.rs b/examples/by-hand/src/main.rs new file mode 100644 index 0000000..e507d71 --- /dev/null +++ b/examples/by-hand/src/main.rs @@ -0,0 +1,33 @@ +use std::time::Instant; + +use macroquad::{prelude::*, rand, ui::root_ui}; + +#[macroquad::main("by-hand")] +async fn main() { + println!("Hello, world!"); + + loop { + let start_update = Instant::now(); + + // update(); + + let update_elapsed = start_update.elapsed(); + + // Render + + let start_render = Instant::now(); + + clear_background(WHITE); + + // draw(); + + let render_elapsed = start_render.elapsed(); + + root_ui().label( + None, + format!("update: {:?}, render: {:?}", update_elapsed, render_elapsed).as_str(), + ); + + next_frame().await + } +} diff --git a/examples/dual-nums/Cargo.toml b/examples/dual-nums/Cargo.toml new file mode 100644 index 0000000..120e595 --- /dev/null +++ b/examples/dual-nums/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dual-nums" +version = "0.1.0" +edition = "2021" + +[dependencies] +nalgebra = "0.33.0" +num-dual = "0.9.1" diff --git a/examples/dual-nums/src/main.rs b/examples/dual-nums/src/main.rs new file mode 100644 index 0000000..2bc52a6 --- /dev/null +++ b/examples/dual-nums/src/main.rs @@ -0,0 +1,15 @@ +// use nalgebra::SVector; +// use num_dual::*; + +// fn bar(x: DualSVec64<2>, y: DualSVec64<2>) -> DualSVec64<2> { +// x * x + y * y +// } + +fn main() { + // // let result = bar( + // // DualSVec64::new(3.0, Derivative::some(SVector::from_vec(vec![1.0, 0.0]))), + // // DualSVec64::new(4.0, Derivative::some(SVector::from_vec(vec![0.0, 1.0]))), + // // ); + + // // println!("{:?}", result); +} diff --git a/examples/fdg-example/src/stress_majorization.rs b/examples/fdg-example/src/stress_majorization.rs index 4c8709e..17a5412 100644 --- a/examples/fdg-example/src/stress_majorization.rs +++ b/examples/fdg-example/src/stress_majorization.rs @@ -71,26 +71,27 @@ impl StressMajorization { &mut self, graph: &mut StableGraph<(N, Point), E>, ) -> F { - graph - .node_indices() - .flat_map(|v| { - graph.node_indices().skip(v.index() + 1).map(move |w| { - let dist = nalgebra::distance( - &graph.node_weight(v).unwrap().1, - &graph.node_weight(w).unwrap().1, - ); - - if dist != F::zero() { - let dij = self.shortest_path_matrix[&v][&w]; - - let sp_diff = self.shortest_path_matrix[&v][&w] - dist; - dij.simd_sqrt().abs() * sp_diff * sp_diff - } else { - F::zero() - } - }) - }) - .sum() + // graph + // .node_indices() + // .flat_map(|v| { + // graph.node_indices().skip(v.index() + 1).map(move |w| { + // let dist = nalgebra::distance( + // &graph.node_weight(v).unwrap().1, + // &graph.node_weight(w).unwrap().1, + // ); + + // if dist != F::zero() { + // let dij = self.shortest_path_matrix[&v][&w]; + + // let sp_diff = self.shortest_path_matrix[&v][&w] - dist; + // dij.simd_sqrt().abs() * sp_diff * sp_diff + // } else { + // F::zero() + // } + // }) + // }) + // .sum() + F::default() } } diff --git a/examples/graphs-1/Cargo.toml b/examples/graphs-1/Cargo.toml index 80f8ef2..90e1a30 100644 --- a/examples/graphs-1/Cargo.toml +++ b/examples/graphs-1/Cargo.toml @@ -15,3 +15,4 @@ petgraph-gen = "0.1.3" fdg = { git = "https://github.com/grantshandy/fdg" } asd = { path = "../../" } rayon = "1.10.0" +indicatif = "0.17.8" diff --git a/examples/graphs-1/src/gd.rs b/examples/graphs-1/src/gd.rs new file mode 100644 index 0000000..f72f061 --- /dev/null +++ b/examples/graphs-1/src/gd.rs @@ -0,0 +1,51 @@ +use std::collections::HashMap; + +struct Graph { + edges_from: Vec, + edges_to: Vec, +} + +impl Graph { + fn new() -> Self { + Self { + edges_from: Vec::new(), + edges_to: Vec::new(), + } + } + + fn new_with_capacity(capacity: usize) -> Self { + Self { + edges_from: Vec::with_capacity(capacity), + edges_to: Vec::with_capacity(capacity), + } + } + + fn add_edge(&mut self, from: u32, to: u32) { + self.edges_from.push(from); + self.edges_to.push(to); + } +} + +pub fn update( + graph: &Graph, + xs: &Vec, + ys: &Vec, + desired_distance_matrix: &HashMap>, +) -> f32 { + desired_distance_matrix + .iter() + .flat_map(|(&i, targets)| { + targets + .iter() + .map(move |(&j, &target_distance)| (i, j, target_distance)) + }) + .map(|(i, j, target_distance)| { + let dx = xs[j] - xs[i]; + let dy = ys[j] - ys[i]; + let distance_sqrd = dx * dx + dy * dy; + let error = distance_sqrd - target_distance * target_distance; + + error * error + }) + .sum() +} diff --git a/examples/graphs-1/src/main.rs b/examples/graphs-1/src/main.rs index d2abcee..a9ae7ba 100644 --- a/examples/graphs-1/src/main.rs +++ b/examples/graphs-1/src/main.rs @@ -1,12 +1,21 @@ -use std::{collections::HashMap, env, ops::AddAssign, time::Instant}; +use std::{ + collections::HashMap, + env, + io::{BufRead, BufReader}, + ops::AddAssign, + time::Instant, +}; use asd::{gfa::Entry, parser}; +use indicatif::ProgressIterator; use macroquad::{prelude::*, rand, ui::root_ui}; use nalgebra::{Point2, SVector}; use petgraph::{algo::dijkstra, graph::NodeIndex, stable_graph::StableGraph}; use rayon::prelude::*; +mod gd; + #[macroquad::main("graphs_1")] async fn main() { println!("Hello, world!"); @@ -127,8 +136,16 @@ fn load_graph() -> StableGraph<(String, Point2), ()> { let mut graph = StableGraph::new(); - let file = std::fs::File::open(env::args().nth(1).expect("missing gfa file argument")).unwrap(); - let entries = parser::parse_source(file).unwrap(); + let filename = env::args().nth(1).expect("missing gfa file argument"); + + let file_lines_count = BufReader::new(std::fs::File::open(&filename).expect("file not found")) + .lines() + .progress_with(indicatif::ProgressBar::new_spinner()) + .count() as u64; + + let file = std::fs::File::open(filename).unwrap(); + + let entries = parser::parse_source(file, file_lines_count).unwrap(); let mut index_map = HashMap::new(); diff --git a/src/graph.rs b/src/graph.rs index 73c0f4a..43c60f6 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -6,6 +6,8 @@ use std::{ rc::Rc, }; +use indicatif::{ProgressIterator, ProgressStyle}; + #[derive(Debug)] pub struct AdjacencyGraph where @@ -152,7 +154,17 @@ where let op = self.opposite(); - for node in self.nodes.iter() { + for node in self + .nodes + .iter() + .progress() + .with_style( + indicatif::ProgressStyle::default_bar() + .template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}") + .unwrap(), + ) + .with_prefix("computing connected components") + { if visited.contains(node) { continue; } @@ -180,8 +192,6 @@ where } } - // println!("CC: {:?}", cc); - visited.extend(cc.iter().map(|x| x.to_owned())); result.push(cc.iter().map(|x| x.to_owned()).collect()); } @@ -197,26 +207,26 @@ where continue; } - println!("All CC: {:?}", cc); + // println!("All CC: {:?}", cc); let new_cc = Rc::new(RefCell::new(HashSet::new())); let mut stack: Vec<&V> = vec![node]; while let Some(node) = stack.pop() { - println!("New CC: {:?}", new_cc.borrow()); + // println!("New CC: {:?}", new_cc.borrow()); if cc.contains_key(&node) { // merge the two connected components and go to the next node let old_cc: &Rc>> = cc.get(&node).unwrap(); - println!( - "Merging {:?} with {:?} due to link to {:?}", - new_cc.borrow(), - old_cc.borrow(), - node - ); + // println!( + // "Merging {:?} with {:?} due to link to {:?}", + // new_cc.borrow(), + // old_cc.borrow(), + // node + // ); new_cc .borrow_mut() diff --git a/src/graph_2.rs b/src/graph_2.rs new file mode 100644 index 0000000..5ad7302 --- /dev/null +++ b/src/graph_2.rs @@ -0,0 +1,104 @@ +use std::{collections::HashMap, io::Read}; + +use crate::{gfa::Entry, parser}; + +pub struct Graph { + nodes: HashMap, + + edges_from: Vec, + edges_to: Vec, +} + +#[derive(Debug)] +pub enum GraphError { + NodeNotFound(String), +} + +impl Graph { + pub fn new() -> Self { + Self { + nodes: HashMap::new(), + edges_from: Vec::new(), + edges_to: Vec::new(), + } + } + + pub fn add_node(&mut self, id: String) { + if self.nodes.contains_key(&id) { + return; + } + + self.nodes.insert(id, self.nodes.len()); + } + + pub fn add_edge(&mut self, from_id: &String, to_id: &String) -> Result<(), GraphError> { + let from = self + .nodes + .get(from_id) + .ok_or(GraphError::NodeNotFound(from_id.clone()))?; + + let to = self + .nodes + .get(to_id) + .ok_or(GraphError::NodeNotFound(to_id.clone()))?; + + self.edges_from.push(*from); + self.edges_to.push(*to); + + Ok(()) + } +} + +#[derive(Debug)] +pub enum LoadGraphError { + IoError(std::io::Error), + GraphError(GraphError), +} + +pub fn load_graph(reader: R, len: u64) -> Result { + println!("Loading graph"); + + let mut graph = Graph::new(); + + let entries = parser::parse_source(reader, len).map_err(|e| LoadGraphError::IoError(e))?; + + let node_count = entries + .iter() + .filter_map(|entry| match entry { + Entry::Segment { id, .. } => Some(id), + _ => None, + }) + .count(); + + println!("Node count: {}", node_count); + + for entry in entries + .iter() + .filter(|entry| matches!(entry, Entry::Link { .. })) + { + if let Entry::Link { + from, + from_orient, + to, + to_orient, + } = entry + { + let node_from = format!("{}{}", from, from_orient); + let node_to = format!("{}{}", to, to_orient); + + graph.add_node(node_from.clone()); + graph.add_node(node_to.clone()); + + graph + .add_edge(&node_from, &node_to) + .expect("Error adding edge"); + graph + .add_edge(&node_to, &node_from) + .expect("Error adding edge"); + } + } + + println!("Loading completed"); + + Ok(graph) +} diff --git a/src/lib.rs b/src/lib.rs index 0193161..d466829 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ pub mod gfa; pub mod graph; +pub mod graph_2; pub mod parser; diff --git a/src/main.rs b/src/main.rs index e00d40e..b3f1ef0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,12 @@ -use std::collections::HashMap; +use std::{ + collections::HashMap, + io::{BufRead, BufReader}, +}; use argh::FromArgs; use gfa::{Entry, Orientation}; use graph::AdjacencyGraph; +use indicatif::ProgressIterator; mod gfa; mod graph; @@ -35,15 +39,21 @@ fn main() -> std::io::Result<()> { match opts.nested { MySubCommandEnum::Show(show) => { + let file_lines_count = BufReader::new(std::fs::File::open(&show.input)?) + .lines() + .progress_with(indicatif::ProgressBar::new_spinner().with_message("counting lines")) + .count() as u64; + let file = std::fs::File::open(show.input)?; - let entries = parser::parse_source(file)?; + + let entries = parser::parse_source(file, file_lines_count)?; + + println!("Number of entries: {}", entries.len()); let mut sequence_map = HashMap::new(); let mut graph: AdjacencyGraph<(String, Orientation)> = AdjacencyGraph::new(); for entry in entries { - println!("{:?}", entry); - match entry { Entry::Segment { id, sequence } => { sequence_map.insert(id.clone(), sequence); diff --git a/src/parser.rs b/src/parser.rs index 9d0f1cf..67c93e5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,12 @@ use std::{ io::{self, BufRead, BufReader, Read}, str::FromStr, + thread, + time::Duration, }; +use indicatif::ProgressIterator; + use crate::gfa::{Entry, Orientation}; fn parse_orientation(s: &str) -> Orientation { @@ -122,9 +126,20 @@ fn parse_walk(line: &str) -> Entry { } } -pub fn parse_source(reader: R) -> io::Result> { +pub fn parse_source(reader: R, line_count: u64) -> io::Result> { let mut entries = Vec::new(); - for line in BufReader::new(reader).lines() { + let mut skipped = Vec::new(); + + for line in BufReader::new(reader) + .lines() + .progress_count(line_count) + .with_style( + indicatif::ProgressStyle::default_bar() + .template("{prefix} {spinner} [{elapsed_precise}] [{wide_bar}] {pos}/{len}") + .unwrap(), + ) + .with_prefix("parsing source file") + { let line = line?; let line = line.trim(); @@ -132,22 +147,25 @@ pub fn parse_source(reader: R) -> io::Result> { continue; } - // println!("Parsing: {}", line); - let first_char = line.chars().next().unwrap(); let entry = match first_char { 'H' => parse_header(line), 'S' => parse_segment(line), 'L' => parse_link(line), - 'P' => parse_path(line), - 'W' => parse_walk(line), + // 'P' => parse_path(line), + // 'W' => parse_walk(line), _ => { - eprintln!("Unknown line type: {}", line); + skipped.push(line.chars().next().expect("got empty line")); continue; } }; + entries.push(entry); } + for s in skipped { + eprintln!("skipped line type: {}", s); + } + Ok(entries) }