public ConflictSet(ConflictFile file, List<ConflictOption> conflicts)
 {
     this.File = file;
     this.Conflicts = conflicts;
 }
 public ConflictSet(ConflictFile file, List <ConflictOption> conflicts)
 {
     this.File      = file;
     this.Conflicts = conflicts;
 }
        private void FindConflictsImpl(string basePath, SlimObservable<ConflictSet> subject, CancellationToken cancellationToken)
        {
            // We may find may conflict files for each conflict, and we need to group them.
            // We can't relay on the order returns by EnumerateFiles either, so it's hard to tell when we've spotted
            // all conflict files. Therefore we need to do this directory by directory, and flush out the cache
            // or conflicts after each directory.

            logger.Debug("Looking for conflicts in {0}", basePath);

            var conflictLookup = new Dictionary<string, List<ParsedConflictFileInfo>>();
            var stack = new Stack<SearchDirectory>();
            stack.Push(new SearchDirectory(basePath, 0));
            while (stack.Count > 0)
            {
                cancellationToken.ThrowIfCancellationRequested();

                conflictLookup.Clear();
                var searchDirectory = stack.Pop();
                var directory = searchDirectory.Directory;

                this.TryFilesystemOperation(() =>
                {
                    foreach (var filePath in this.filesystemProvider.EnumerateFiles(directory, conflictPattern, System.IO.SearchOption.TopDirectoryOnly))
                    {
                        if (this.IsFileIgnored(filePath))
                            continue;

                        ParsedConflictFileInfo conflictFileInfo;
                        // We may not be able to parse it properly (conflictPattern is pretty basic), or it might not exist, or...
                        if (!this.TryFindBaseFileForConflictFile(filePath, out conflictFileInfo))
                            continue;

                        List<ParsedConflictFileInfo> existingConflicts;
                        if (!conflictLookup.TryGetValue(conflictFileInfo.OriginalPath, out existingConflicts))
                        {
                            existingConflicts = new List<ParsedConflictFileInfo>();
                            conflictLookup.Add(conflictFileInfo.OriginalPath, existingConflicts);
                        }
                        existingConflicts.Add(conflictFileInfo);

                        cancellationToken.ThrowIfCancellationRequested();
                    }
                }, directory, "directories");

                foreach (var kvp in conflictLookup)
                {
                    var file = new ConflictFile(kvp.Key, this.filesystemProvider.GetLastWriteTime(kvp.Key), this.filesystemProvider.GetFileSize(kvp.Key));
                    var conflicts = kvp.Value.Select(x => new ConflictOption(x.FilePath, this.filesystemProvider.GetLastWriteTime(x.FilePath), x.Created, this.filesystemProvider.GetFileSize(x.FilePath))).ToList();
                    subject.Next(new ConflictSet(file, conflicts));
                }

                if (searchDirectory.Depth < maxSearchDepth)
                {
                    this.TryFilesystemOperation(() =>
                    {
                        foreach (var subDirectory in this.filesystemProvider.EnumerateDirectories(directory, "*", System.IO.SearchOption.TopDirectoryOnly))
                        {
                            if (IsPathIgnored(subDirectory))
                                continue;

                            stack.Push(new SearchDirectory(subDirectory, searchDirectory.Depth + 1));

                            cancellationToken.ThrowIfCancellationRequested();
                        }
                    }, directory, "files");
                }
                else
                {
                    logger.Warn($"Max search depth of {maxSearchDepth} exceeded with path {directory}. Not proceeding further.");
                }
            }
        }
        private void FindConflictsImpl(string basePath, IObserver <ConflictSet> observer, CancellationToken cancellationToken)
        {
            // We may find may conflict files for each conflict, and we need to group them.
            // We can't relay on the order returns by EnumerateFiles either, so it's hard to tell when we've spotted
            // all conflict files. Therefore we need to do this directory by directory, and flush out the cache
            // or conflicts after each directory.

            logger.Debug("Looking for conflicts in {0}", basePath);

            var conflictLookup = new Dictionary <string, List <ParsedConflictFileInfo> >();
            var stack          = new Stack <SearchDirectory>();

            stack.Push(new SearchDirectory(basePath, 0));
            while (stack.Count > 0)
            {
                cancellationToken.ThrowIfCancellationRequested();

                conflictLookup.Clear();
                var searchDirectory = stack.Pop();
                var directory       = searchDirectory.Directory;

                this.TryFilesystemEnumeration(() =>
                {
                    foreach (var filePath in this.filesystemProvider.EnumerateFiles(directory, conflictPattern, System.IO.SearchOption.TopDirectoryOnly))
                    {
                        if (this.IsFileIgnored(filePath))
                        {
                            continue;
                        }

                        // We may not be able to parse it properly (conflictPattern is pretty basic), or it might not exist, or...
                        if (!this.TryFindBaseFileForConflictFile(filePath, out var conflictFileInfo))
                        {
                            continue;
                        }

                        if (!conflictLookup.TryGetValue(conflictFileInfo.OriginalPath, out var existingConflicts))
                        {
                            existingConflicts = new List <ParsedConflictFileInfo>();
                            conflictLookup.Add(conflictFileInfo.OriginalPath, existingConflicts);
                        }
                        existingConflicts.Add(conflictFileInfo);

                        cancellationToken.ThrowIfCancellationRequested();
                    }
                }, directory, "directories");

                foreach (var kvp in conflictLookup)
                {
                    // The file can have disappeared between us finding it, and this
                    try
                    {
                        var file      = new ConflictFile(kvp.Key, this.filesystemProvider.GetLastWriteTime(kvp.Key), this.filesystemProvider.GetFileSize(kvp.Key));
                        var devices   = this.syncthingManager.Devices.FetchDevices();
                        var conflicts = kvp.Value.Select(x =>
                        {
                            var device = x.ShortDeviceId == null ? null : devices.FirstOrDefault(d => d.ShortDeviceId == x.ShortDeviceId);
                            return(new ConflictOption(x.FilePath, this.filesystemProvider.GetLastWriteTime(x.FilePath), x.Created, this.filesystemProvider.GetFileSize(x.FilePath), device));
                        }).ToList();
                        observer.OnNext(new ConflictSet(file, conflicts));
                        cancellationToken.ThrowIfCancellationRequested();
                    }
                    catch (Exception e)
                    {
                        logger.Error(e, $"Error while trying to access {kvp.Key}, maybe it was deleted since we scanned it?");
                    }
                }

                if (searchDirectory.Depth < maxSearchDepth)
                {
                    this.TryFilesystemEnumeration(() =>
                    {
                        foreach (var subDirectory in this.filesystemProvider.EnumerateDirectories(directory, "*", System.IO.SearchOption.TopDirectoryOnly))
                        {
                            if (IsPathIgnored(subDirectory))
                            {
                                continue;
                            }

                            stack.Push(new SearchDirectory(subDirectory, searchDirectory.Depth + 1));

                            cancellationToken.ThrowIfCancellationRequested();
                        }
                    }, directory, "files");
                }
                else
                {
                    logger.Warn($"Max search depth of {maxSearchDepth} exceeded with path {directory}. Not proceeding further.");
                }
            }
        }