Repository navigation
feat: add Obsidian opener and per-opener filter metadata (0.2.0) - #11
Merged
Merged
Conversation
Imported WIP state: Launch::{Argv, Custom(fn)} on KnownApp, obsidian.rs with discover_vaults() reading obsidian.json + build_command() walking the three-way strategy ladder (vault root / file-in-vault / outside). Not yet: accepts_directories + file_support fields, Obsidian registration in KnownApp registry, public open(path, app_id) convenience. Tracked by polish task 2026-05-16-path-opener-uri-scheme-and-obsidian.
…its Vault struct were exposed as pub on the WIP commit, but vault is Obsidian-domain terminology that does not belong in path-opener's general vocabulary. Callers go through open(path, app_id) and never need to see vault metadata.
…s two flat metadata fields on PathOpener that callers use to build 'what can I open this with?' UIs: - accepts_directories: bool — can this opener open a directory path? - file_support: FileSupport — Any | NotSupported | Extensions(Vec<String>) Every built-in declares both fields explicitly (no silent defaults). Editors and file managers get (true, Any); terminals get (true, NotSupported); Obsidian gets (true, Extensions(['md','markdown','canvas'])). The static registry uses a sibling FileSupportSpec with &'static [&'static str] (consts can't allocate); detect_installed_apps converts to the owned FileSupport on the public surface. This keeps serde Deserialize working on PathOpener — the alternative shape using &'static [&'static str] on FileSupport directly does not derive Deserialize. A FileSupport::accepts_extension(ext) helper provides case-insensitive extension matching, consistent with how Path::extension yields a bare extension. Adds AlwaysUnavailable Detection so Linux/Windows Obsidian reports is_available=false (detection follow-up tracked in the task).
…d) as the primary entry point — callers hand path-opener a (path, app_id) and let it own the strategy. Internally resolves the registered KnownApp, picks the platform entry, and dispatches through the same Launch::{Argv, Custom} machinery as open_with. open_with stays as the lower-level form for callers that already have a PathOpener struct in hand. Extracts a build_command_for helper so upcoming Obsidian strategy tests can inspect the constructed Command without spawning a process.
… pure URI-building logic in obsidian::build_command into a private build_uri(path, vaults) helper. The launch step (uri_launcher(&uri)) is unchanged but the URI assembly is now testable without spawning a process. New tests cover the three strategy-ladder arms: - path equals a known vault root → ?vault=Name - path is inside a known vault → ?vault=Name&file=rel - path outside every vault → ?path=abs - empty vault list (obsidian.json absent) → ?path=abs Plus a parser test confirming discover_vaults' malformed-JSON branch returns no vaults (the underlying serde_json::from_slice errors are swallowed into Vec::new()).
…categories ('we know editors / terminals / file managers') to explicit per-opener metadata (accepts_directories + file_support). Includes: - open(path, app_id) as the primary entry point - the FileSupport variants table with concrete examples - a doctest showing how to filter detect_installed_apps() by (accepts_directories, file_support) to build a 'what can I open this with?' list for a given path - Obsidian opener with internal vault-discovery routing called out as experimental - Linux/Windows Obsidian detection follow-up noted - migration notes for the field additions and the FileSupport::Extensions(Vec<String>) shape The README is included into the crate-level rustdoc via include_str!, so its example snippets are exercised by cargo test --doc.
….1.1 -> 0.2.0 (new API surface: open(path, app_id), FileSupport, accepts_directories, Obsidian opener) - repository URL was a typo (rs-path-opener); real repo is rust-path-opener - description gains 'Markdown apps' to reflect the Obsidian addition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
obsidian://URIs by reading the user's ownobsidian.jsonto discover registered vaults and routing the path through a three-way strategy ladder (vault root →?vault=Name; file inside a vault →?vault=Name&file=rel; outside any known vault →?path=abs). All Obsidian/URI specifics stay insidesrc/obsidian.rs— noVaultor URI scheme leaks to the public API.accepts_directories: boolplus a concreteenum FileSupport { Any, NotSupported, Extensions(Vec<String>) }. Every built-in declares both fields explicitly via macro-required arguments — no silent defaults.open(path: &Path, app_id: &str)convenience that resolves the opener from the registry internally. Legacyopen_path(command, path)from 0.1.x is retained as a primitive. Existingopen_with(opener, path)stays as the lower-level entry point.Bump to 0.2.0 (minor —
PathOpenerfield shape changed). Fixes theCargo.tomlrepository/homepageURLs that pointed to a non-existentrs-path-openerrepo.Task
Tracked in the consuming project at
docs/planning/tasks/2026-05-16-path-opener-uri-scheme-and-obsidian.md(insksizer/polish). That document carries the full Goal / Today / Proposed / Approach / ACs / Out-of-scope / Discovery-context, including the design pivot away from a publicLaunchStrategy/TargetKindenum.Acceptance criteria
open,open_default,open_with,open_path(legacy primitive retained per user call),detect_installed_apps,PathOpener,FileSupport. NoLaunchStrategy, noTargetKind.grep "[Vv]ault" src/lib.rsreturns only one hit, an internal//(not///) comment inside the privateLaunchenum — not rendered in docs.pub(crate) mod obsidianandpub(crate) struct Vaultkeep that vocabulary crate-private.enum FileSupport { Any, NotSupported, Extensions(Vec<String>) }derivesspecta::Typeunder thespectafeature. Variant name stayedExtensions;Vec<String>reshape (vs&'static [&'static str]) was the spec's Approach-8 escape-hatch (b) — forced by serde Deserialize onPathOpener. PrivateFileSupportSpecsibling carries the&'staticform so the constKNOWN_APPStable doesn't allocate; conversion happens atdetect_installed_apps()time. README migration notes document.every_known_app_declares_expected_metadatawalks the registry. Editors / file-managers →(true, Any); terminals →(true, NotSupported); Obsidian →(true, Extensions(["md","markdown","canvas"])).Launch::Custom(obsidian::build_command). macOS detection viaMacAppBundle("Obsidian.app"). Linux/Windows use newDetection::AlwaysUnavailablevariant so they always reportis_available: false(avoids false-positive on any unrelatedobsidianbinary on PATH) with TODO pointing to the follow-up.uri_for_vault_root_uses_vault_query_only—?vault=<Name>.uri_for_file_inside_vault_uses_vault_and_file—?vault=<Name>&file=<rel>.uri_for_path_outside_any_vault_falls_through_to_path_query—?path=<abs>.parse_handles_missing_unreadable_malformed_inputscovers the JSON-parse error branch;discover_does_not_paniccovers the runtime path; missing-file / unreadable-bytes branches are simplelet Ok(..) = .. else { return Vec::new() }.cargo test --doc, including the README'sopeners_for(path)example that filters by bothaccepts_directoriesandfile_support. README isinclude_str!'d intosrc/lib.rsso its examples are exercised.cargo test(16 unit + 5 doc) green;cargo check --all-featuresgreen.Cargo.tomlrepositoryandhomepagecorrected tohttps://github.com/sksizer/rust-path-opener.0.2.0published on crates.io +v0.2.0tag — not yet.Cargo.tomlbumped, tree clean; to be done by maintainer after merge viacargo publish+git tag v0.2.0 && git push --tags.Test plan
just ciruns green locally.open(vault_root, "obsidian")opens Obsidian to that vault;open(vault/sub/file.md, "obsidian")opens the note. Unregistered path still launches Obsidian (it'll prompt).detect_installed_apps()filtered byaccepts_directories: true+file_support: Extensions("md")matchesmdfiles to Obsidian + any general editors (VS Code, Zed, ...), excludes terminals.