func Sync(srcRaw string, destRaw string, opts *Options) error { options = opts // Remove from src/dest strings src := utils.ExtractURL(srcRaw).Path dest := utils.ExtractURL(destRaw).Path // Get a handle on the to/from Filesystems fromFile, fromFs, err := utils.MakeFile(srcRaw) if err != nil { return err } if fromFile.IsDir() { toFile, toFs, err := utils.MakeFile(destRaw) var leftMap filesystem.FileMap var rightMap filesystem.FileMap var done sync.WaitGroup done.Add(2) go func() { leftMap = fromFs.FileMap(fromFile) done.Done() }() go func() { rightMap = toFs.FileMap(toFile) done.Done() }() done.Wait() diff, err := filesystem.FileMapDiff( leftMap, rightMap, filesystem.ModifiedComparator) if err == nil { for _, file := range diff { if ignoreFile(file.Path(), options.Exclude) { continue } toFile = utils.MkToFile(src, dest, file) if err == nil { if file.IsDir() { logOutput("Mkdir: %s -> %s\n", file.Path(), toFile.Path()) toFs.MkDir(toFile) } else { logOutput("Copying file: %s -> %s\n", file.Path(), toFile.Path()) bytes, err := fromFs.Read(file) err = toFs.Write(toFile, bytes, file.Mode()) if err != nil { logOutput("Error copying file %s: %v", file.Path(), err) } } } } } else { logOutput("Error: %v\n", err) } } else { toFile := utils.MkToFile(src, dest, fromFile) toFs, err := utils.GetFileSystemFromFile(destRaw) if err != nil { logOutput("Error opening dest file: %v", err) return fmt.Errorf("Error opening dest file: %v", err) } bytes, err := fromFs.Read(fromFile) if err != nil { logOutput("Error reading from source file: %s", err.Error()) return fmt.Errorf("Error reading source file: %v", err) } err = toFs.Write(toFile, bytes, toFile.Mode()) if err != nil { logOutput("Error writing to remote path: %s", err.Error()) return fmt.Errorf("Error write to remote path: %v", err) } } return err }
func Watch(srcRaw string, destRaw string, opts *Options) error { options = opts watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() err = filepath.Walk(srcRaw, func(path string, f os.FileInfo, err error) error { if f.IsDir() && !ignoreFile(path, options.Exclude) { err = watcher.Add(path) if err != nil { log.Fatal(err) } } return nil }) if err != nil { log.Fatal(err) } fromFs, err := utils.GetFileSystemFromFile(srcRaw) toFs, err := utils.GetFileSystemFromFile(destRaw) src := utils.ExtractURL(srcRaw).Path dest := utils.ExtractURL(destRaw).Path done := make(chan bool) go func() { for { select { case event := <-watcher.Events: file, _, _ := utils.MakeFile(event.Name) if ignoreFile(file.Path(), options.Exclude) { continue } // File Delete if event.Op&fsnotify.Remove == fsnotify.Remove { path := utils.RelativeFilePath(src, dest, event.Name) err := DeleteSingle(toFs, path) if err != nil { log.Fatalf("Unable to delete remote file: %v", err) } if file.IsDir() { err := watcher.Remove(event.Name) if err != nil { log.Fatalf("Unable to stop watching remote file %s. %v", event.Name, err) } } } else { // File create/update path := utils.RelativeFilePath(src, dest, event.Name) CopySingle(fromFs, event.Name, toFs, path) if file.IsDir() && event.Op&fsnotify.Create == fsnotify.Create { err := watcher.Add(event.Name) if err != nil { log.Fatalf("Unable to setup watch on file %s. %v", event.Name, err) } } } case err := <-watcher.Errors: logOutput("Watch error: %v\n", err) } } }() err = watcher.Add(srcRaw) if err != nil { log.Fatal(err) } <-done return nil }