-
-
Notifications
You must be signed in to change notification settings - Fork 1k
feat(output): implement JSON output #1800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Dustin-Jiang
wants to merge
39
commits into
sharkdp:master
Choose a base branch
from
Dustin-Jiang:feature-yaml
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
ffec773
feat(output): add yaml output
Dustin-Jiang 97d6bfb
docs: update CHANGELOG
Dustin-Jiang cdb7bc0
fix: escape path, and disable permission display on Windows
Dustin-Jiang c9cbb25
fix(ci): fix warnings in cargo clippy
Dustin-Jiang 192ca92
refactor: make output stateful
Dustin-Jiang 602b389
feat(output): add json output
Dustin-Jiang eb2f10d
fix(ci): fix warnings in cargo clippy
Dustin-Jiang 4ea0ed3
fix: fix function calling in Windows
Dustin-Jiang e225946
fix: fix reference mutable type annotation in Windows
Dustin-Jiang 977ee0e
fix: resolve suggested changes
Dustin-Jiang 6434ee5
fix: move JSON array printing to Printer
Dustin-Jiang b2d385f
feat: implement NDJSON output
Dustin-Jiang 703b32f
fix(ci): fix warnings in cargo clippy
Dustin-Jiang 650e86c
tests: add tests for `--output` flags
Dustin-Jiang 2e463c7
docs: update manpage for `--output` flags
Dustin-Jiang e46cce0
Merge branch 'master' into feature-yaml
Dustin-Jiang 10570e9
tests: fix invalid utf8 base64 test
Dustin-Jiang 949a5aa
docs: update manpage to change "ndjson" to "jsonl"
Dustin-Jiang cb3ef97
fix: change FileDetail creating logic and base64 import
Dustin-Jiang 60ecc09
fix: change ndjson flag to commonly used jsonl
Dustin-Jiang 7c9f1d8
fix: replace String to &str with lifetime, adopt as_encoded_bytes
Dustin-Jiang 2c1bdb5
feat: add --json flag for JSONL output
Dustin-Jiang e831976
docs: add --json flag to manpage
Dustin-Jiang 30c9e86
Merge branch 'master' into feature-yaml
Dustin-Jiang 49654f4
fix(clippy): collapse if blocks
Dustin-Jiang e4741c0
Merge remote-tracking branch 'origin/master' into feature-yaml
Dustin-Jiang 4a0ecc5
Merge remote-tracking branch 'origin/master' into feature-yaml
Dustin-Jiang 56d347e
fix: remove the `--output` flag
Dustin-Jiang 6db4409
tests: fix `--output` tests to `--json`
Dustin-Jiang c2c8497
docs: add fields explaination in manual
Dustin-Jiang 13d0868
docs: change the flag to `--json` in CHANGELOG.md
Dustin-Jiang 47ee6ce
fix(printer): make `Priner.stdout` private
Dustin-Jiang 58b90f7
Merge remote-tracking branch 'origin/master' into feature-yaml
Dustin-Jiang 8f36886
fix: use `jiff::Timestamp::try_from` to process SystemTime
Dustin-Jiang 64da6b5
Merge remote-tracking branch 'origin/master' into feature-yaml
Dustin-Jiang 474cdd3
refactor: json fmt mod
tmccombs 766d684
Merge branch 'master' into feature-yaml
tmccombs 3a620b0
refactor: json fmt mod
tmccombs 2adb30d
fix: Use path separator in json output
tmccombs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| ## Features | ||
|
|
||
| - Add `--json` flag for JSONL format output. | ||
|
|
||
| ## Bugfixes | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| use std::borrow::Cow; | ||
| use std::fs::{FileType, Metadata}; | ||
| use std::io::Write; | ||
| #[cfg(unix)] | ||
| use std::os::unix::{ffi::OsStrExt, fs::MetadataExt}; | ||
| use std::path::{MAIN_SEPARATOR, Path}; | ||
| use std::time::SystemTime; | ||
|
|
||
| use base64::{Engine as _, prelude::BASE64_STANDARD}; | ||
| use jiff::Timestamp; | ||
|
|
||
| pub fn output_json<W: Write>( | ||
| out: &mut W, | ||
| path: &Path, | ||
| filetype: Option<FileType>, | ||
| metadata: Option<&Metadata>, | ||
| path_separator: &Option<String>, | ||
| ) -> std::io::Result<()> { | ||
| out.write_all(b"{")?; | ||
|
|
||
| // Print the path as an object that either has a "text" key containing the | ||
| // utf8 path, or a "bytes" key with the base64 encoded bytes of the path | ||
| #[cfg(unix)] | ||
| match path.to_str() { | ||
| Some(text) => { | ||
| let final_path: Cow<str> = if let Some(sep) = path_separator { | ||
| text.replace(MAIN_SEPARATOR, sep).into() | ||
| } else { | ||
| text.into() | ||
| }; | ||
| // NB: This assumes that rust's debug output for a string | ||
| // is a valid JSON string. At time of writing this is the case | ||
| // but it is possible, though unlikely, that this could change | ||
| // in the future. | ||
| write!(out, r#""path":{{"text":{:?}}}"#, final_path)?; | ||
| } | ||
| None => { | ||
| let encoded_bytes = BASE64_STANDARD.encode(path.as_os_str().as_bytes()); | ||
| write!(out, r#""path":{{"bytes":"{}"}}"#, encoded_bytes)?; | ||
| } | ||
| }; | ||
| // On non-unix platforms, if the path isn't valid utf-8, | ||
| // we don't know what kind of encoding was used, and | ||
| // as_encoded_bytes() isn't necessarily stable between rust versions | ||
| // so the best we can really do is a lossy string | ||
| #[cfg(not(unix))] | ||
| write!(out, r#""path":{{"text":{:?}}}"#, path.to_string_lossy())?; | ||
|
|
||
| // print the type of file | ||
| let ft = match filetype { | ||
| Some(ft) if ft.is_dir() => "directory", | ||
| Some(ft) if ft.is_file() => "file", | ||
| Some(ft) if ft.is_symlink() => "symlink", | ||
| _ => "unknown", | ||
| }; | ||
| write!(out, r#","type":"{}""#, ft)?; | ||
|
|
||
| if let Some(meta) = metadata { | ||
| // Output the mode as octal | ||
| // We also need to mask it to just include the permission | ||
| // bits and not the file type bits (that is handled by "type" above) | ||
| #[cfg(unix)] | ||
| write!(out, r#","mode":"{:o}""#, meta.mode() & 0x7777)?; | ||
|
|
||
| write!(out, r#","size_bytes":{}"#, meta.len())?; | ||
|
|
||
| // would it be better to do these with os-specific functions? | ||
| if let Ok(modified) = meta.modified().map(json_timestamp) { | ||
| write!(out, r#","modified":"{}""#, modified)?; | ||
| } | ||
| if let Ok(accessed) = meta.accessed().map(json_timestamp) { | ||
| write!(out, r#","modified":"{}""#, accessed)?; | ||
| } | ||
| if let Ok(created) = meta.created().map(json_timestamp) { | ||
| write!(out, r#","modified":"{}""#, created)?; | ||
| } | ||
| } | ||
|
|
||
| out.write_all(b"}") | ||
| } | ||
|
|
||
| fn json_timestamp(time: SystemTime) -> Timestamp { | ||
| // System timestamps should always be valid, so assume that we can | ||
| // unwrap it | ||
| // If we ever do want to handle an error here, maybe convert to either the MAX or MIN | ||
| // timestamp depending on which side of the epoch the SystemTime is? | ||
| Timestamp::try_from(time).expect("Invalid timestamp") | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what ripgrep does. Although I'm not sure if it would be better to do one of the following:
OsStr::as_encoded_bytes. Currently, on windows, I think this uses WTF-8, but the documentation makes it clear this isn't guaranteed, and could change in a future version of rust.[u16](or an iterater over wide chars), then back to wtf8. Possibly using the "wtf8" crate. It seems wasteful, and not very performant, but at least we aren't reliant on rust's current implementation.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, ripgrep has been doing this lossy encoding for years. So far there hasn't been a single complaint. I think non-UTF-16 paths are extremely rare on Windows. Much rarer than non-UTF-8 paths on Unix.
If you want to avoid lossiness, I think this is the best option. ripgrep also does this for invalid UTF-8. (It looks like this PR does it too.) Namely, this avoids making it too easy to spread WTF-8 as an interchange format: