From 40c40f83d6d2800f56a20679f8e95619d2f29d75 Mon Sep 17 00:00:00 2001 From: Troy Date: Thu, 22 May 2025 00:59:04 +0100 Subject: [PATCH] use indexmap crate for sorted output --- Cargo.lock | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/main.rs | 25 +++++++++++-------- 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef94afc..8f532ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,11 +12,19 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" name = "broot" version = "0.0.1" dependencies = [ + "indexmap", + "open", "pulldown-cmark", "serde", "serde_json", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "getopts" version = "0.2.21" @@ -26,18 +34,82 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "proc-macro2" version = "1.0.95" diff --git a/Cargo.toml b/Cargo.toml index 16f3b7b..f9a39fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ description = "Markdown to JSON bookmark opener." authors = ["Troy Lusty "] [dependencies] +indexmap = "2.9.0" +open = "5.3.2" pulldown-cmark = "0.13.0" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" diff --git a/src/main.rs b/src/main.rs index 596d1bf..d90bc70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ +use indexmap::IndexMap; use pulldown_cmark::{Event, LinkType, Parser, Tag, TagEnd}; use serde::Serialize; -use std::collections::HashMap; use std::env; use std::fs; use std::io::Write; @@ -13,27 +13,27 @@ struct Link { } fn main() -> std::io::Result<()> { - let path = env::args().nth(1).unwrap_or_else(|| { - eprintln!("❌ Usage: select_bookmark "); - std::process::exit(1); - }); - + let path = env::args() + .nth(1) + .expect("❌ Usage: select_bookmark "); let markdown = fs::read_to_string(path)?; let parser = Parser::new(&markdown); - let mut links_by_heading: HashMap> = HashMap::new(); - let mut current_heading = "No Heading".to_string(); + let mut links_by_heading: IndexMap> = IndexMap::new(); + let mut current_heading = String::from("No Heading"); let mut heading_buf = String::new(); let mut current_link: Option = None; for event in parser { match event { Event::Start(Tag::Heading { .. }) => heading_buf.clear(), + Event::End(TagEnd::Heading(_)) => { if !heading_buf.trim().is_empty() { - current_heading = heading_buf.trim().to_string(); + current_heading = heading_buf.trim().to_owned(); } } + Event::Start(Tag::Link { link_type: LinkType::Inline, dest_url, @@ -44,6 +44,7 @@ fn main() -> std::io::Result<()> { title: String::new(), }); } + Event::End(TagEnd::Link) => { if let Some(link) = current_link.take() { links_by_heading @@ -52,6 +53,7 @@ fn main() -> std::io::Result<()> { .push(link); } } + Event::Text(text) => { if let Some(link) = current_link.as_mut() { link.title.push_str(&text); @@ -59,6 +61,7 @@ fn main() -> std::io::Result<()> { heading_buf.push_str(&text); } } + _ => {} } } @@ -92,11 +95,11 @@ fn main() -> std::io::Result<()> { } let output = child.wait_with_output()?; - let selected = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let selected = String::from_utf8_lossy(&output.stdout).trim().to_owned(); if let Some((_, url)) = entries.iter().find(|(label, _)| label == &selected) { println!("🌐 Opening: {}", url); - Command::new("xdg-open").arg(url).spawn()?; + open::that(url)?; } else { println!("❌ Selection not found or canceled."); }