Merge pull request #37 from zoni/start_at
New: add start_at option to export a partial vault
This commit is contained in:
commit
d138c92a25
107
README.md
107
README.md
@ -67,6 +67,8 @@ Running `obsidian-export --version` should print a version number rather than gi
|
|||||||
>
|
>
|
||||||
> For example `~/Downloads/obsidian-export --version` on Mac/Linux or `~\Downloads\obsidian-export --version` on Windows (PowerShell).
|
> For example `~/Downloads/obsidian-export --version` on Mac/Linux or `~\Downloads\obsidian-export --version` on Windows (PowerShell).
|
||||||
|
|
||||||
|
## Exporting notes
|
||||||
|
|
||||||
In it's most basic form, `obsidian-export` takes just two mandatory arguments, a source and a destination:
|
In it's most basic form, `obsidian-export` takes just two mandatory arguments, a source and a destination:
|
||||||
|
|
||||||
````sh
|
````sh
|
||||||
@ -89,6 +91,31 @@ obsidian-export my-obsidian-vault/some-note.md /tmp/export/
|
|||||||
obsidian-export my-obsidian-vault/some-note.md /tmp/exported-note.md
|
obsidian-export my-obsidian-vault/some-note.md /tmp/exported-note.md
|
||||||
````
|
````
|
||||||
|
|
||||||
|
Note that in this mode, obsidian-export sees `some-note.md` as being the only file that exists in your vault so references to other notes won't be resolved.
|
||||||
|
This is by design.
|
||||||
|
|
||||||
|
If you'd like to export a single note while resolving links or embeds to other areas in your vault then you should instead specify the root of your vault as the source, passing the file you'd like to export with `--start-at`, as described in the next section.
|
||||||
|
|
||||||
|
### Exporting a partial vault
|
||||||
|
|
||||||
|
Using the `--start-at` argument, you can export just a subset of your vault.
|
||||||
|
Given the following vault structure:
|
||||||
|
|
||||||
|
````
|
||||||
|
my-obsidian-vault
|
||||||
|
├── Notes/
|
||||||
|
├── Books/
|
||||||
|
└── People/
|
||||||
|
````
|
||||||
|
|
||||||
|
This will export only the notes in the `Books` directory to `exported-notes`:
|
||||||
|
|
||||||
|
````sh
|
||||||
|
obsidian-export my-obsidian-vault --start-at my-obsidian-vault/Books exported-notes
|
||||||
|
````
|
||||||
|
|
||||||
|
In this mode, all notes under the source (the first argument) are considered part of the vault so any references to these files will remain intact, even if they're not part of the exported notes.
|
||||||
|
|
||||||
## Character encodings
|
## Character encodings
|
||||||
|
|
||||||
At present, UTF-8 character encoding is assumed for all note text as well as filenames.
|
At present, UTF-8 character encoding is assumed for all note text as well as filenames.
|
||||||
@ -230,7 +257,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
* Postprocessing support. \[Nick Groenen]
|
* Postprocessing support. \[Nick Groenen\]
|
||||||
|
|
||||||
Add support for postprocessing of Markdown prior to writing converted
|
Add support for postprocessing of Markdown prior to writing converted
|
||||||
notes to disk.
|
notes to disk.
|
||||||
@ -254,7 +281,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Also percent-encode `?` in filenames. \[Nick Groenen]
|
* Also percent-encode `?` in filenames. \[Nick Groenen\]
|
||||||
|
|
||||||
A recent Obsidian update expanded the list of allowed characters in
|
A recent Obsidian update expanded the list of allowed characters in
|
||||||
filenames, which now includes `?` as well. This needs to be
|
filenames, which now includes `?` as well. This needs to be
|
||||||
@ -262,20 +289,20 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
* Bump pretty_assertions from 0.6.1 to 0.7.1. \[dependabot\[bot]]
|
* Bump pretty_assertions from 0.6.1 to 0.7.1. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [pretty_assertions](https://github.com/colin-kiegel/rust-pretty-assertions) from 0.6.1 to 0.7.1.
|
Bumps [pretty_assertions](https://github.com/colin-kiegel/rust-pretty-assertions) from 0.6.1 to 0.7.1.
|
||||||
|
|
||||||
* [Release notes](https://github.com/colin-kiegel/rust-pretty-assertions/releases)
|
* [Release notes](https://github.com/colin-kiegel/rust-pretty-assertions/releases)
|
||||||
* [Changelog](https://github.com/colin-kiegel/rust-pretty-assertions/blob/main/CHANGELOG.md)
|
* [Changelog](https://github.com/colin-kiegel/rust-pretty-assertions/blob/main/CHANGELOG.md)
|
||||||
* [Commits](https://github.com/colin-kiegel/rust-pretty-assertions/compare/v0.6.1...v0.7.1)
|
* [Commits](https://github.com/colin-kiegel/rust-pretty-assertions/compare/v0.6.1...v0.7.1)
|
||||||
* Bump walkdir from 2.3.1 to 2.3.2. \[dependabot\[bot]]
|
* Bump walkdir from 2.3.1 to 2.3.2. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [walkdir](https://github.com/BurntSushi/walkdir) from 2.3.1 to 2.3.2.
|
Bumps [walkdir](https://github.com/BurntSushi/walkdir) from 2.3.1 to 2.3.2.
|
||||||
|
|
||||||
* [Release notes](https://github.com/BurntSushi/walkdir/releases)
|
* [Release notes](https://github.com/BurntSushi/walkdir/releases)
|
||||||
* [Commits](https://github.com/BurntSushi/walkdir/compare/2.3.1...2.3.2)
|
* [Commits](https://github.com/BurntSushi/walkdir/compare/2.3.1...2.3.2)
|
||||||
* Bump regex from 1.4.3 to 1.4.5. \[dependabot\[bot]]
|
* Bump regex from 1.4.3 to 1.4.5. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.3 to 1.4.5.
|
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.3 to 1.4.5.
|
||||||
|
|
||||||
@ -287,11 +314,11 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
* Add `--version` flag. \[Nick Groenen]
|
* Add `--version` flag. \[Nick Groenen\]
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
* Don't Box FilterFn in WalkOptions. \[Nick Groenen]
|
* Don't Box FilterFn in WalkOptions. \[Nick Groenen\]
|
||||||
|
|
||||||
Previously, `filter_fn` on the `WalkOptions` struct looked like:
|
Previously, `filter_fn` on the `WalkOptions` struct looked like:
|
||||||
|
|
||||||
@ -313,7 +340,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Recognize notes beginning with underscores. \[Nick Groenen]
|
* Recognize notes beginning with underscores. \[Nick Groenen\]
|
||||||
|
|
||||||
Notes with an underscore would fail to be recognized within Obsidian
|
Notes with an underscore would fail to be recognized within Obsidian
|
||||||
`[[_WikiLinks]]` due to the assumption that the underlying Markdown
|
`[[_WikiLinks]]` due to the assumption that the underlying Markdown
|
||||||
@ -324,46 +351,46 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
machine which correctly recognizes this corner-case (and likely some
|
machine which correctly recognizes this corner-case (and likely some
|
||||||
others).
|
others).
|
||||||
|
|
||||||
* Support self-references. \[Joshua Coles]
|
* Support self-references. \[Joshua Coles\]
|
||||||
|
|
||||||
This ensures links to headings within the same note (`[[#Heading]]`)
|
This ensures links to headings within the same note (`[[#Heading]]`)
|
||||||
resolve correctly.
|
resolve correctly.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
* Avoid redundant "Release" in GitHub release titles. \[Nick Groenen]
|
* Avoid redundant "Release" in GitHub release titles. \[Nick Groenen\]
|
||||||
|
|
||||||
* Add failing testcase for files with underscores. \[Nick Groenen]
|
* Add failing testcase for files with underscores. \[Nick Groenen\]
|
||||||
|
|
||||||
* Add unit tests for display of ObsidianNoteReference. \[Nick Groenen]
|
* Add unit tests for display of ObsidianNoteReference. \[Nick Groenen\]
|
||||||
|
|
||||||
* Add some unit tests for ObsidianNoteReference::from_str. \[Nick Groenen]
|
* Add some unit tests for ObsidianNoteReference::from_str. \[Nick Groenen\]
|
||||||
|
|
||||||
* Also run tests on pull requests. \[Nick Groenen]
|
* Also run tests on pull requests. \[Nick Groenen\]
|
||||||
|
|
||||||
* Apply clippy suggestions following rust 1.50.0. \[Nick Groenen]
|
* Apply clippy suggestions following rust 1.50.0. \[Nick Groenen\]
|
||||||
|
|
||||||
* Fix infinite recursion bug with references to current file. \[Joshua Coles]
|
* Fix infinite recursion bug with references to current file. \[Joshua Coles\]
|
||||||
|
|
||||||
* Add tests for self-references. \[Joshua Coles]
|
* Add tests for self-references. \[Joshua Coles\]
|
||||||
|
|
||||||
Note as there is no support for block references at the moment, the generated link goes nowhere, however it is to a reasonable ID
|
Note as there is no support for block references at the moment, the generated link goes nowhere, however it is to a reasonable ID
|
||||||
|
|
||||||
* Bump tempfile from 3.1.0 to 3.2.0. \[dependabot\[bot]]
|
* Bump tempfile from 3.1.0 to 3.2.0. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.1.0 to 3.2.0.
|
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.1.0 to 3.2.0.
|
||||||
|
|
||||||
* [Release notes](https://github.com/Stebalien/tempfile/releases)
|
* [Release notes](https://github.com/Stebalien/tempfile/releases)
|
||||||
* [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS)
|
* [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS)
|
||||||
* [Commits](https://github.com/Stebalien/tempfile/commits)
|
* [Commits](https://github.com/Stebalien/tempfile/commits)
|
||||||
* Bump eyre from 0.6.3 to 0.6.5. \[dependabot\[bot]]
|
* Bump eyre from 0.6.3 to 0.6.5. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [eyre](https://github.com/yaahc/eyre) from 0.6.3 to 0.6.5.
|
Bumps [eyre](https://github.com/yaahc/eyre) from 0.6.3 to 0.6.5.
|
||||||
|
|
||||||
* [Release notes](https://github.com/yaahc/eyre/releases)
|
* [Release notes](https://github.com/yaahc/eyre/releases)
|
||||||
* [Changelog](https://github.com/yaahc/eyre/blob/v0.6.5/CHANGELOG.md)
|
* [Changelog](https://github.com/yaahc/eyre/blob/v0.6.5/CHANGELOG.md)
|
||||||
* [Commits](https://github.com/yaahc/eyre/compare/v0.6.3...v0.6.5)
|
* [Commits](https://github.com/yaahc/eyre/compare/v0.6.3...v0.6.5)
|
||||||
* Bump regex from 1.4.2 to 1.4.3. \[dependabot\[bot]]
|
* Bump regex from 1.4.2 to 1.4.3. \[dependabot\[bot\]\]
|
||||||
|
|
||||||
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.2 to 1.4.3.
|
Bumps [regex](https://github.com/rust-lang/regex) from 1.4.2 to 1.4.3.
|
||||||
|
|
||||||
@ -375,7 +402,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Find uppercased notes when referenced with lowercase. \[Nick Groenen]
|
* Find uppercased notes when referenced with lowercase. \[Nick Groenen\]
|
||||||
|
|
||||||
This commit fixes a bug where, if a note contained uppercase characters
|
This commit fixes a bug where, if a note contained uppercase characters
|
||||||
(for example `Note.md`) but was referred to using lowercase
|
(for example `Note.md`) but was referred to using lowercase
|
||||||
@ -385,7 +412,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
* Add --no-recursive-embeds to break infinite recursion cycles. \[Nick Groenen]
|
* Add --no-recursive-embeds to break infinite recursion cycles. \[Nick Groenen\]
|
||||||
|
|
||||||
It's possible to end up with "recursive embeds" when two notes embed
|
It's possible to end up with "recursive embeds" when two notes embed
|
||||||
each other. This happens for example when a `Note A.md` contains
|
each other. This happens for example when a `Note A.md` contains
|
||||||
@ -400,14 +427,14 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
See also: https://github.com/zoni/obsidian-export/issues/1
|
See also: https://github.com/zoni/obsidian-export/issues/1
|
||||||
|
|
||||||
* Make walk options configurable on CLI. \[Nick Groenen]
|
* Make walk options configurable on CLI. \[Nick Groenen\]
|
||||||
|
|
||||||
By default hidden files, patterns listed in `.export-ignore` as well as
|
By default hidden files, patterns listed in `.export-ignore` as well as
|
||||||
any files ignored by git are excluded from exports. This behavior has
|
any files ignored by git are excluded from exports. This behavior has
|
||||||
been made configurable on the CLI using the new flags `--hidden`,
|
been made configurable on the CLI using the new flags `--hidden`,
|
||||||
`--ignore-file` and `--no-git`.
|
`--ignore-file` and `--no-git`.
|
||||||
|
|
||||||
* Support links referencing headings. \[Nick Groenen]
|
* Support links referencing headings. \[Nick Groenen\]
|
||||||
|
|
||||||
Previously, links referencing a heading (`[[note#heading]]`) would just
|
Previously, links referencing a heading (`[[note#heading]]`) would just
|
||||||
link to the file name without including an anchor in the link target.
|
link to the file name without including an anchor in the link target.
|
||||||
@ -427,7 +454,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
end with a smiley. The slug library, and thus obsidian-export, will
|
end with a smiley. The slug library, and thus obsidian-export, will
|
||||||
avoid such dangling dashes).
|
avoid such dangling dashes).
|
||||||
|
|
||||||
* Support embeds referencing headings. \[Nick Groenen]
|
* Support embeds referencing headings. \[Nick Groenen\]
|
||||||
|
|
||||||
Previously, partial embeds (`![[note#heading]]`) would always include
|
Previously, partial embeds (`![[note#heading]]`) would always include
|
||||||
the entire file into the source note. Now, such embeds will only include
|
the entire file into the source note. Now, such embeds will only include
|
||||||
@ -437,20 +464,20 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
* Print warnings to stderr rather than stdout. \[Nick Groenen]
|
* Print warnings to stderr rather than stdout. \[Nick Groenen\]
|
||||||
|
|
||||||
Warning messages emitted when encountering broken links/references will
|
Warning messages emitted when encountering broken links/references will
|
||||||
now be printed to stderr as opposed to stdout.
|
now be printed to stderr as opposed to stdout.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
* Include filter_fn field in WalkOptions debug display. \[Nick Groenen]
|
* Include filter_fn field in WalkOptions debug display. \[Nick Groenen\]
|
||||||
|
|
||||||
## v0.4.0 (2020-12-23)
|
## v0.4.0 (2020-12-23)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Correct relative links within embedded notes. \[Nick Groenen]
|
* Correct relative links within embedded notes. \[Nick Groenen\]
|
||||||
|
|
||||||
Links within an embedded note would point to other local resources
|
Links within an embedded note would point to other local resources
|
||||||
relative to the filesystem location of the note being embedded.
|
relative to the filesystem location of the note being embedded.
|
||||||
@ -463,13 +490,13 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
* Add brief library documentation to all public types and functions. \[Nick Groenen]
|
* Add brief library documentation to all public types and functions. \[Nick Groenen\]
|
||||||
|
|
||||||
## v0.3.0 (2020-12-21)
|
## v0.3.0 (2020-12-21)
|
||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
* Report file tree when RecursionLimitExceeded is hit. \[Nick Groenen]
|
* Report file tree when RecursionLimitExceeded is hit. \[Nick Groenen\]
|
||||||
|
|
||||||
This refactors the Context to maintain a list of all the files which
|
This refactors the Context to maintain a list of all the files which
|
||||||
have been processed so far in a chain of embeds. This information is
|
have been processed so far in a chain of embeds. This information is
|
||||||
@ -478,37 +505,37 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
* Add extra whitespace around multi-line warnings. \[Nick Groenen]
|
* Add extra whitespace around multi-line warnings. \[Nick Groenen\]
|
||||||
|
|
||||||
This makes errors a bit easier to distinguish after a number of warnings
|
This makes errors a bit easier to distinguish after a number of warnings
|
||||||
has been printed.
|
has been printed.
|
||||||
|
|
||||||
### Other
|
### Other
|
||||||
|
|
||||||
* Setup gitchangelog. \[Nick Groenen]
|
* Setup gitchangelog. \[Nick Groenen\]
|
||||||
|
|
||||||
This adds a changelog (CHANGES.md) which is automatically generated with
|
This adds a changelog (CHANGES.md) which is automatically generated with
|
||||||
[gitchangelog](https://github.com/vaab/gitchangelog).
|
[gitchangelog](https://github.com/vaab/gitchangelog).
|
||||||
|
|
||||||
## v0.2.0 (2020-12-13)
|
## v0.2.0 (2020-12-13)
|
||||||
|
|
||||||
* Allow custom filter function to be passed with WalkOptions. \[Nick Groenen]
|
* Allow custom filter function to be passed with WalkOptions. \[Nick Groenen\]
|
||||||
|
|
||||||
* Re-export vault_contents and WalkOptions as pub from crate root. \[Nick Groenen]
|
* Re-export vault_contents and WalkOptions as pub from crate root. \[Nick Groenen\]
|
||||||
|
|
||||||
* Run mdbook hook against README.md too. \[Nick Groenen]
|
* Run mdbook hook against README.md too. \[Nick Groenen\]
|
||||||
|
|
||||||
* Update installation instructions. \[Nick Groenen]
|
* Update installation instructions. \[Nick Groenen\]
|
||||||
|
|
||||||
Installation no longer requires a git repository URL now that a crate is
|
Installation no longer requires a git repository URL now that a crate is
|
||||||
published.
|
published.
|
||||||
|
|
||||||
* Add MdBook generation script and precommit hook. \[Nick Groenen]
|
* Add MdBook generation script and precommit hook. \[Nick Groenen\]
|
||||||
|
|
||||||
* Add more reliable non-ASCII tetscase. \[Nick Groenen]
|
* Add more reliable non-ASCII tetscase. \[Nick Groenen\]
|
||||||
|
|
||||||
* Create FUNDING.yml. \[Nick Groenen]
|
* Create FUNDING.yml. \[Nick Groenen\]
|
||||||
|
|
||||||
## v0.1.0 (2020-11-28)
|
## v0.1.0 (2020-11-28)
|
||||||
|
|
||||||
* Public release. \[Nick Groenen]
|
* Public release. \[Nick Groenen\]
|
||||||
|
@ -10,6 +10,8 @@ Running `obsidian-export --version` should print a version number rather than gi
|
|||||||
>
|
>
|
||||||
> For example `~/Downloads/obsidian-export --version` on Mac/Linux or `~\Downloads\obsidian-export --version` on Windows (PowerShell).
|
> For example `~/Downloads/obsidian-export --version` on Mac/Linux or `~\Downloads\obsidian-export --version` on Windows (PowerShell).
|
||||||
|
|
||||||
|
## Exporting notes
|
||||||
|
|
||||||
In it's most basic form, `obsidian-export` takes just two mandatory arguments, a source and a destination:
|
In it's most basic form, `obsidian-export` takes just two mandatory arguments, a source and a destination:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -31,6 +33,31 @@ obsidian-export my-obsidian-vault/some-note.md /tmp/export/
|
|||||||
obsidian-export my-obsidian-vault/some-note.md /tmp/exported-note.md
|
obsidian-export my-obsidian-vault/some-note.md /tmp/exported-note.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that in this mode, obsidian-export sees `some-note.md` as being the only file that exists in your vault so references to other notes won't be resolved.
|
||||||
|
This is by design.
|
||||||
|
|
||||||
|
If you'd like to export a single note while resolving links or embeds to other areas in your vault then you should instead specify the root of your vault as the source, passing the file you'd like to export with `--start-at`, as described in the next section.
|
||||||
|
|
||||||
|
### Exporting a partial vault
|
||||||
|
|
||||||
|
Using the `--start-at` argument, you can export just a subset of your vault.
|
||||||
|
Given the following vault structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
my-obsidian-vault
|
||||||
|
├── Notes/
|
||||||
|
├── Books/
|
||||||
|
└── People/
|
||||||
|
```
|
||||||
|
|
||||||
|
This will export only the notes in the `Books` directory to `exported-notes`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
obsidian-export my-obsidian-vault --start-at my-obsidian-vault/Books exported-notes
|
||||||
|
```
|
||||||
|
|
||||||
|
In this mode, all notes under the source (the first argument) are considered part of the vault so any references to these files will remain intact, even if they're not part of the exported notes.
|
||||||
|
|
||||||
## Character encodings
|
## Character encodings
|
||||||
|
|
||||||
At present, UTF-8 character encoding is assumed for all note text as well as filenames.
|
At present, UTF-8 character encoding is assumed for all note text as well as filenames.
|
||||||
|
43
src/lib.rs
43
src/lib.rs
@ -211,6 +211,7 @@ pub enum PostprocessorResult {
|
|||||||
pub struct Exporter<'a> {
|
pub struct Exporter<'a> {
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
destination: PathBuf,
|
destination: PathBuf,
|
||||||
|
start_at: PathBuf,
|
||||||
frontmatter_strategy: FrontmatterStrategy,
|
frontmatter_strategy: FrontmatterStrategy,
|
||||||
vault_contents: Option<Vec<PathBuf>>,
|
vault_contents: Option<Vec<PathBuf>>,
|
||||||
walk_options: WalkOptions<'a>,
|
walk_options: WalkOptions<'a>,
|
||||||
@ -239,11 +240,12 @@ impl<'a> fmt::Debug for Exporter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Exporter<'a> {
|
impl<'a> Exporter<'a> {
|
||||||
/// Create a new exporter which reads notes from `source` and exports these to
|
/// Create a new exporter which reads notes from `root` and exports these to
|
||||||
/// `destination`.
|
/// `destination`.
|
||||||
pub fn new(source: PathBuf, destination: PathBuf) -> Exporter<'a> {
|
pub fn new(root: PathBuf, destination: PathBuf) -> Exporter<'a> {
|
||||||
Exporter {
|
Exporter {
|
||||||
root: source,
|
start_at: root.clone(),
|
||||||
|
root,
|
||||||
destination,
|
destination,
|
||||||
frontmatter_strategy: FrontmatterStrategy::Auto,
|
frontmatter_strategy: FrontmatterStrategy::Auto,
|
||||||
walk_options: WalkOptions::default(),
|
walk_options: WalkOptions::default(),
|
||||||
@ -253,6 +255,15 @@ impl<'a> Exporter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a custom starting point for the export.
|
||||||
|
///
|
||||||
|
/// Normally all notes under `root` (except for notes excluded by ignore rules) will be exported.
|
||||||
|
/// When `start_at` is set, only notes under this path will be exported to the target destination.
|
||||||
|
pub fn start_at(&mut self, start_at: PathBuf) -> &mut Exporter<'a> {
|
||||||
|
self.start_at = start_at;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the [`WalkOptions`] to be used for this exporter.
|
/// Set the [`WalkOptions`] to be used for this exporter.
|
||||||
pub fn walk_options(&mut self, options: WalkOptions<'a>) -> &mut Exporter<'a> {
|
pub fn walk_options(&mut self, options: WalkOptions<'a>) -> &mut Exporter<'a> {
|
||||||
self.walk_options = options;
|
self.walk_options = options;
|
||||||
@ -292,13 +303,17 @@ impl<'a> Exporter<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a single file is specified, we can short-circuit contruction of walk and associated
|
self.vault_contents = Some(vault_contents(
|
||||||
// directory traversal. This also allows us to accept destination as either a file or a
|
self.root.as_path(),
|
||||||
// directory name.
|
self.walk_options.clone(),
|
||||||
if self.root.is_file() {
|
)?);
|
||||||
self.vault_contents = Some(vec![self.root.clone()]);
|
|
||||||
|
// When a single file is specified, just need to export that specific file instead of
|
||||||
|
// iterating over all discovered files. This also allows us to accept destination as either
|
||||||
|
// a file or a directory name.
|
||||||
|
if self.root.is_file() || self.start_at.is_file() {
|
||||||
let source_filename = self
|
let source_filename = self
|
||||||
.root
|
.start_at
|
||||||
.file_name()
|
.file_name()
|
||||||
.expect("File without a filename? How is that possible?")
|
.expect("File without a filename? How is that possible?")
|
||||||
.to_string_lossy();
|
.to_string_lossy();
|
||||||
@ -317,7 +332,7 @@ impl<'a> Exporter<'a> {
|
|||||||
self.destination.clone()
|
self.destination.clone()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return self.export_note(&self.root, &destination);
|
return self.export_note(&self.start_at, &destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.destination.exists() {
|
if !self.destination.exists() {
|
||||||
@ -325,19 +340,15 @@ impl<'a> Exporter<'a> {
|
|||||||
path: self.destination.clone(),
|
path: self.destination.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vault_contents = Some(vault_contents(
|
|
||||||
self.root.as_path(),
|
|
||||||
self.walk_options.clone(),
|
|
||||||
)?);
|
|
||||||
self.vault_contents
|
self.vault_contents
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone()
|
.clone()
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
|
.filter(|file| file.starts_with(&self.start_at))
|
||||||
.try_for_each(|file| {
|
.try_for_each(|file| {
|
||||||
let relative_path = file
|
let relative_path = file
|
||||||
.strip_prefix(&self.root.clone())
|
.strip_prefix(&self.start_at.clone())
|
||||||
.expect("file should always be nested under root")
|
.expect("file should always be nested under root")
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
let destination = &self.destination.join(&relative_path);
|
let destination = &self.destination.join(&relative_path);
|
||||||
|
15
src/main.rs
15
src/main.rs
@ -13,12 +13,15 @@ struct Opts {
|
|||||||
#[options(help = "Display version information")]
|
#[options(help = "Display version information")]
|
||||||
version: bool,
|
version: bool,
|
||||||
|
|
||||||
#[options(help = "Source file containing reference", free, required)]
|
#[options(help = "Read notes from this source", free, required)]
|
||||||
source: Option<PathBuf>,
|
source: Option<PathBuf>,
|
||||||
|
|
||||||
#[options(help = "Destination file being linked to", free, required)]
|
#[options(help = "Write notes to this destination", free, required)]
|
||||||
destination: Option<PathBuf>,
|
destination: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[options(no_short, help = "Only export notes under this sub-path")]
|
||||||
|
start_at: Option<PathBuf>,
|
||||||
|
|
||||||
#[options(
|
#[options(
|
||||||
help = "Frontmatter strategy (one of: always, never, auto)",
|
help = "Frontmatter strategy (one of: always, never, auto)",
|
||||||
no_short,
|
no_short,
|
||||||
@ -64,7 +67,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let args = Opts::parse_args_default_or_exit();
|
let args = Opts::parse_args_default_or_exit();
|
||||||
let source = args.source.unwrap();
|
let root = args.source.unwrap();
|
||||||
let destination = args.destination.unwrap();
|
let destination = args.destination.unwrap();
|
||||||
|
|
||||||
let walk_options = WalkOptions {
|
let walk_options = WalkOptions {
|
||||||
@ -74,11 +77,15 @@ fn main() {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut exporter = Exporter::new(source, destination);
|
let mut exporter = Exporter::new(root, destination);
|
||||||
exporter.frontmatter_strategy(args.frontmatter_strategy);
|
exporter.frontmatter_strategy(args.frontmatter_strategy);
|
||||||
exporter.process_embeds_recursively(!args.no_recursive_embeds);
|
exporter.process_embeds_recursively(!args.no_recursive_embeds);
|
||||||
exporter.walk_options(walk_options);
|
exporter.walk_options(walk_options);
|
||||||
|
|
||||||
|
if let Some(path) = args.start_at {
|
||||||
|
exporter.start_at(path);
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(err) = exporter.run() {
|
if let Err(err) = exporter.run() {
|
||||||
match err {
|
match err {
|
||||||
ExportError::FileExportError {
|
ExportError::FileExportError {
|
||||||
|
@ -161,6 +161,79 @@ fn test_single_file_to_file() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_at_subdir() {
|
||||||
|
let tmp_dir = TempDir::new().expect("failed to make tempdir");
|
||||||
|
let mut exporter = Exporter::new(
|
||||||
|
PathBuf::from("tests/testdata/input/start-at/"),
|
||||||
|
tmp_dir.path().to_path_buf(),
|
||||||
|
);
|
||||||
|
exporter.start_at(PathBuf::from("tests/testdata/input/start-at/subdir"));
|
||||||
|
exporter.run().unwrap();
|
||||||
|
|
||||||
|
let expected = if cfg!(windows) {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/subdir/Note B.md")
|
||||||
|
.unwrap()
|
||||||
|
.replace("/", "\\")
|
||||||
|
} else {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/subdir/Note B.md").unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected,
|
||||||
|
read_to_string(tmp_dir.path().clone().join(PathBuf::from("Note B.md"))).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_at_file_within_subdir_destination_is_dir() {
|
||||||
|
let tmp_dir = TempDir::new().expect("failed to make tempdir");
|
||||||
|
let mut exporter = Exporter::new(
|
||||||
|
PathBuf::from("tests/testdata/input/start-at/"),
|
||||||
|
tmp_dir.path().to_path_buf(),
|
||||||
|
);
|
||||||
|
exporter.start_at(PathBuf::from(
|
||||||
|
"tests/testdata/input/start-at/subdir/Note B.md",
|
||||||
|
));
|
||||||
|
exporter.run().unwrap();
|
||||||
|
|
||||||
|
let expected = if cfg!(windows) {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/single-file/Note B.md")
|
||||||
|
.unwrap()
|
||||||
|
.replace("/", "\\")
|
||||||
|
} else {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/single-file/Note B.md").unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expected,
|
||||||
|
read_to_string(tmp_dir.path().clone().join(PathBuf::from("Note B.md"))).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_at_file_within_subdir_destination_is_file() {
|
||||||
|
let tmp_dir = TempDir::new().expect("failed to make tempdir");
|
||||||
|
let dest = tmp_dir.path().clone().join(PathBuf::from("note.md"));
|
||||||
|
let mut exporter = Exporter::new(
|
||||||
|
PathBuf::from("tests/testdata/input/start-at/"),
|
||||||
|
dest.clone(),
|
||||||
|
);
|
||||||
|
exporter.start_at(PathBuf::from(
|
||||||
|
"tests/testdata/input/start-at/subdir/Note B.md",
|
||||||
|
));
|
||||||
|
exporter.run().unwrap();
|
||||||
|
|
||||||
|
let expected = if cfg!(windows) {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/single-file/Note B.md")
|
||||||
|
.unwrap()
|
||||||
|
.replace("/", "\\")
|
||||||
|
} else {
|
||||||
|
read_to_string("tests/testdata/expected/start-at/single-file/Note B.md").unwrap()
|
||||||
|
};
|
||||||
|
assert_eq!(expected, read_to_string(dest).unwrap(),);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not_existing_source() {
|
fn test_not_existing_source() {
|
||||||
let tmp_dir = TempDir::new().expect("failed to make tempdir");
|
let tmp_dir = TempDir::new().expect("failed to make tempdir");
|
||||||
|
4
tests/testdata/expected/start-at/single-file/Note B.md
vendored
Normal file
4
tests/testdata/expected/start-at/single-file/Note B.md
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is note B. It links to:
|
||||||
|
|
||||||
|
* [Note A](../Note%20A.md)
|
||||||
|
* [Note C](Note%20C.md)
|
4
tests/testdata/expected/start-at/subdir/Note B.md
vendored
Normal file
4
tests/testdata/expected/start-at/subdir/Note B.md
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is note B. It links to:
|
||||||
|
|
||||||
|
* [Note A](../Note%20A.md)
|
||||||
|
* [Note C](Note%20C.md)
|
1
tests/testdata/expected/start-at/subdir/Note C.md
vendored
Normal file
1
tests/testdata/expected/start-at/subdir/Note C.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is note C.
|
1
tests/testdata/input/start-at/Note A.md
vendored
Normal file
1
tests/testdata/input/start-at/Note A.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is note A.
|
4
tests/testdata/input/start-at/subdir/Note B.md
vendored
Normal file
4
tests/testdata/input/start-at/subdir/Note B.md
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
This is note B. It links to:
|
||||||
|
|
||||||
|
- [[Note A]]
|
||||||
|
- [[Note C]]
|
1
tests/testdata/input/start-at/subdir/Note C.md
vendored
Normal file
1
tests/testdata/input/start-at/subdir/Note C.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
This is note C.
|
Loading…
Reference in New Issue
Block a user