Sync.cs

Synchronize Files of two folder / subdirectories
mail@pastecode.io avatar
unknown
csharp
3 years ago
5.5 kB
12
Indexable
Never
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Linq;

namespace FileTools
{
    public struct Command
    {
        public enum Kind
        {
            Missing,
            Ecxess,
            DateMismatch
        }

        public Kind Type { get; }
        public IDirectoryInfo Source { get; }
        public IDirectoryInfo Destination { get; }
        public string FileLocation { get; }

        public Command(Kind syncCommandKind, IDirectoryInfo source, IDirectoryInfo destination, string fileLocation)
        {
            if (string.IsNullOrEmpty(fileLocation))
                throw new ArgumentException($"'{nameof(fileLocation)}' cannot be null or empty.", nameof(fileLocation));

            Type = syncCommandKind;
            Source = source;
            Destination = destination;
            FileLocation = fileLocation;
        }
    }

    public delegate void InterpretCommandDelegate(Command command);

    public static class DirectorySynchronizer
    {
        private static IEnumerable<string> GetRelativeFiles(IDirectoryInfo dI, string fP, SearchOption sO) =>
            dI.GetFiles(fP, sO).Select(f => Path.GetRelativePath(dI.FullName, f.FullName));

        private static string RootFile(IDirectoryInfo root, string fileLocation) =>
            Path.Combine(root.FullName, fileLocation);

        public static IEnumerable<Command> QueryCommands(IFileSystem fileSystem, IDirectoryInfo source, IDirectoryInfo destination, string filePattern, SearchOption searchOption)
        {
            var sourceFiles = GetRelativeFiles(source, filePattern, searchOption);
            var destinationFiles = GetRelativeFiles(destination, filePattern, searchOption);
            var filesPresentInBothLocations = sourceFiles.Intersect(destinationFiles);
            var missingFiles = sourceFiles.Except(filesPresentInBothLocations);
            var excessFiles =  destinationFiles.Except(filesPresentInBothLocations);
            var filesPresentInBothLocationsWithDifferentDate =
                filesPresentInBothLocations
                .Where(f => fileSystem.FileInfo.FromFileName(RootFile(source, f)).LastWriteTime != fileSystem.FileInfo.FromFileName(RootFile(destination, f)).LastWriteTime);

            return missingFiles
            .Select(mf => new Command(Command.Kind.Missing, source, destination, mf))
            .Concat(excessFiles.Select(ef => new Command(Command.Kind.Ecxess, source, destination, ef)))
            .Concat(filesPresentInBothLocationsWithDifferentDate.Select(dMF => new Command(Command.Kind.DateMismatch, source, destination, dMF)));
        }

        public static void InterpretCommand(IFileSystem fileSystem, Command command)
        {
            switch(command)
            {
                case Command { Type: Command.Kind.Missing }:
                {
                    var sourceFileLocation = RootFile(command.Source, command.FileLocation);
                    var destinationFileLocation = RootFile(command.Destination, command.FileLocation);
                    var destinationDirectoryLocation = fileSystem.Path.GetDirectoryName(destinationFileLocation);

                    // Create directory if not present, so files can be copied
                    if (fileSystem.Directory.Exists(destinationDirectoryLocation) == false)
                        fileSystem.Directory.CreateDirectory(destinationDirectoryLocation);

                    fileSystem.File.Copy(sourceFileLocation, destinationFileLocation);
                } break;

                case Command { Type: Command.Kind.Ecxess }:
                {
                    fileSystem.File.Delete(RootFile(command.Destination, command.FileLocation));
                } break;

                case Command { Type: Command.Kind.DateMismatch }:
                {
                    var sourceFileLocation = RootFile(command.Source, command.FileLocation);
                    var destinationFileLocation = RootFile(command.Destination, command.FileLocation);

                    fileSystem.File.Copy(sourceFileLocation, destinationFileLocation, true);
                } break;

                default: throw new ArgumentOutOfRangeException("command.Kind");
            }
        }

        public static InterpretCommandDelegate CreateCommandInterpreter(IFileSystem fileSystem) =>
            delegate (Command command) { InterpretCommand(fileSystem, command); };

        public static void InterpretCommands(InterpretCommandDelegate commandInterpreter, IEnumerable<Command> commands)
        {
            foreach (var command in commands)
               commandInterpreter(command);
        }

        public static void Synchronize(IFileSystem fileSystem, IDirectoryInfo source, IDirectoryInfo destination, string filePattern, SearchOption searchOption) =>
            InterpretCommands(CreateCommandInterpreter(fileSystem), QueryCommands(fileSystem, source, destination, filePattern, searchOption));

        public static void Synchronize(IFileSystem fileSystem, IDirectoryInfo source, IDirectoryInfo destination, string filePattern, SearchOption searchOption, InterpretCommandDelegate command) =>
            InterpretCommands(CreateCommandInterpreter(fileSystem) + command, QueryCommands(fileSystem, source, destination, filePattern, searchOption));
    }
}