Correctly track modified/unmodified notes
This commit is contained in:
parent
cb2464133b
commit
42e507ec0a
2 changed files with 192 additions and 39 deletions
142
notes.sh
142
notes.sh
|
@ -151,6 +151,27 @@ get_header() {
|
||||||
grep -m1 "^${HEADER}: " < "$FILE" | sed -n "s/^${HEADER}: \(.*\)$/\1/p"
|
grep -m1 "^${HEADER}: " < "$FILE" | sed -n "s/^${HEADER}: \(.*\)$/\1/p"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find_file_by_id() {
|
||||||
|
ID="$1"
|
||||||
|
|
||||||
|
{ grep -l -r -m1 "^X-Note-Id: $ID$" "$BASEDIR" || true; } | sort| head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_find_file_by_id() {
|
||||||
|
FILE="$(find_file_by_id "$1")"
|
||||||
|
|
||||||
|
if [ ! -f "$FILE" ]; then
|
||||||
|
die "Note with ID <$ID> not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
find_files_by_id() {
|
||||||
|
ID="$1"
|
||||||
|
grep -l -r -m 1 "^X-Note-Id: $ID$" "$BASEDIR" || true
|
||||||
|
}
|
||||||
|
|
||||||
get_part() {
|
get_part() {
|
||||||
FILE="$1"
|
FILE="$1"
|
||||||
BOUNDARY="$2"
|
BOUNDARY="$2"
|
||||||
|
@ -237,11 +258,7 @@ unpack_mime() {
|
||||||
MIME_TYPE=$(get_header "$FILE" Content-Type)
|
MIME_TYPE=$(get_header "$FILE" Content-Type)
|
||||||
NOTE_ID=$(get_header "$FILE" X-Note-Id)
|
NOTE_ID=$(get_header "$FILE" X-Note-Id)
|
||||||
|
|
||||||
echo "X-Date: $DATE" > "$DIR/note.md"
|
get_headers "$FILE" | grep -v "^Content-Type\|^Content-Disposition\|^Date\|^MIME-Version" >> "$DIR/note.md"
|
||||||
if [ -n "$NOTE_ID" ]; then
|
|
||||||
echo "X-Note-Id: $NOTE_ID" >> "$DIR/note.md"
|
|
||||||
fi
|
|
||||||
echo "Subject: $SUBJECT" >> "$DIR/note.md"
|
|
||||||
echo "" >> "$DIR/note.md"
|
echo "" >> "$DIR/note.md"
|
||||||
|
|
||||||
TMP=$(mktemp --tmpdir="$DIR")
|
TMP=$(mktemp --tmpdir="$DIR")
|
||||||
|
@ -290,7 +307,7 @@ pack_mime() {
|
||||||
echo "MIME-Version: 1.0"
|
echo "MIME-Version: 1.0"
|
||||||
echo "Date: $MIME_TIMESTAMP"
|
echo "Date: $MIME_TIMESTAMP"
|
||||||
echo "Content-Type: multipart/mixed; boundary=\"$BOUNDARY\""
|
echo "Content-Type: multipart/mixed; boundary=\"$BOUNDARY\""
|
||||||
get_headers "$DIR/note.md"
|
get_headers "$DIR/note.md"
|
||||||
echo
|
echo
|
||||||
echo "--$BOUNDARY"
|
echo "--$BOUNDARY"
|
||||||
echo "Content-Type: text/plain; charset=utf-8"
|
echo "Content-Type: text/plain; charset=utf-8"
|
||||||
|
@ -310,20 +327,14 @@ pack_mime() {
|
||||||
echo "--$BOUNDARY--" >> "$FILE"
|
echo "--$BOUNDARY--" >> "$FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entry() {
|
input_note() {
|
||||||
INP="$1"
|
INP="$1"
|
||||||
DIR=$(mktemp -d)
|
OUTP="$2"
|
||||||
|
|
||||||
|
DIR="$(mktemp -d)"
|
||||||
ENTRY_FILE="$DIR/note.md"
|
ENTRY_FILE="$DIR/note.md"
|
||||||
ENTRY_FILE_START="$(mktemp)"
|
|
||||||
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
MIME_TIMESTAMP=$(LC_ALL="en_US.UTF-8" date "+$DATE_FORMAT")
|
||||||
UTC_TIMESTAMP=$(utc_timestamp)
|
UTC_TIMESTAMP=$(utc_timestamp)
|
||||||
cat > "$ENTRY_FILE" <<- EOF
|
|
||||||
X-Date: $UTC_TIMESTAMP
|
|
||||||
X-Note-Id: $(uuid)
|
|
||||||
Subject:
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cp "$ENTRY_FILE" "$ENTRY_FILE_START"
|
|
||||||
|
|
||||||
if [ -n "$INP" ] && [ ! -f "$INP" ] && [ ! -d "$INP" ]; then
|
if [ -n "$INP" ] && [ ! -f "$INP" ] && [ ! -d "$INP" ]; then
|
||||||
die "File or directory doesn't exist: $INP"
|
die "File or directory doesn't exist: $INP"
|
||||||
|
@ -340,12 +351,12 @@ new_entry() {
|
||||||
die "File doesn't exist: $INP/note.md"
|
die "File doesn't exist: $INP/note.md"
|
||||||
fi
|
fi
|
||||||
cp -n "$INP"/* "$DIR/" || true
|
cp -n "$INP"/* "$DIR/" || true
|
||||||
{
|
|
||||||
get_headers "$INP/note.md"
|
|
||||||
echo ""
|
|
||||||
get_body "$INP/note.md"
|
|
||||||
} >> "$DIR/note.md"
|
|
||||||
elif [ -t 0 ]; then
|
elif [ -t 0 ]; then
|
||||||
|
cat > "$ENTRY_FILE" <<- EOF
|
||||||
|
X-Date: $UTC_TIMESTAMP
|
||||||
|
X-Note-Id: $(uuid)
|
||||||
|
Subject:
|
||||||
|
EOF
|
||||||
OLD_DIR="$(pwd)"
|
OLD_DIR="$(pwd)"
|
||||||
cd "$DIR"
|
cd "$DIR"
|
||||||
"$EDITOR" "$ENTRY_FILE"
|
"$EDITOR" "$ENTRY_FILE"
|
||||||
|
@ -357,42 +368,95 @@ new_entry() {
|
||||||
fi
|
fi
|
||||||
MERGED_ENTRY_FILE="$(mktemp)"
|
MERGED_ENTRY_FILE="$(mktemp)"
|
||||||
|
|
||||||
HEADERS=$(get_headers "$ENTRY_FILE" | tac | sort -u -k1,1)
|
HEADERS=$(get_headers "$ENTRY_FILE")
|
||||||
|
BODY="$(get_body "$ENTRY_FILE")"
|
||||||
{
|
{
|
||||||
|
echo "$HEADERS" | grep -q "^X-Date:" || echo "X-Date: $UTC_TIMESTAMP"
|
||||||
|
echo "$HEADERS" | grep -q "^X-Note-Id:" || echo "X-Note-Id: $(uuid)"
|
||||||
echo "$HEADERS"
|
echo "$HEADERS"
|
||||||
|
echo "$HEADERS" | grep -q "^Subject:" || echo "Subject: "
|
||||||
echo ""
|
echo ""
|
||||||
get_body "$ENTRY_FILE"
|
get_body "$ENTRY_FILE"
|
||||||
} > "$MERGED_ENTRY_FILE"
|
} > "$MERGED_ENTRY_FILE"
|
||||||
|
|
||||||
|
|
||||||
mv "$MERGED_ENTRY_FILE" "$ENTRY_FILE"
|
mv "$MERGED_ENTRY_FILE" "$ENTRY_FILE"
|
||||||
|
|
||||||
if ! cmp -s "$ENTRY_FILE" "$ENTRY_FILE_START" ; then
|
pack_mime "$DIR" "$OUTP"
|
||||||
UNIX_TIMESTAMP=$(date "+%s")
|
|
||||||
HOSTNAME=$(hostname -s)
|
|
||||||
|
|
||||||
RESULT=$(mktemp)
|
rm -rf "$DIR"
|
||||||
pack_mime "$DIR" "$RESULT"
|
|
||||||
FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
|
||||||
mv "$RESULT" "$BASEDIR/cur/$FILENAME"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
find_file_by_id() {
|
remove_notes_by_id() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
|
|
||||||
FILE="$( { grep -l -r "^X-Note-Id: $ID$" "$BASEDIR" || true; } | head -1)"
|
find_files_by_id "$ID" | while read -r FN
|
||||||
|
do
|
||||||
|
rm "$FN"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
if [ ! -f "$FILE" ]; then
|
notes_equal() {
|
||||||
die "Note with ID <$ID> not found"
|
NOTE1="$1"
|
||||||
|
NOTE2="$2"
|
||||||
|
|
||||||
|
MIME_TYPE1=$(get_header "$NOTE1" Content-Type)
|
||||||
|
MIME_TYPE2=$(get_header "$NOTE2" Content-Type)
|
||||||
|
BOUNDARY1=$(echo "$MIME_TYPE1" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
||||||
|
BOUNDARY2=$(echo "$MIME_TYPE2" | sed -n 's/^.*boundary="\(.*\)"$/\1/p')
|
||||||
|
|
||||||
|
FILTER1="^Date:"
|
||||||
|
FILTER2="^Date:"
|
||||||
|
|
||||||
|
if [ ! -z "$BOUNDARY1" ]; then
|
||||||
|
FILTER1="^Date:\|$BOUNDARY1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$FILE"
|
if [ ! -z "$BOUNDARY2" ]; then
|
||||||
|
FILTER2="^Date:\|$BOUNDARY2"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NOTE1_S="$(mktemp)"
|
||||||
|
|
||||||
|
cat "$NOTE1" | grep -v "$FILTER1" > "$NOTE1_S"
|
||||||
|
|
||||||
|
if cat "$NOTE2" | grep -v "$FILTER2" | cmp -s "$NOTE1_S"; then
|
||||||
|
rm "$NOTE1_S"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
rm "$NOTE1_S"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_entry() {
|
||||||
|
OUTP="$(mktemp)"
|
||||||
|
input_note "$1" "$OUTP"
|
||||||
|
|
||||||
|
if [ ! -s "$OUTP" ]; then
|
||||||
|
rm "$OUTP"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
NOTE_ID="$(get_header "$OUTP" X-Note-Id)"
|
||||||
|
|
||||||
|
OLD_NOTE="$(find_file_by_id "$NOTE_ID")"
|
||||||
|
if [ ! -z "$OLD_NOTE" ] && notes_equal "$OLD_NOTE" "$OUTP"; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
remove_notes_by_id "$NOTE_ID"
|
||||||
|
|
||||||
|
UNIX_TIMESTAMP=$(date "+%s")
|
||||||
|
HOSTNAME=$(hostname -s)
|
||||||
|
FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
||||||
|
mv "$OUTP" "$BASEDIR/cur/$FILENAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
edit_entry() {
|
edit_entry() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
FILENAME="$(find_file_by_id "$ID")"
|
FILENAME="$(assert_find_file_by_id "$ID")"
|
||||||
|
|
||||||
DIR="$CACHE_DIR/$ID"
|
DIR="$CACHE_DIR/$ID"
|
||||||
|
|
||||||
|
@ -419,7 +483,7 @@ edit_entry() {
|
||||||
RESULT=$(mktemp)
|
RESULT=$(mktemp)
|
||||||
pack_mime "$DIR" "$RESULT"
|
pack_mime "$DIR" "$RESULT"
|
||||||
|
|
||||||
if ! cmp -s "$RESULT" "$FILENAME"; then
|
if ! notes_equal "$RESULT" "$FILENAME"; then
|
||||||
DEST_FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
DEST_FILENAME="$UNIX_TIMESTAMP.${PID}_1.${HOSTNAME}:2,S"
|
||||||
mv "$RESULT" "$BASEDIR/cur/$DEST_FILENAME"
|
mv "$RESULT" "$BASEDIR/cur/$DEST_FILENAME"
|
||||||
rm "$FILENAME"
|
rm "$FILENAME"
|
||||||
|
@ -460,7 +524,7 @@ list_entries() {
|
||||||
|
|
||||||
export_note() {
|
export_note() {
|
||||||
ID="$1"
|
ID="$1"
|
||||||
FILENAME="$(find_file_by_id "$ID")"
|
FILENAME="$(assert_find_file_by_id "$ID")"
|
||||||
|
|
||||||
DIR="$2"
|
DIR="$2"
|
||||||
unpack_mime "$FILENAME" "$DIR"
|
unpack_mime "$FILENAME" "$DIR"
|
||||||
|
|
89
test.sh
89
test.sh
|
@ -30,9 +30,13 @@ BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||||
export BASE_DIR
|
export BASE_DIR
|
||||||
cd "$BASE_DIR"
|
cd "$BASE_DIR"
|
||||||
|
|
||||||
|
TESTNAME="$1"
|
||||||
RESULT=0
|
RESULT=0
|
||||||
|
|
||||||
testcase() {
|
testcase() {
|
||||||
|
if [ ! -z "$TESTNAME" ] && [[ "$TESTNAME" != "$1" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
TMP=$(mktemp -d)
|
TMP=$(mktemp -d)
|
||||||
cd "$TMP"
|
cd "$TMP"
|
||||||
NOTES_SH_BASEDIR="$(pwd)/notes"
|
NOTES_SH_BASEDIR="$(pwd)/notes"
|
||||||
|
@ -166,6 +170,47 @@ edit_note() {
|
||||||
assert 'echo "$OUTPUT" | grep -o line2' "line2"
|
assert 'echo "$OUTPUT" | grep -o line2' "line2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edit_note_add_file() {
|
||||||
|
"$BASE_DIR/notes.sh" -n <<- EOF
|
||||||
|
Subject: header1
|
||||||
|
|
||||||
|
line1
|
||||||
|
EOF
|
||||||
|
NOTE_ID="$(cat "$(pwd)/notes/cur"/* | grep X-Note-Id | cut -d ' ' -f 2)"
|
||||||
|
|
||||||
|
cat > "$(pwd)/editor.sh" <<- EOF
|
||||||
|
#!/bin/bash
|
||||||
|
FILENAME="\$1"
|
||||||
|
echo "newfile" > "\$FILENAME.txt"
|
||||||
|
EOF
|
||||||
|
chmod a+x "$(pwd)/editor.sh"
|
||||||
|
export EDITOR="$(pwd)/editor.sh"
|
||||||
|
|
||||||
|
"$BASE_DIR/notes.sh" -e "$NOTE_ID"
|
||||||
|
|
||||||
|
OUTPUT="$(cat "$(pwd)/notes/cur"/*)"
|
||||||
|
assert 'echo "$OUTPUT" | grep -o line1' "line1"
|
||||||
|
assert 'echo "$OUTPUT" | grep -o newfile' "newfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_note_no_modifications() {
|
||||||
|
"$BASE_DIR/notes.sh" -n <<- EOF
|
||||||
|
Subject: header1
|
||||||
|
|
||||||
|
line1
|
||||||
|
EOF
|
||||||
|
NOTE_ID="$(cat "$(pwd)/notes/cur"/* | grep X-Note-Id | cut -d ' ' -f 2)"
|
||||||
|
NOTE_FILE="$(ls "$(pwd)/notes/cur"/*)"
|
||||||
|
|
||||||
|
cat > "$(pwd)/editor.sh" <<- EOF
|
||||||
|
#!/bin/bash
|
||||||
|
EOF
|
||||||
|
chmod a+x "$(pwd)/editor.sh"
|
||||||
|
export EDITOR="$(pwd)/editor.sh"
|
||||||
|
|
||||||
|
"$BASE_DIR/notes.sh" -e "$NOTE_ID"
|
||||||
|
assert 'ls "$(pwd)/notes/cur"/*' "$NOTE_FILE"
|
||||||
|
}
|
||||||
resume_editing() {
|
resume_editing() {
|
||||||
"$BASE_DIR/notes.sh" -n <<- EOF
|
"$BASE_DIR/notes.sh" -n <<- EOF
|
||||||
Subject: header1
|
Subject: header1
|
||||||
|
@ -370,12 +415,54 @@ import_export() {
|
||||||
assert 'cat "$TMP/outpdir/file.txt"' "$(cat "$TMP/inpdir/file.txt")"
|
assert 'cat "$TMP/outpdir/file.txt"' "$(cat "$TMP/inpdir/file.txt")"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_note_overwrite_without_modifications() {
|
||||||
|
"$BASE_DIR/notes.sh" -n <<- EOF
|
||||||
|
Subject: header1
|
||||||
|
X-Date: 2021-05-30T18:25:38Z
|
||||||
|
|
||||||
|
line1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
NOTE_FILE="$(ls "$(pwd)/notes/cur"/*)"
|
||||||
|
mv "$NOTE_FILE" "$NOTE_FILE.keep"
|
||||||
|
NOTE_ID="$(cat "$(pwd)/notes/cur"/* | grep X-Note-Id | cut -d ' ' -f 2)"
|
||||||
|
|
||||||
|
mkdir "$TMP/outdir"
|
||||||
|
"$BASE_DIR/notes.sh" -E "$NOTE_ID" "$TMP/outdir"
|
||||||
|
|
||||||
|
"$BASE_DIR/notes.sh" -n "$TMP/outdir"
|
||||||
|
|
||||||
|
assert 'ls "$(pwd)/notes/cur"/*' "$NOTE_FILE.keep"
|
||||||
|
}
|
||||||
|
|
||||||
|
new_note_overwrite_with_modifications() {
|
||||||
|
"$BASE_DIR/notes.sh" -n <<- EOF
|
||||||
|
Subject: header1
|
||||||
|
|
||||||
|
line1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
NOTE_FILE="$(ls "$(pwd)/notes/cur"/*)"
|
||||||
|
NOTE_ID="$(cat "$(pwd)/notes/cur"/* | grep X-Note-Id | cut -d ' ' -f 2)"
|
||||||
|
|
||||||
|
mkdir "$TMP/outdir"
|
||||||
|
"$BASE_DIR/notes.sh" -E "$NOTE_ID" "$TMP/outdir"
|
||||||
|
|
||||||
|
echo "line2" >> "$TMP/outdir/note.md"
|
||||||
|
|
||||||
|
"$BASE_DIR/notes.sh" -n "$TMP/outdir"
|
||||||
|
|
||||||
|
assert 'cat "$(pwd)/notes/cur"/* | grep line2' "line2"
|
||||||
|
}
|
||||||
|
|
||||||
testcase new_note_from_stdin
|
testcase new_note_from_stdin
|
||||||
testcase new_note_from_file
|
testcase new_note_from_file
|
||||||
testcase new_note_from_dir
|
testcase new_note_from_dir
|
||||||
testcase list_notes
|
testcase list_notes
|
||||||
testcase export_note
|
testcase export_note
|
||||||
testcase edit_note
|
testcase edit_note
|
||||||
|
testcase edit_note_add_file
|
||||||
|
testcase edit_note_no_modifications
|
||||||
testcase resume_editing
|
testcase resume_editing
|
||||||
testcase pack_multipart
|
testcase pack_multipart
|
||||||
testcase pack_multipart_binary
|
testcase pack_multipart_binary
|
||||||
|
@ -383,6 +470,8 @@ testcase existing_headers
|
||||||
testcase no_headers
|
testcase no_headers
|
||||||
testcase no_headers_dir
|
testcase no_headers_dir
|
||||||
testcase import_export
|
testcase import_export
|
||||||
|
testcase new_note_overwrite_without_modifications
|
||||||
|
testcase new_note_overwrite_with_modifications
|
||||||
|
|
||||||
if [[ "$RESULT" == "0" ]]; then
|
if [[ "$RESULT" == "0" ]]; then
|
||||||
echo "All tests passed."
|
echo "All tests passed."
|
||||||
|
|
Loading…
Reference in a new issue