Skip to content

Commit

Permalink
Implement line option for show
Browse files Browse the repository at this point in the history
- Fix bug displaying file tree and add better test
- Add vscode launch.json config for debugging

run rustfmt
  • Loading branch information
allie-wake-up committed Jan 5, 2023
1 parent 27b84e3 commit 790f1a8
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 82 deletions.
67 changes: 67 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in library 'pointguard'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=pointguard"
],
"filter": {
"name": "pointguard",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'pg'",
"cargo": {
"args": [
"build",
"--bin=pg",
"--package=pointguard"
],
"filter": {
"name": "pg",
"kind": "bin"
}
},
"args": ["show"],
"env": {
"POINT_GUARD_DIR": "${workspaceFolder}/test-store-enc"
},
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'pg'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=pg",
"--package=pointguard"
],
"filter": {
"name": "pg",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}
5 changes: 2 additions & 3 deletions setup-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ echo "$1" > test-store-enc/.gpg-id
function encrypt {
for file in $1/*
do
if [ -d $file ]
then
if [ -d $file ]; then
mkdir -p "../test-store-enc/$file"
encrypt $file
else
elif [ -f $file ]; then
gpg --yes --batch -o "../test-store-enc/$file" --recipient "$KEY" -e "$file"
fi
done
Expand Down
2 changes: 1 addition & 1 deletion src/clip.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::error::Result;
use crate::opts::Clip;
use std::io::{self, Read};
use cli_clipboard::{ClipboardContext, ClipboardProvider};
use std::io::{self, Read};

pub fn clip(opts: Clip) -> Result<()> {
let mut clipboard = ClipboardContext::new()?;
Expand Down
5 changes: 1 addition & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ pub use settings::Settings;

pub fn run(buffer: &mut dyn std::io::Write, opts: Opts, settings: Settings) -> error::Result<()> {
let show_opts = opts.show;
match opts
.subcmd
.unwrap_or_else(|| SubCommand::Show(show_opts))
{
match opts.subcmd.unwrap_or_else(|| SubCommand::Show(show_opts)) {
SubCommand::Clip(clip_opts) => clip::clip(clip_opts),
SubCommand::Show(show_opts) => show::show(buffer, show_opts, settings),
}
Expand Down
12 changes: 8 additions & 4 deletions src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,19 @@ pub struct Show {
/// A password file or directory to display
pub input: Option<String>,
/// Copy to clipboard instead of printing
#[clap(name="clip", long, short)]
#[clap(name = "clip", long, short)]
pub clip: bool,
/// Line number to print or copy (starts with 1).
#[clap(name="line", long, short)]
pub line: Option<i32>,
#[clap(name = "line", long, short)]
pub line: Option<usize>,
}

impl Show {
pub fn new(input: Option<String>) -> Self {
Show { input, clip: false, line: None }
Show {
input,
clip: false,
line: None,
}
}
}
102 changes: 33 additions & 69 deletions src/show.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::{PointGuardError, Result};
use crate::error::Result;
use crate::gpg;
use crate::opts::Show;
use crate::settings::Settings;
Expand All @@ -10,76 +10,19 @@ use std::{
path::Path,
process::{Command, Stdio},
};
use walkdir::{DirEntry, WalkDir};

mod pgtree;
use pgtree::TreeBuilder;

fn is_hidden(entry: &DirEntry) -> bool {
entry
.file_name()
.to_str()
.map(|s| s.starts_with('.'))
.unwrap_or(false)
}

fn display_path(path: &Path) -> Result<String> {
Ok(path
.file_stem()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Found a file with no name"))?
.to_str()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "File name is not valid unicode")
})?
.to_string())
}

fn print_tree(buffer: &mut dyn io::Write, path: &Path, input: Option<String>) -> Result<()> {
let mut builder =
TreeBuilder::new(input.unwrap_or_else(|| String::from("Point Guard Password Store")));
let walker = WalkDir::new(&path).into_iter();
let mut depth = 1;
for entry in walker.filter_entry(|e| !is_hidden(e)) {
let entry = match entry {
Ok(entry) => entry,
// TODO: should this return an error?
Err(_e) => continue,
};
if entry.depth() == 0 {
continue;
}
let path = entry.path();
if path.is_dir() {
builder.begin_child(display_path(path)?);
depth += 1;
} else if entry.depth() == depth {
builder.add_empty_child(display_path(path)?);
} else {
builder.end_child();
builder.add_empty_child(display_path(path)?);
depth -= 1;
}
}
let mut root = builder.build();
root.sort();
output::write_tree(&root, buffer)?;
Ok(())
}

fn clip(buffer: &mut dyn io::Write, pw: String, clip_time: u64, opts: Show) -> Result<()> {
fn clip(buffer: &mut dyn io::Write, pw: &str, clip_time: u64, opts: Show) -> Result<()> {
let exe = env::current_exe()?;
let mut child = Command::new(exe)
.arg("clip")
.arg(clip_time.to_string())
.stdin(Stdio::piped())
.spawn()?;
let child_stdin = child.stdin.as_mut();
let child_stdin = child_stdin.ok_or_else(|| {
PointGuardError::Other(anyhow!("Error launching child to copy to clipboard."))
})?;
let pw = pw.lines().next().ok_or_else(||
PointGuardError::Other(anyhow!("Error reading the line from the password file."))
)?;
let child_stdin =
child_stdin.ok_or_else(|| anyhow!("Error launching child to copy to clipboard."))?;
child_stdin.write_all(pw.as_bytes())?;
writeln!(
buffer,
Expand All @@ -90,6 +33,31 @@ fn clip(buffer: &mut dyn io::Write, pw: String, clip_time: u64, opts: Show) -> R
Ok(())
}

fn show_password(
buffer: &mut dyn io::Write,
file: &Path,
clip_time: u64,
opts: Show,
) -> Result<()> {
let pw = gpg::decrypt(file)?;
let pw = match &opts.line {
Some(line) => pw
.lines()
.nth(line - 1)
.ok_or_else(|| anyhow!("Error reading line {} of the password file", line)),
None => Ok(&pw[..]),
}?;
if opts.clip {
clip(buffer, pw, clip_time, opts)
} else {
match opts.line {
Some(_) => writeln!(buffer, "{}", pw)?,
None => write!(buffer, "{}", pw)?,
}
Ok(())
}
}

pub fn show(buffer: &mut dyn io::Write, opts: Show, settings: Settings) -> Result<()> {
let (path, file) = match &opts.input {
Some(name) => (
Expand All @@ -99,15 +67,11 @@ pub fn show(buffer: &mut dyn io::Write, opts: Show, settings: Settings) -> Resul
None => (settings.dir.clone(), settings.dir),
};
if file.exists() && !file.is_dir() {
let pw = gpg::decrypt(&file)?;
if opts.clip {
clip(buffer, pw, settings.clip_time, opts)
} else {
write!(buffer, "{}", pw)?;
Ok(())
}
show_password(buffer, &file, settings.clip_time, opts)
} else if path.is_dir() {
print_tree(buffer, &path, opts.input)
let root = pgtree::build_tree(&path, opts.input)?;
output::write_tree(&root, buffer)?;
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
Expand Down
84 changes: 83 additions & 1 deletion src/show/pgtree.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use ptree::item::TreeItem;
use ptree::style::Style;
use std::borrow::Cow;
use std::io::{Result, Write};
use std::io::{self, Result, Write};
use std::path::Path;
use walkdir::{DirEntry, WalkDir};

#[derive(Debug)]
pub struct TreeBuilder {
Expand Down Expand Up @@ -80,3 +82,83 @@ impl TreeItem for Tree {
Cow::from(&self.children)
}
}

fn is_hidden(entry: &DirEntry) -> bool {
entry
.file_name()
.to_str()
.map(|s| s.starts_with('.'))
.unwrap_or(false)
}

fn display_path(path: &Path) -> Result<String> {
Ok(path
.file_stem()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Found a file with no name"))?
.to_str()
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "File name is not valid unicode")
})?
.to_string())
}

pub fn build_tree(path: &Path, input: Option<String>) -> Result<Tree> {
let mut builder =
TreeBuilder::new(input.unwrap_or_else(|| String::from("Point Guard Password Store")));
let walker = WalkDir::new(&path).into_iter();
let mut depth = 1;
for entry in walker.filter_entry(|e| !is_hidden(e)) {
let entry = match entry {
Ok(entry) => entry,
// TODO: should this return an error?
Err(_e) => continue,
};
if entry.depth() == 0 {
continue;
}
let path = entry.path();
if entry.depth() == depth {
if path.is_dir() {
builder.begin_child(display_path(path)?);
depth += 1;
} else {
builder.add_empty_child(display_path(path)?);
}
} else {
builder.end_child();
depth -= 1;
if path.is_dir() {
builder.begin_child(display_path(path)?);
depth += 1;
} else {
builder.add_empty_child(display_path(path)?);
}
}
}
let mut root = builder.build();
root.sort();
Ok(root)
}

#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;

#[test]
fn build_tree_test() {
let mut tree = build_tree(&PathBuf::from("test-store-enc"), None).unwrap();
tree.sort();
assert_eq!(tree.file_stem, "Point Guard Password Store");
assert_eq!(tree.children.len(), 6);
assert_eq!(tree.children[0].file_stem, "dir");
assert_eq!(tree.children[0].children.len(), 0);
assert_eq!(tree.children[1].file_stem, "dir");
assert_eq!(tree.children[1].children.len(), 2);
assert_eq!(tree.children[1].children[0].file_stem, "test");
assert_eq!(tree.children[1].children[0].children.len(), 0);
assert_eq!(tree.children[2].file_stem, "empty");
assert_eq!(tree.children[3].file_stem, "empty1");
assert_eq!(tree.children[4].file_stem, "pointguard.dev");
}
}

0 comments on commit 790f1a8

Please sign in to comment.