├── .gitignore
├── Client
├── Client.csproj
├── CommandOptions.cs
├── ConsoleLogger.cs
├── FileCopier.cs
├── FileWatcher.cs
├── IFileCopier.cs
├── IFileWatcher.cs
├── ILogger.cs
├── IPluginLoader.cs
├── PluginLoader.cs
├── Program.cs
└── QueuedFileCopier.cs
├── Copier.sln
├── CopierPluginBase
├── CopierPluginBase.csproj
├── IPostCopyEventListener.cs
└── IPreCopyEventListener.cs
├── SamplePlugin
├── SamplePlugin.csproj
├── SamplePostCopyEventListener.cs
└── SamplePreCopyEventListener.cs
├── assests
└── banner.png
├── copier-yt-thumbnail.png
├── copier-yt-thumbnail.snagproj
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /Client/Obj/*
2 | .idea/*
3 | Copier.sln.DotSettings.user
4 | /Client/bin/*
5 | CopierPluginBase/obj/*
6 | CopierPluginBase/bin*
7 | SamplePlugin/bin/*
8 | SamplePlugin/obj/*
9 |
--------------------------------------------------------------------------------
/Client/Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | Copier
7 | Copier.Client
8 | latest
9 |
10 |
11 |
12 | Tarik Guney
13 | 1.0.0
14 | git
15 | https://github.com/tarikguney/copier
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Client/CommandOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq.Expressions;
3 | using CommandLine;
4 | using CommandLine.Text;
5 |
6 | namespace Copier.Client
7 | {
8 | public class CommandOptions
9 | {
10 | [Option('s', "sourceDirectoryPath",
11 | HelpText =
12 | "Parent directory where the files will be searched. If skipped, the current directory will be searched.",
13 | Required = false)]
14 | public string SourceDirectoryPath { get; set; }
15 |
16 | [Option('f', "fileGlobPattern", Required = true,
17 | HelpText = "Files to be searched. Accepts glob patter for pattern matching.")]
18 | public string FileGlobPattern { get; set; }
19 |
20 | [Option('d', "destinationDirectoryPath", Required = true, HelpText = "Destination directory path")]
21 | public string DestinationDirectoryPath { get; set; }
22 |
23 | [Option('o', "overwriteTargetFiles", Default = false, Required = false,
24 | HelpText = "If passed true, copier will overwrite existing files at the target location.")]
25 | public bool OverwriteTargetFile { get; set; }
26 |
27 | [Option('v', "verbose", Default = false, Required = false,
28 | HelpText = "If passed true, more information will be outputted to the console.")]
29 | public bool Verbose { get; set; }
30 |
31 | [Option('e', "debug", Default = false, Required = false, HelpText = "Shows debug information.")]
32 | public bool Debug { get; set; }
33 |
34 | [Option('t', "delay", Default = 0, Required = false, HelpText = "Delays copy operation for a given time in milliseconds.")]
35 | public int Delay { get; set; }
36 |
37 | [Usage]
38 | public static IEnumerable Examples => new List()
39 | {
40 | new Example("Starts the copier", new UnParserSettings() {PreferShortName = true},
41 | new CommandOptions
42 | {
43 | SourceDirectoryPath = "C:/Users/MyDocuments/Images",
44 | FileGlobPattern = "*.jpg",
45 | DestinationDirectoryPath = "C:/Users/MyDocuments/NewImages"
46 | }),
47 | new Example("Starts the copier and overwrites the target files.",
48 | new UnParserSettings() {PreferShortName = true},
49 | new CommandOptions
50 | {
51 | SourceDirectoryPath = "C:/Users/MyDocuments/Images",
52 | FileGlobPattern = "*.jpg",
53 | DestinationDirectoryPath = "C:/Users/MyDocuments/NewImages",
54 | OverwriteTargetFile = true
55 | }),
56 | new Example("Starts the copier and overwrites the target files and outputs verbose messages.",
57 | new UnParserSettings() {PreferShortName = true},
58 | new CommandOptions
59 | {
60 | SourceDirectoryPath = "C:/Users/MyDocuments/Images",
61 | FileGlobPattern = "*.jpg",
62 | DestinationDirectoryPath = "C:/Users/MyDocuments/NewImages",
63 | OverwriteTargetFile = true,
64 | Verbose = true
65 | })
66 | };
67 | }
68 | }
--------------------------------------------------------------------------------
/Client/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 |
4 | namespace Copier.Client
5 | {
6 | class ConsoleLogger : ILogger
7 | {
8 | public void LogInfo(string message)
9 | {
10 | Console.WriteLine(message);
11 | }
12 |
13 | public void LogError(string message)
14 | {
15 | Console.ForegroundColor = ConsoleColor.Red;
16 | Console.WriteLine("ERROR: " + message);
17 | Console.ResetColor();
18 | }
19 |
20 | public void LogWarning(string message)
21 | {
22 | Console.ForegroundColor = ConsoleColor.Yellow;
23 | Console.WriteLine("WARNING: " + message);
24 | Console.ResetColor();
25 | }
26 |
27 | public void LogDebug(string message)
28 | {
29 | Console.ForegroundColor = ConsoleColor.Cyan;
30 | Console.WriteLine("DEBUG: " + message);
31 | Console.ResetColor();
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Client/FileCopier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Copier.Client
5 | {
6 | class FileCopier : IFileCopier, IPreCopyEventBroadcaster, IPostCopyEventBroadcaster
7 | {
8 | public event Action PreCopyEvent = delegate { };
9 | public event Action PostCopyEvent = delegate { };
10 |
11 | private readonly ILogger _logger;
12 | private readonly CommandOptions _options;
13 |
14 | public FileCopier(ILogger logger, CommandOptions options)
15 | {
16 | _logger = logger;
17 | _options = options;
18 | }
19 |
20 | public void CopyFile(string fileName)
21 | {
22 | var absoluteSourceFilePath = Path.Combine(_options.SourceDirectoryPath, fileName);
23 | var absoluteTargetFilePath = Path.Combine(_options.DestinationDirectoryPath, fileName);
24 |
25 | if (File.Exists(absoluteTargetFilePath) && !_options.OverwriteTargetFile)
26 | {
27 | _logger.LogInfo($"{fileName} exists. Skipped the copy operation because OverwriteTargetFile is set to false.");
28 | return;
29 | }
30 |
31 | PreCopyEvent(absoluteSourceFilePath);
32 | File.Copy(absoluteSourceFilePath, absoluteTargetFilePath, _options.OverwriteTargetFile);
33 | PostCopyEvent(absoluteSourceFilePath);
34 | }
35 | }
36 |
37 | public interface IPostCopyEventBroadcaster
38 | {
39 | event Action PostCopyEvent;
40 | }
41 |
42 | public interface IPreCopyEventBroadcaster
43 | {
44 | event Action PreCopyEvent;
45 | }
46 | }
--------------------------------------------------------------------------------
/Client/FileWatcher.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace Copier.Client
4 | {
5 | class FileWatcher : IFileWatcher
6 | {
7 | private readonly IFileCopier _fileCopier;
8 | private readonly ILogger _logger;
9 |
10 | public FileWatcher(IFileCopier fileCopier, ILogger logger)
11 | {
12 | _fileCopier = fileCopier;
13 | _logger = logger;
14 | }
15 |
16 | public void Watch(CommandOptions options)
17 | {
18 | var watcher = new FileSystemWatcher
19 | {
20 | Path = options.SourceDirectoryPath,
21 | NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
22 | Filter = options.FileGlobPattern,
23 | };
24 |
25 | watcher.Changed += (sender, args) =>
26 | {
27 | if (args.ChangeType != WatcherChangeTypes.Changed) return;
28 |
29 | if (options.Verbose)
30 | {
31 | _logger.LogInfo($"{args.Name} file has changed.");
32 | }
33 |
34 | _fileCopier.CopyFile(args.Name);
35 | };
36 |
37 | watcher.Renamed += (sender, args) =>
38 | {
39 | if (options.Verbose)
40 | {
41 | _logger.LogInfo($"{args.OldName} has been renamed to {args.Name}.");
42 | }
43 |
44 | _fileCopier.CopyFile(args.Name);
45 | };
46 |
47 | watcher.EnableRaisingEvents = true;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Client/IFileCopier.cs:
--------------------------------------------------------------------------------
1 | namespace Copier.Client
2 | {
3 | public interface IFileCopier
4 | {
5 | void CopyFile(string fileName);
6 | }
7 | }
--------------------------------------------------------------------------------
/Client/IFileWatcher.cs:
--------------------------------------------------------------------------------
1 | namespace Copier.Client
2 | {
3 | public interface IFileWatcher
4 | {
5 | void Watch(CommandOptions options);
6 | }
7 | }
--------------------------------------------------------------------------------
/Client/ILogger.cs:
--------------------------------------------------------------------------------
1 | namespace Copier.Client
2 | {
3 | public interface ILogger
4 | {
5 | void LogInfo(string message);
6 | void LogError(string message);
7 | void LogWarning(string message);
8 | void LogDebug(string message);
9 | }
10 | }
--------------------------------------------------------------------------------
/Client/IPluginLoader.cs:
--------------------------------------------------------------------------------
1 | namespace Copier.Client
2 | {
3 | public interface IPluginLoader
4 | {
5 | void Subscribe(IPreCopyEventBroadcaster pre, IPostCopyEventBroadcaster post);
6 | }
7 | }
--------------------------------------------------------------------------------
/Client/PluginLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using CopierPluginBase;
7 |
8 | namespace Copier.Client
9 | {
10 | public class PluginLoader : IPluginLoader
11 | {
12 | private readonly ILogger _debugLogger;
13 | private readonly List _preCopyListeners = new List();
14 | private readonly List _postCopyListeners = new List();
15 |
16 | private bool ShowDebugMessages { get; set; }
17 |
18 | public PluginLoader(ILogger debugLogger, bool showDebugMessages = false)
19 | {
20 | _debugLogger = debugLogger;
21 | ShowDebugMessages = showDebugMessages && debugLogger != null;
22 | Initialize();
23 | }
24 |
25 | public PluginLoader()
26 | {
27 | Initialize();
28 | }
29 |
30 | private static string GetPluginsDirectory()
31 | {
32 | #if DEBUG
33 | return Path.Combine(Directory.GetCurrentDirectory(), "bin\\Debug\\netcoreapp2.1", "plugins");
34 | #else
35 | return Path.Combine(Directory.GetCurrentDirectory(), "plugins");
36 | #endif
37 | }
38 |
39 | private void Initialize()
40 | {
41 | var pluginDirectory = GetPluginsDirectory();
42 |
43 | if (!Directory.Exists(pluginDirectory))
44 | {
45 | if (ShowDebugMessages)
46 | {
47 | _debugLogger.LogWarning("Cannot find plugins folder.");
48 | }
49 |
50 | return;
51 | }
52 |
53 | var assemblyFiles = Directory.GetFiles(pluginDirectory, "*.dll");
54 |
55 | foreach (var assemblyName in assemblyFiles)
56 | {
57 | var pluginAssembly = Assembly.LoadFile(assemblyName);
58 |
59 | if (ShowDebugMessages)
60 | {
61 | _debugLogger.LogDebug($"Loaded {assemblyName}");
62 | }
63 |
64 | var existingTypes = pluginAssembly.GetTypes();
65 |
66 | bool TypePredicate(Type child, Type parent) =>
67 | child.IsPublic && !child.IsAbstract && child.IsClass && parent.IsAssignableFrom(child);
68 |
69 | var postCopyListenerTypes =
70 | existingTypes.Where(a => TypePredicate(a, typeof(IPostCopyEventListener))).ToList();
71 | _postCopyListeners.AddRange(postCopyListenerTypes);
72 |
73 | var preCopyListenerTypes =
74 | existingTypes.Where(a => TypePredicate(a, typeof(IPreCopyEventListener))).ToList();
75 | _preCopyListeners.AddRange(preCopyListenerTypes);
76 |
77 | // If enabled, logging debug messages for the found types in the iterated assembly.
78 | if (ShowDebugMessages)
79 | {
80 | _debugLogger.LogDebug($"Found the following PostCopy types from plugin {assemblyName}:");
81 | _debugLogger.LogDebug(string.Join("\n", postCopyListenerTypes.Select(a => a.Name).ToArray()));
82 |
83 | _debugLogger.LogDebug($"Found the following PreCopy types from plugin {assemblyName}:");
84 | // Used LINQ for fun.
85 | var preCopyTypeNames = (from a in preCopyListenerTypes
86 | select a.Name).ToArray();
87 | _debugLogger.LogDebug(string.Join("\n", preCopyTypeNames));
88 | }
89 | }
90 | }
91 |
92 | public void Subscribe(IPreCopyEventBroadcaster pre, IPostCopyEventBroadcaster post)
93 | {
94 | _preCopyListeners.ForEach(a =>
95 | {
96 | var listenerObject = (IPreCopyEventListener) Activator.CreateInstance(a);
97 | pre.PreCopyEvent += listenerObject.OnPreCopy;
98 | });
99 |
100 | _postCopyListeners.ForEach(a =>
101 | {
102 | var listenerObject = (IPostCopyEventListener) Activator.CreateInstance(a);
103 | post.PostCopyEvent += listenerObject.OnPostCopy;
104 | });
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/Client/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using CommandLine;
4 |
5 | namespace Copier.Client
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | Parser.Default.ParseArguments(args)
12 | .WithParsed(StartWatching)
13 | .WithNotParsed(a => { Environment.Exit(1); });
14 |
15 | Console.WriteLine("Please press any key to exit.");
16 | Console.ReadLine();
17 | }
18 |
19 | private static void StartWatching(CommandOptions options)
20 | {
21 | ILogger logger = new ConsoleLogger();
22 |
23 | logger.LogInfo("Watching has started...");
24 |
25 | options.SourceDirectoryPath = string.IsNullOrWhiteSpace(options.SourceDirectoryPath)
26 | ? Directory.GetCurrentDirectory()
27 | : options.SourceDirectoryPath;
28 |
29 | IPluginLoader loader = new PluginLoader(logger, options.Debug);
30 |
31 | var fileCopier = new FileCopier(logger, options);
32 | IFileCopier copier = fileCopier;
33 |
34 | if (options.Delay > 0)
35 | {
36 | copier = new QueuedFileCopier(fileCopier, logger, options);
37 | }
38 |
39 | IFileWatcher fileWatcher = new FileWatcher(copier, logger);
40 |
41 | loader.Subscribe((IPreCopyEventBroadcaster) copier, (IPostCopyEventBroadcaster) copier);
42 |
43 | fileWatcher.Watch(options);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Client/QueuedFileCopier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace Copier.Client
6 | {
7 | public class QueuedFileCopier : IFileCopier, IPreCopyEventBroadcaster, IPostCopyEventBroadcaster
8 | {
9 | public event Action PreCopyEvent = delegate {};
10 | public event Action PostCopyEvent = delegate {};
11 |
12 | private readonly IFileCopier _fileCopier;
13 | private readonly ILogger _logger;
14 | private readonly CommandOptions _options;
15 |
16 | private readonly HashSet _fileNameQueue = new HashSet();
17 | private Task _copyTask;
18 |
19 | public QueuedFileCopier(IFileCopier fileCopier, ILogger logger, CommandOptions options)
20 | {
21 | _fileCopier = fileCopier;
22 | _logger = logger;
23 | _options = options;
24 |
25 | if (_options.Debug)
26 | {
27 | logger.LogInfo("Delay option has been specified. QueuedFileCopier is chosen as the copier strategy.");
28 | }
29 | }
30 |
31 | public void CopyFile(string fileName)
32 | {
33 | if (_copyTask == null)
34 | {
35 | _copyTask = Task.Run(async () =>
36 | {
37 | await Task.Delay(TimeSpan.FromMilliseconds(_options.Delay));
38 | if (_options.Verbose || _options.Debug)
39 | {
40 | _logger.LogInfo($"{_options.Delay} milliseconds have passed. The copy operation has started...");
41 | }
42 |
43 | PreCopyEvent("");
44 |
45 | foreach (var item in _fileNameQueue)
46 | {
47 | _fileCopier.CopyFile(item);
48 | }
49 |
50 | PostCopyEvent("");
51 |
52 | _copyTask = null;
53 |
54 | if (_options.Verbose || _options.Debug)
55 | {
56 | _logger.LogInfo($"The copy operation has finished...");
57 | _logger.LogInfo("The file queue has been emptied.");
58 | }
59 | });
60 | }
61 |
62 | if (!_fileNameQueue.Contains(fileName))
63 | {
64 | if (_options.Verbose || _options.Debug)
65 | {
66 | _logger.LogInfo(
67 | $"{fileName} has been added to the file queue and will be copied over in {_options.Delay} milliseconds.");
68 | }
69 |
70 | _fileNameQueue.Add(fileName);
71 | }
72 | else if (_options.Debug)
73 | {
74 | _logger.LogInfo($"{fileName} exists in the file queue, thereby skipped.");
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/Copier.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{62C280E7-A4FC-4E4B-9F9D-CB5A0F94A847}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopierPluginBase", "CopierPluginBase\CopierPluginBase.csproj", "{176D1208-81FB-4F0B-9E81-52D2ACE3B705}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplePlugin", "SamplePlugin\SamplePlugin.csproj", "{F97F0A1D-2A3B-4EE4-9639-EEA9C62BA9BB}"
8 | EndProject
9 | Global
10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
11 | Debug|Any CPU = Debug|Any CPU
12 | Release|Any CPU = Release|Any CPU
13 | EndGlobalSection
14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
15 | {62C280E7-A4FC-4E4B-9F9D-CB5A0F94A847}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
16 | {62C280E7-A4FC-4E4B-9F9D-CB5A0F94A847}.Debug|Any CPU.Build.0 = Debug|Any CPU
17 | {62C280E7-A4FC-4E4B-9F9D-CB5A0F94A847}.Release|Any CPU.ActiveCfg = Release|Any CPU
18 | {62C280E7-A4FC-4E4B-9F9D-CB5A0F94A847}.Release|Any CPU.Build.0 = Release|Any CPU
19 | {176D1208-81FB-4F0B-9E81-52D2ACE3B705}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {176D1208-81FB-4F0B-9E81-52D2ACE3B705}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {176D1208-81FB-4F0B-9E81-52D2ACE3B705}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {176D1208-81FB-4F0B-9E81-52D2ACE3B705}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {F97F0A1D-2A3B-4EE4-9639-EEA9C62BA9BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {F97F0A1D-2A3B-4EE4-9639-EEA9C62BA9BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {F97F0A1D-2A3B-4EE4-9639-EEA9C62BA9BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {F97F0A1D-2A3B-4EE4-9639-EEA9C62BA9BB}.Release|Any CPU.Build.0 = Release|Any CPU
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/CopierPluginBase/CopierPluginBase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CopierPluginBase/IPostCopyEventListener.cs:
--------------------------------------------------------------------------------
1 | namespace CopierPluginBase
2 | {
3 | public interface IPostCopyEventListener
4 | {
5 | void OnPostCopy(string filePath);
6 | }
7 | }
--------------------------------------------------------------------------------
/CopierPluginBase/IPreCopyEventListener.cs:
--------------------------------------------------------------------------------
1 | namespace CopierPluginBase
2 | {
3 | public interface IPreCopyEventListener
4 | {
5 | void OnPreCopy(string filePath);
6 | }
7 | }
--------------------------------------------------------------------------------
/SamplePlugin/SamplePlugin.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/SamplePlugin/SamplePostCopyEventListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CopierPluginBase;
3 |
4 | namespace SamplePlugin
5 | {
6 | public class SamplePostCopyEventListener: IPostCopyEventListener
7 | {
8 | public void OnPostCopy(string filePath)
9 | {
10 | Console.WriteLine("SamplePostCopyEventListener is executed.");
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/SamplePlugin/SamplePreCopyEventListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CopierPluginBase;
3 |
4 | namespace SamplePlugin
5 | {
6 | public class SamplePreCopyEventListener: IPreCopyEventListener
7 | {
8 | public void OnPreCopy(string filePath)
9 | {
10 | Console.WriteLine("SamplePreCopyEventListener is executed.");
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/assests/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/copier/a95984e1fdeb495d9db13e015110e1ec5cbccaac/assests/banner.png
--------------------------------------------------------------------------------
/copier-yt-thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/copier/a95984e1fdeb495d9db13e015110e1ec5cbccaac/copier-yt-thumbnail.png
--------------------------------------------------------------------------------
/copier-yt-thumbnail.snagproj:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tarikguney/copier/a95984e1fdeb495d9db13e015110e1ec5cbccaac/copier-yt-thumbnail.snagproj
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Table of Contents
4 | =================
5 |
6 |
7 | * [What is it?](#what-is-it)
8 | * [Underlying Technology](#underlying-technology)
9 | * [How to use?](#how-to-use)
10 | * [An example:](#an-example)
11 | * [Delayed Copy](#delayed-copy)
12 | * [Plugins](#plugins)
13 | * [Having issues?](#having-issues)
14 | * [How to download?](#how-to-download)
15 | * [Watch how this application was developed!](#watch-how-this-application-was-developed)
16 | * [Roadmap](#roadmap)
17 | * [Note](#note)
18 | * [How will you feel when using this tool?](#how-will-you-feel-when-using-this-tool)
19 |
20 |
21 |
22 |
23 |
24 | # What is it?
25 |
26 | A cross-platform (works on Windows, Linux, and Mac) and open-source file watcher and copier application. It allows you to specify folders/files to track and copies them to a target location. It continuously watches changes on the specified files in the background and copies them to the destination directory.
27 |
28 | # Underlying Technology
29 |
30 | The application has been developed with .NET Core technology and depends on the existence of .NET Core framework on the target machine where it is being used. You can simply download the framework from http://dot.net.
31 |
32 | **Note:** The future release will be built without .net core dependency.
33 |
34 | # How to use?
35 |
36 | Currently, it is a command-line application; therefore, you need to be passing a few options to be able to tell what to copy, the source directory, and the destination directory.
37 |
38 | The simple way to find all the available options is to run the tool with `--help` flag such as `dotnet Copier.dll --help`.
39 |
40 | ## An example:
41 | A simple way to start off with this tool would be as follows:
42 |
43 | ```
44 | dotnet Copier.dll -s "C:\source-folder\" -f "*.txt" -d "C:\destination-folder"
45 | ```
46 |
47 | - `-s` is the source folder.
48 | - `-f` is the file name pattern in glob format. For instance, `*.txt` means all the files ending with `.txt` extension.
49 | - `-d` is the destination folder where the files will be copied over.
50 |
51 | By default, if a file already exists in the destination directory, the file won't be copied unless told otherwise with `-o` flag. Check out `--help` for more information about all the available options.
52 |
53 | ## Delayed Copy
54 | One of the major features of this tool is to be able to queue all the files that are changed and copy them to the destination folder with a given delay. For instance, if you specify `-t 5000` option along with the other required options when running the application, it will wait for 5 seconds before copying all the changed files that it has been queuing. An example would look like as following:
55 |
56 | ```
57 | dotnet Copier.dll -s "C:\source-folder\" -f "*.txt" -d "C:\destination-folder" -t 5000
58 | ```
59 |
60 | ## Plugins
61 |
62 | Copier app supports plugin integration. You can easily develop your own plugins and have them executed before and after the copy operations. All you have to do is to reference `CopierPluginBase` in your dotnet core class library, and implement `IPostCopyEventListener` and|or `IPreCopyEventListener` interfaces. Once you build your class library, put the `.dll` file under `plugins` folder in the Copier app installation location. If you don't see this folder, simply create it where the Copier.dll file exists.
63 |
64 | # Having issues?
65 |
66 | Copier app has been developed with an in-depth logging mechanism. You can see all the steps it takes when running it with the debug `-e` flag. If you would like to see more than regular messages but less than debug messages, use `-v` verbose flag. If you still have issues, create a new issue at https://github.com/tarikguney/copier/issues or please send a pull request with the fix. I'd love to see other people's contributions.
67 |
68 |
69 | # How to download?
70 |
71 | You can go to release page (https://github.com/tarikguney/copier/releases) and find the most recent version to download. It is a zip file that you need to unzip on your machine.
72 |
73 | # Watch how this application was developed!
74 |
75 | If you speak in Turkish, you can watch the entire process of this application being developed here:
76 |
77 | https://www.youtube.com/watch?v=aW8W2gze8JE&list=PL_Z0TaFYSF3LLSRobjiV0y-I18kjRm7cx
78 |
79 | # Roadmap
80 |
81 | 1. Accepts all of the arguments from a config file. An sample command would look like the following: `dotnet Copier.dll -f Config.txt`. `-f` is the path of the config file.
82 | 2. Make the executable self-contained, meaning that it won't require .NET Core runtime to be installed on the target machine.
83 | 3. Bump the .NET Core framework version to the latest .NET 5 framework.
84 |
85 | ## Note
86 | *If you'd like to store all the options in a file and have copier app read the options from that file, check out the branch `configfile-experiment`.* The further development will continue from that branch until it is fully merged into `master` branch. For more information, take a look at the `readme.md` file from `configfile-experiment` branch.
87 |
88 |
89 | # How will you feel when using this tool?
90 |
91 | 
--------------------------------------------------------------------------------