Implement synchronized renaming

This commit is contained in:
2024-07-09 23:18:13 -04:00
parent 42bed109d6
commit be1f602770
5 changed files with 70 additions and 46 deletions
+8 -1
View File
@@ -40,6 +40,13 @@ func main() {
// print all information needed for syncing to stdout for interpretation by the client
// stdin[0] is expected to be the device ID
sync.GetRemoteDataFromServer(stdin[0])
case "rename":
// move an entry to a new location before using fallthrough to add its previous iteration to the deletions directory
// stdin[0] is evaluated after fallthrough
// stdin[1] is expected to be the OLD incomplete target location with "\x1d" representing path separators - always pass in UNIX format
// stdin[2] is expected to be the NEW incomplete target location with "\x1d" representing path separators - always pass in UNIX format
sync.Rename(strings.ReplaceAll(stdin[1], "\x1d", "/"), strings.ReplaceAll(stdin[2], "\x1d", "/"), true)
fallthrough // fallthrough to add the old entry to the deletions directory
case "shear":
// shear an entry from the server and add it to the deletions directory
// stdin[0] is expected to be the device ID
@@ -73,7 +80,7 @@ This software exists under the MIT license; you may redistribute it under certai
This program comes with absolutely no warranty; type "libmuttonserver version" for details.
` + cli.AnsiBold + "Usage:" + backend.AnsiReset + ` libmuttonserver <argument>
` + cli.AnsiBold + "Arguments (user):" + backend.AnsiReset + `
help|-h Bring up this menu
version|-v Display version and license information
+1 -1
View File
@@ -97,7 +97,7 @@ func main() {
case "note", "-n":
field = 4
case "rename", "-r":
cli.RenameCli(targetLocation)
cli.RenameCli(args[1]) // pass the incomplete path as the server and all clients (reading from the deletions directory) will have a different home directory
default:
cli.HelpEdit()
}
-24
View File
@@ -1,10 +1,5 @@
package backend
import (
"fmt"
"os"
)
// GetOldEntryData decrypts and returns old entry data (with all required lines present)
func GetOldEntryData(targetLocation string, field int) []string {
// ensure targetLocation exists
@@ -21,25 +16,6 @@ func GetOldEntryData(targetLocation string, field int) []string {
}
}
// Rename renames oldLocation to newLocation
func Rename(oldLocation, newLocation string) {
// ensure newLocation does not exist
_, isAccessible := TargetIsFile(newLocation, false, 0)
if isAccessible {
fmt.Println(AnsiError + "\"" + newLocation + "\" already exists" + AnsiReset)
os.Exit(1)
}
// rename oldLocation to newLocation
err := os.Rename(oldLocation, newLocation)
if err != nil {
fmt.Println(AnsiError + "Failed to rename - does the target containing directory exists?" + AnsiReset)
}
// TODO implement synced renaming
Exit(0)
}
// EnsureSliceLength ensures slice is long enough to contain the specified index
func EnsureSliceLength(slice []string, index int) []string {
for len(slice) <= index {
+6 -8
View File
@@ -4,22 +4,20 @@ import (
"bufio"
"fmt"
"github.com/rwinkhart/MUTN/src/backend"
"github.com/rwinkhart/MUTN/src/sync"
"os"
"os/exec"
"reflect"
"strings"
)
// RenameCli renames an entry at oldLocation to a new location (user input)
func RenameCli(oldLocation string) {
// ensure targetLocation exists
backend.TargetIsFile(oldLocation, true, 0)
// RenameCli renames an entry at oldLocation to a new location (user input) on both the client and the server
func RenameCli(oldLocationIncomplete string) {
// prompt user for new location and rename
newLocation := backend.TargetLocationFormat(input("New location:"))
backend.Rename(oldLocation, newLocation)
newLocationIncomplete := input("New location:")
sync.Rename(oldLocationIncomplete, newLocationIncomplete, false)
// exit is done from backend.Rename
// exit is done from sync.Rename
}
// EditEntryField edits a field of an entry at targetLocation (user input)
+55 -12
View File
@@ -3,12 +3,13 @@ package sync
import (
"fmt"
"github.com/rwinkhart/MUTN/src/backend"
"io/fs"
"os"
"strings"
)
// getModTimes returns a list of all entry modification times
func getModTimes(entryList []string) []int64 {
// get a list of all entry modification times
var modList []int64
for _, file := range entryList {
modTime, _ := os.Stat(backend.TargetLocationFormat(file))
@@ -18,6 +19,53 @@ func getModTimes(entryList []string) []int64 {
return modList
}
// genDeviceIDList returns a pointer to a slice of all registered device IDs
func genDeviceIDList() *[]fs.DirEntry {
// create a slice of all registered devices
deviceIDList, err := os.ReadDir(backend.ConfigDir + backend.PathSeparator + "devices")
if err != nil {
fmt.Println(backend.AnsiError + "Failed to read the devices directory: " + err.Error() + backend.AnsiReset)
os.Exit(1)
}
return &deviceIDList
}
// Rename renames oldLocation to newLocation
// if running on the client, also calls the server to rename the entry and add the old entry name to the deletions list
func Rename(oldLocationIncomplete, newLocationIncomplete string, onServer bool) {
// get full paths for both locations
oldLocation := backend.TargetLocationFormat(oldLocationIncomplete)
newLocation := backend.TargetLocationFormat(newLocationIncomplete)
// ensure oldLocation exists
backend.TargetIsFile(oldLocation, true, 0)
// ensure newLocation does not exist
_, isAccessible := backend.TargetIsFile(newLocation, false, 0)
if isAccessible {
fmt.Println(backend.AnsiError + "\"" + newLocation + "\" already exists" + backend.AnsiReset)
os.Exit(1)
}
// rename oldLocation to newLocation
err := os.Rename(oldLocation, newLocation)
if err != nil {
fmt.Println(backend.AnsiError + "Failed to rename - does the target containing directory exist?" + backend.AnsiReset)
}
// call server to complete rename
if !onServer {
deviceIDList := genDeviceIDList()
if len(*deviceIDList) > 0 { // ensure a device ID exists (online mode)
GetSSHOutput("libmuttonserver rename",
(*deviceIDList)[0].Name()+"\n"+
strings.ReplaceAll(oldLocationIncomplete, backend.PathSeparator, "\x1d")+"\n"+
strings.ReplaceAll(newLocationIncomplete, backend.PathSeparator, "\x1d"), false)
}
backend.Exit(0) // do not exit program on server, as fallthrough is used to add the old entry name to the deletions list
}
}
// ShearLocal removes the target file or directory from the local system
// returns: deviceID (on client), for use in ShearRemoteFromClient
// if the local system is a server, it will also add the target to the deletions list for all clients (except the requesting client)
@@ -29,18 +77,13 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string {
onServer = true
}
// create a slice of all registered devices
deviceIDList, err := os.ReadDir(backend.ConfigDir + backend.PathSeparator + "devices")
if err != nil {
fmt.Println(backend.AnsiError + "Failed to read the devices directory: " + err.Error() + backend.AnsiReset)
os.Exit(1)
}
deviceIDList := genDeviceIDList()
// add the sheared target (incomplete, vanity) to the deletions list (if running on a server)
if onServer {
for _, device := range deviceIDList {
for _, device := range *deviceIDList {
if device.Name() != clientDeviceID {
_, err = os.Create(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + device.Name() + "\x1e" + strings.ReplaceAll(targetLocationIncomplete, "/", "\x1d"))
_, err := os.Create(backend.ConfigDir + backend.PathSeparator + "deletions" + backend.PathSeparator + device.Name() + "\x1e" + strings.ReplaceAll(targetLocationIncomplete, "/", "\x1d"))
if err != nil {
// do not print error as there is currently no way of seeing server-side errors
// failure to add the target to the deletions list will exit the program and result in a client re-uploading the target (non-critical)
@@ -55,14 +98,14 @@ func ShearLocal(targetLocationIncomplete, clientDeviceID string) string {
if !onServer { // error if target does not exist on client, needed because os.RemoveAll does not return an error if target does not exist
backend.TargetIsFile(targetLocationComplete, true, 0)
}
err = os.RemoveAll(targetLocationComplete)
err := os.RemoveAll(targetLocationComplete)
if err != nil {
fmt.Println(backend.AnsiError + "Failed to remove local target: " + err.Error() + backend.AnsiReset)
os.Exit(1)
}
if !onServer && len(deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode)
return deviceIDList[0].Name()
if !onServer && len(*deviceIDList) > 0 { // return the device ID if running on the client and a device ID exists (online mode)
return (*deviceIDList)[0].Name()
}
return ""