From dc8b502ae1b7bd0aaa00c588ac1c6c63979cd9f4 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Thu, 16 Jan 2025 20:15:39 +0500 Subject: [PATCH 01/18] add Result.cs --- TagsCloudContainer/Result.cs | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 TagsCloudContainer/Result.cs diff --git a/TagsCloudContainer/Result.cs b/TagsCloudContainer/Result.cs new file mode 100644 index 00000000..8c469b91 --- /dev/null +++ b/TagsCloudContainer/Result.cs @@ -0,0 +1,130 @@ +namespace TagsCloudContainer; + +public class None +{ + private None() + { + } +} + +public struct Result +{ + public Result(string error, T value = default(T)) + { + Error = error; + Value = value; + } + public static implicit operator Result(T v) + { + return Result.Ok(v); + } + + public string Error { get; } + internal T Value { get; } + public T GetValueOrThrow() + { + if (IsSuccess) return Value; + throw new InvalidOperationException($"No value. Only Error {Error}"); + } + public bool IsSuccess => Error == null; +} + +public static class Result +{ + public static Result AsResult(this T value) + { + return Ok(value); + } + + public static Result Ok(T value) + { + return new Result(null, value); + } + public static Result Ok() + { + return Ok(null); + } + + public static Result Fail(string e) + { + return new Result(e); + } + + public static Result Of(Func f, string error = null) + { + try + { + return Ok(f()); + } + catch (Exception e) + { + return Fail(error ?? e.Message); + } + } + + public static Result OfAction(Action f, string error = null) + { + try + { + f(); + return Ok(); + } + catch (Exception e) + { + return Fail(error ?? e.Message); + } + } + + public static Result Then( + this Result input, + Func continuation) + { + return input.Then(inp => Of(() => continuation(inp))); + } + + public static Result Then( + this Result input, + Action continuation) + { + return input.Then(inp => OfAction(() => continuation(inp))); + } + + public static Result Then( + this Result input, + Action continuation) + { + return input.Then(inp => OfAction(() => continuation(inp))); + } + + public static Result Then( + this Result input, + Func> continuation) + { + return input.IsSuccess + ? continuation(input.Value) + : Fail(input.Error); + } + + public static Result OnFail( + this Result input, + Action handleError) + { + if (!input.IsSuccess) handleError(input.Error); + return input; + } + + public static Result ReplaceError( + this Result input, + Func replaceError) + { + if (input.IsSuccess) return input; + return Fail(replaceError(input.Error)); + } + + public static Result RefineError( + this Result input, + string errorMessage) + { + return input.ReplaceError(err => errorMessage + ". " + err); + } +} \ No newline at end of file From 99391eb9d10005e10bfdcb7038f6d74fc2e1ec0c Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Fri, 17 Jan 2025 16:14:05 +0500 Subject: [PATCH 02/18] some exceptions --- Client/Program.cs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index f9db896b..cb7b787b 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -1,10 +1,12 @@ using System.Drawing; +using System.Drawing.Text; using System.Reflection; using TagsCloudContainer.Configuration; using TagsCloudContainer.PointGenerators; using TagsCloudContainer.TextProviders; using TagsCloudContainer; using Autofac; +using NPOI.SS.Util.CellWalk; namespace Client { @@ -39,7 +41,21 @@ private static void ConfigureSupportedReadingFormats(Config config) private static void ConfigureFont(Config config) { - config.Font = new Font("arial", 12); + Console.WriteLine("Введите размер шрифта"); + if (!int.TryParse(Console.ReadLine(), out var fontSize)) + throw new Exception("invalid fontSize"); + + Console.WriteLine("Введите название шрифта"); + var fontName = Console.ReadLine(); + if (!CheckFont(fontName)) + throw new Exception("invalid fontName"); + config.Font = new Font(fontName, fontSize); + } + + private static bool CheckFont(string fontName) + { + var fontCollection = new InstalledFontCollection(); + return fontCollection.Families.Any(f => f.Name.Equals(fontName, StringComparison.InvariantCultureIgnoreCase)); } private static void ConfigurePathToSave(Config config) @@ -65,6 +81,8 @@ private static void ConfigureFileSource(Config config) { Console.WriteLine("Введите имя файла источника тэгов"); var inp = Console.ReadLine(); + if ((inp.Length != 0) && !config.SupportedReadingFormats.TryGetValue(Path.GetExtension(inp), out var textProvider)) + throw new Exception("wrong file format for text source"); config.FilePath = inp.Length == 0 ? @"TestFile.txt" : inp; } @@ -95,7 +113,6 @@ private static void ConfigureColor(Config config) } else Console.WriteLine("Цвет будет выбираться случайно"); - } private static void ConfigureCloudView(Config config) @@ -123,11 +140,11 @@ private static Dictionary FindImplemetations() .Where(t => type.IsAssignableFrom(t) && !t.IsInterface) .ToDictionary(x => x.GetCustomAttribute().LabelText.ToLower(), x => x); } - + private static string? ReadValue(string? argName = null) { Console.Write($"{argName ?? ""}: "); return Console.ReadLine(); } } -} +} \ No newline at end of file From b02e7bceac1854e7eec76fae3515eb51d45090e8 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Sat, 18 Jan 2025 13:29:44 +0500 Subject: [PATCH 03/18] result pattern for comfiguration --- Client/Program.cs | 81 +++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index cb7b787b..69227279 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -16,56 +16,63 @@ static void Main() { var config = new Config(); - ConfigureSupportedReadingFormats(config); - ConfigureFileSource(config); - ConfigureCloudView(config); - ConfigureColor(config); - ConfigurePathToSave(config); - ConfigureStartPoint(config); - ConfigureFont(config); - - var container = DependencyInjection.BuildContainer(config); - using var scope = container.BeginLifetimeScope(); - scope.Resolve().DrawPicture(); - Console.WriteLine($"результат сохранен в {config.PicturePath}"); + var result = ConfigureSupportedReadingFormats(config) + .Then(ConfigureFileSource) + .Then(ConfigureCloudView) + .Then(ConfigureColor) + .Then(ConfigurePathToSave) + .Then(ConfigureStartPoint) + .Then(ConfigureFont); + + if (result.IsSuccess) + { + var container = DependencyInjection.BuildContainer(config); + using var scope = container.BeginLifetimeScope(); + scope.Resolve().DrawPicture(); + Console.WriteLine($"результат сохранен в {config.PicturePath}"); + } + Console.WriteLine($"Ошибка конфигурирования: {result.Error}"); } - private static void ConfigureSupportedReadingFormats(Config config) + private static Result ConfigureSupportedReadingFormats(Config config) { Console.WriteLine("Поддерживаются следующие форматы файлов для чтения:"); var textProviders = FindImplemetations(); foreach (var point in textProviders) Console.WriteLine("\t" + point.Key); config.SupportedReadingFormats = textProviders; + return Result.Ok(config); } - private static void ConfigureFont(Config config) + private static Result ConfigureFont(Config config) { Console.WriteLine("Введите размер шрифта"); if (!int.TryParse(Console.ReadLine(), out var fontSize)) - throw new Exception("invalid fontSize"); + return Result.Fail("invalid fontSize"); - Console.WriteLine("Введите название шрифта"); + Console.WriteLine("Введите название шрифта"); var fontName = Console.ReadLine(); if (!CheckFont(fontName)) - throw new Exception("invalid fontName"); + return Result.Fail("invalid fontName"); config.Font = new Font(fontName, fontSize); + return Result.Ok(config); } private static bool CheckFont(string fontName) { var fontCollection = new InstalledFontCollection(); - return fontCollection.Families.Any(f => f.Name.Equals(fontName, StringComparison.InvariantCultureIgnoreCase)); + return fontCollection.Families.Any( + f => f.Name.Equals(fontName, StringComparison.InvariantCultureIgnoreCase)); } - private static void ConfigurePathToSave(Config config) + private static Result ConfigurePathToSave(Config config) { - Console.WriteLine("Введите полный путь и название файла для сохранения"); - var inp = Console.ReadLine(); + var inp = ReadValue("Введите полный путь и название файла для сохранения"); config.PicturePath = inp.Length == 0 ? "1.bmp" : inp; + return Result.Ok(config); } - private static void ConfigureStartPoint(Config config) + private static Result ConfigureStartPoint(Config config) { Console.WriteLine("Введите координаты центра поля для рисования" + "\n При некорректном вводе координаты центра составят ( 1000, 1000)"); @@ -74,16 +81,20 @@ private static void ConfigureStartPoint(Config config) if (int.TryParse(xLine, out var xResult) && int.TryParse(yLine, out var yResult)) config.StartPoint = new Point(xResult, yResult); - config.StartPoint = new Point(1000, 1000); + else + config.StartPoint = new Point(1000, 1000); + return Result.Ok(config); } - private static void ConfigureFileSource(Config config) + private static Result ConfigureFileSource(Config config) { Console.WriteLine("Введите имя файла источника тэгов"); var inp = Console.ReadLine(); - if ((inp.Length != 0) && !config.SupportedReadingFormats.TryGetValue(Path.GetExtension(inp), out var textProvider)) - throw new Exception("wrong file format for text source"); + if ((inp.Length != 0) && + !config.SupportedReadingFormats.TryGetValue(Path.GetExtension(inp), out var textProvider)) + return Result.Fail("wrong file format for text source"); config.FilePath = inp.Length == 0 ? @"TestFile.txt" : inp; + return Result.Ok(config); } private static string GetLabel(RainbowColors color) @@ -94,7 +105,7 @@ private static string GetLabel(RainbowColors color) return attribute.LabelText; } - private static void ConfigureColor(Config config) + private static Result ConfigureColor(Config config) { Console.WriteLine("Выборите цвет из возможных:"); var colors = Enum.GetValues(typeof(RainbowColors)) @@ -104,8 +115,8 @@ private static void ConfigureColor(Config config) foreach (var color in colors) Console.WriteLine("\t" + color.Key); - Console.WriteLine("В случае неправильного ввода - цвет будет выбираться случайным образом"); - var inp = Console.ReadLine().ToLower(); + var inp = ReadValue("В случае неправильного ввода - цвет будет выбираться случайным образом") + .ToLower(); if (colors.TryGetValue(inp, out var colorName)) { config.Color = Color.FromName(colorName.ToString()); @@ -113,9 +124,11 @@ private static void ConfigureColor(Config config) } else Console.WriteLine("Цвет будет выбираться случайно"); + + return Result.Ok(config); } - private static void ConfigureCloudView(Config config) + private static Result ConfigureCloudView(Config config) { Console.WriteLine("Выберите внешний вид облака из возможных:"); var pointGenerators = FindImplemetations(); @@ -124,12 +137,12 @@ private static void ConfigureCloudView(Config config) Console.WriteLine("Введите, соблюдая орфографию"); var pointGenerator = Console.ReadLine().ToLower(); if (pointGenerators.TryGetValue(pointGenerator, out var pointGeneratorName)) - config.PointGenerator = pointGeneratorName; - else { - Console.WriteLine("Такой формы не предусмотрено"); - ConfigureCloudView(config); + config.PointGenerator = pointGeneratorName; + return Result.Ok(config); } + + return Result.Fail("Такой формы не предусмотрено"); } private static Dictionary FindImplemetations() From f705e0dd05d717e4e742e911e891876b612a3488 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Sat, 18 Jan 2025 17:29:35 +0500 Subject: [PATCH 04/18] result pattern to other exceptions --- Client/Program.cs | 37 +++++++++++-------- TagsCloudContainer.Tests/CloudLayoutShould.cs | 2 +- .../TagGeneratorShould.cs | 7 ++-- .../TxtTextProviderShould.cs | 4 +- TagsCloudContainer/CloudLayout.cs | 14 +++++-- TagsCloudContainer/PictureMaker.cs | 2 +- .../TextProcessor/TextProcessor.cs | 2 +- .../TextProviders/DocTextProvider.cs | 4 +- .../TextProviders/DocXTextProvider.cs | 4 +- .../TextProviders/ITextProvider.cs | 2 +- .../TextProviders/TxtTextProvider.cs | 8 ++-- 11 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index 69227279..2354ae0f 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -16,13 +16,7 @@ static void Main() { var config = new Config(); - var result = ConfigureSupportedReadingFormats(config) - .Then(ConfigureFileSource) - .Then(ConfigureCloudView) - .Then(ConfigureColor) - .Then(ConfigurePathToSave) - .Then(ConfigureStartPoint) - .Then(ConfigureFont); + var result = ConfigureApp(config); if (result.IsSuccess) { @@ -31,17 +25,29 @@ static void Main() scope.Resolve().DrawPicture(); Console.WriteLine($"результат сохранен в {config.PicturePath}"); } - Console.WriteLine($"Ошибка конфигурирования: {result.Error}"); + else + Console.WriteLine($"Ошибка конфигурирования: {result.Error}"); } - private static Result ConfigureSupportedReadingFormats(Config config) + private static Result ConfigureApp(Config config) + { + return ConfigureSupportedReadingFormats(config).AsResult() + .Then(ConfigureFileSource) + .Then(ConfigureCloudView) + .Then(ConfigureColor) + .Then(ConfigurePathToSave) + .Then(ConfigureStartPoint) + .Then(ConfigureFont); + } + + private static Config ConfigureSupportedReadingFormats(Config config) { Console.WriteLine("Поддерживаются следующие форматы файлов для чтения:"); var textProviders = FindImplemetations(); foreach (var point in textProviders) Console.WriteLine("\t" + point.Key); config.SupportedReadingFormats = textProviders; - return Result.Ok(config); + return config; } private static Result ConfigureFont(Config config) @@ -65,11 +71,11 @@ private static bool CheckFont(string fontName) f => f.Name.Equals(fontName, StringComparison.InvariantCultureIgnoreCase)); } - private static Result ConfigurePathToSave(Config config) + private static Config ConfigurePathToSave(Config config) { var inp = ReadValue("Введите полный путь и название файла для сохранения"); config.PicturePath = inp.Length == 0 ? "1.bmp" : inp; - return Result.Ok(config); + return config; } private static Result ConfigureStartPoint(Config config) @@ -88,8 +94,7 @@ private static Result ConfigureStartPoint(Config config) private static Result ConfigureFileSource(Config config) { - Console.WriteLine("Введите имя файла источника тэгов"); - var inp = Console.ReadLine(); + var inp = ReadValue("Введите имя файла источника тэгов"); if ((inp.Length != 0) && !config.SupportedReadingFormats.TryGetValue(Path.GetExtension(inp), out var textProvider)) return Result.Fail("wrong file format for text source"); @@ -105,7 +110,7 @@ private static string GetLabel(RainbowColors color) return attribute.LabelText; } - private static Result ConfigureColor(Config config) + private static Config ConfigureColor(Config config) { Console.WriteLine("Выборите цвет из возможных:"); var colors = Enum.GetValues(typeof(RainbowColors)) @@ -125,7 +130,7 @@ private static Result ConfigureColor(Config config) else Console.WriteLine("Цвет будет выбираться случайно"); - return Result.Ok(config); + return config; } private static Result ConfigureCloudView(Config config) diff --git a/TagsCloudContainer.Tests/CloudLayoutShould.cs b/TagsCloudContainer.Tests/CloudLayoutShould.cs index c99dd167..d9c5b69b 100644 --- a/TagsCloudContainer.Tests/CloudLayoutShould.cs +++ b/TagsCloudContainer.Tests/CloudLayoutShould.cs @@ -37,7 +37,7 @@ public void PutNextRectangle_ShouldKeepEnteredSize() { var layout = new CloudLayout(new Point(5, 5), new ArchemedianSpiral()); var enteredSize = new Size(3, 4); - var returnedSize = layout.PutNextRectangle(enteredSize).Size; + var returnedSize = layout.PutNextRectangle(enteredSize).GetValueOrThrow().Size; returnedSize.Should().BeEquivalentTo(enteredSize); } diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs index 3ab47e91..42913e40 100644 --- a/TagsCloudContainer.Tests/TagGeneratorShould.cs +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -11,9 +11,10 @@ public class TagGeneratorShould [Test] public void SetRightFontSize() { - var processor = new TextProcessor.TextProcessor( - new TxtTextProvider(@"TextFile1.txt"), new RegexParser(), new ToLowerFilter(), new BoringWordFilter()); - var words = processor.WordFrequencies(); + var words = new Dictionary() + { + {new Word("a"), 3} + }; var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); var result = generator.GenerateTags(words).First(); diff --git a/TagsCloudContainer.Tests/TxtTextProviderShould.cs b/TagsCloudContainer.Tests/TxtTextProviderShould.cs index 461f2818..e5f7829b 100644 --- a/TagsCloudContainer.Tests/TxtTextProviderShould.cs +++ b/TagsCloudContainer.Tests/TxtTextProviderShould.cs @@ -15,9 +15,9 @@ public void Setup() [Test] public void ThrowExceptionIfFileNotFounded() { - Action act = () => _provider.ReadFile(); + var result = _provider.ReadFile(); - act.Should().Throw(); + result.Error.Should().Be("File NotExisted.txt does not exist"); } } } \ No newline at end of file diff --git a/TagsCloudContainer/CloudLayout.cs b/TagsCloudContainer/CloudLayout.cs index bcc26d51..21371f62 100644 --- a/TagsCloudContainer/CloudLayout.cs +++ b/TagsCloudContainer/CloudLayout.cs @@ -9,6 +9,7 @@ public class CloudLayout public readonly Size Size; private readonly IEnumerable _points; private List Rectangles { get; set; } + private readonly Rectangle _frame; public CloudLayout(Point center, IPointGenerator pointGenerator) @@ -19,6 +20,7 @@ public CloudLayout(Point center, IPointGenerator pointGenerator) Size = CountSize(center); Rectangles = []; _points = pointGenerator.GeneratePoints(Center); + _frame = new Rectangle(0, 0, Size.Width, Size.Height); } public CloudLayout(Size size, IPointGenerator pointGenerator) @@ -27,6 +29,7 @@ public CloudLayout(Size size, IPointGenerator pointGenerator) Center = FindCenter(size); Rectangles = []; _points = pointGenerator.GeneratePoints(Center); + _frame = new Rectangle(0, 0, Size.Width, Size.Height); } @@ -42,7 +45,7 @@ private static Point FindCenter(Size size) return new Point(size.Width / 2, size.Height / 2); } - public Rectangle PutNextRectangle(Size rectangleSize) + public Result PutNextRectangle(Size rectangleSize) { foreach (var point in _points) { @@ -50,8 +53,13 @@ public Rectangle PutNextRectangle(Size rectangleSize) rectangleSize); if (IntersectsWithAnyOther(supposed, Rectangles)) continue; - Rectangles.Add(supposed); - return supposed; + if (_frame.Contains(supposed)) + { + Rectangles.Add(supposed); + return supposed; + } + + return Result.Fail("Вышли за границы рисунка"); } throw new ArgumentException("Not Enough Points Generated"); } diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index 1b58c696..64f7e4ab 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -28,7 +28,7 @@ public void DrawPicture() foreach (var tag in _tags) { var rectangle = layout.PutNextRectangle(tag.Frame); - DrawTag(image, rectangle, tag); + DrawTag(image, rectangle.GetValueOrThrow(), tag); } image.Save(_fileName); } diff --git a/TagsCloudContainer/TextProcessor/TextProcessor.cs b/TagsCloudContainer/TextProcessor/TextProcessor.cs index 03f2fe10..95de04a6 100644 --- a/TagsCloudContainer/TextProcessor/TextProcessor.cs +++ b/TagsCloudContainer/TextProcessor/TextProcessor.cs @@ -9,7 +9,7 @@ public class TextProcessor(ITextProvider provider, IStringParser parser, { public Dictionary WordFrequencies() { - var words = parser.GetWordsFromString(provider.ReadFile()); + var words = parser.GetWordsFromString(provider.ReadFile().GetValueOrThrow()); return filters.Aggregate(words, (current, filter) => filter.Process(current)) .GroupBy(word => word) .ToDictionary(group => group.Key, group => group.Count()); diff --git a/TagsCloudContainer/TextProviders/DocTextProvider.cs b/TagsCloudContainer/TextProviders/DocTextProvider.cs index cfdd9cc1..c9778b58 100644 --- a/TagsCloudContainer/TextProviders/DocTextProvider.cs +++ b/TagsCloudContainer/TextProviders/DocTextProvider.cs @@ -12,10 +12,10 @@ public DocTextProvider(string filePath) _filePath = filePath; } - public string ReadFile() + public Result ReadFile() { if (!File.Exists(_filePath)) - throw new FileNotFoundException(); + return new Result($"File not found: {_filePath}"); using var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read); var document = new HWPFDocument(stream); var range = document.GetRange(); diff --git a/TagsCloudContainer/TextProviders/DocXTextProvider.cs b/TagsCloudContainer/TextProviders/DocXTextProvider.cs index e4e83b22..28f55319 100644 --- a/TagsCloudContainer/TextProviders/DocXTextProvider.cs +++ b/TagsCloudContainer/TextProviders/DocXTextProvider.cs @@ -12,10 +12,10 @@ public DocXTextProvider(string filePath) _filePath = filePath; } - public string ReadFile() + public Result ReadFile() { if (!File.Exists(_filePath)) - throw new FileNotFoundException(); + return Result.Fail($"File {_filePath} does not exist"); using var document = DocX.Load(_filePath); return document.Text; } diff --git a/TagsCloudContainer/TextProviders/ITextProvider.cs b/TagsCloudContainer/TextProviders/ITextProvider.cs index cb7a29b5..594caa74 100644 --- a/TagsCloudContainer/TextProviders/ITextProvider.cs +++ b/TagsCloudContainer/TextProviders/ITextProvider.cs @@ -2,5 +2,5 @@ public interface ITextProvider { - public string ReadFile(); + public Result ReadFile(); } \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/TxtTextProvider.cs b/TagsCloudContainer/TextProviders/TxtTextProvider.cs index 1d774fb6..327502e9 100644 --- a/TagsCloudContainer/TextProviders/TxtTextProvider.cs +++ b/TagsCloudContainer/TextProviders/TxtTextProvider.cs @@ -10,10 +10,10 @@ public TxtTextProvider(string filePath) _filePath = filePath; } - public string ReadFile() + public Result ReadFile() { - if (!File.Exists(_filePath)) - throw new FileNotFoundException(); - return File.ReadAllText(_filePath); + return !File.Exists(_filePath) + ? Result.Fail($"File {_filePath} does not exist") + : File.ReadAllText(_filePath); } } \ No newline at end of file From e9192f1f3213e3afded319499759c3661580d629 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Sun, 19 Jan 2025 14:21:11 +0500 Subject: [PATCH 05/18] OnFail except checking --- Client/Program.cs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index 2354ae0f..7237c6f8 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -16,17 +16,12 @@ static void Main() { var config = new Config(); - var result = ConfigureApp(config); - - if (result.IsSuccess) - { - var container = DependencyInjection.BuildContainer(config); - using var scope = container.BeginLifetimeScope(); - scope.Resolve().DrawPicture(); - Console.WriteLine($"результат сохранен в {config.PicturePath}"); - } - else - Console.WriteLine($"Ошибка конфигурирования: {result.Error}"); + ConfigureApp(config).OnFail(error => Console.WriteLine($"Ошибка конфигурирования: {error}")); + + var container = DependencyInjection.BuildContainer(config); + using var scope = container.BeginLifetimeScope(); + scope.Resolve().DrawPicture(); + Console.WriteLine($"результат сохранен в {config.PicturePath}"); } private static Result ConfigureApp(Config config) @@ -146,10 +141,9 @@ private static Result ConfigureCloudView(Config config) config.PointGenerator = pointGeneratorName; return Result.Ok(config); } - return Result.Fail("Такой формы не предусмотрено"); } - + private static Dictionary FindImplemetations() { var assembly = Assembly.LoadFrom("TagsCloudContainer.dll"); @@ -159,7 +153,7 @@ private static Dictionary FindImplemetations() .ToDictionary(x => x.GetCustomAttribute().LabelText.ToLower(), x => x); } - private static string? ReadValue(string? argName = null) + private static string ReadValue(string? argName = null) { Console.Write($"{argName ?? ""}: "); return Console.ReadLine(); From 79b05fceb5f8017e1db73e132dcffafefcf25be9 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Sun, 19 Jan 2025 18:49:58 +0500 Subject: [PATCH 06/18] senseis notes are taken into account --- Client/Program.cs | 13 +++++++++---- TagsCloudContainer.Tests/TagGeneratorShould.cs | 3 ++- TagsCloudContainer/CloudLayout.cs | 2 +- TagsCloudContainer/PictureMaker.cs | 18 +++++++++++++----- .../TagGenerator/ITagsGenerator.cs | 2 +- .../TagGenerator/TagGenerator.cs | 7 +++++-- .../TextProcessor/ITextProcessor.cs | 2 +- .../TextProcessor/TextProcessor.cs | 12 +++++++----- .../TextProviders/DocTextProvider.cs | 4 +--- .../TextProviders/DocXTextProvider.cs | 2 +- .../TextProviders/TxtTextProvider.cs | 2 +- 11 files changed, 42 insertions(+), 25 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index 7237c6f8..63f7076c 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -16,12 +16,17 @@ static void Main() { var config = new Config(); - ConfigureApp(config).OnFail(error => Console.WriteLine($"Ошибка конфигурирования: {error}")); - + ConfigureApp(config) + .OnFail(error => Console.WriteLine($"Ошибка конфигурирования: {error}")) + .Then(Run); + } + + private static void Run(Config config) + { var container = DependencyInjection.BuildContainer(config); using var scope = container.BeginLifetimeScope(); - scope.Resolve().DrawPicture(); - Console.WriteLine($"результат сохранен в {config.PicturePath}"); + scope.Resolve().DrawPicture().OnFail(error => Console.WriteLine($"Ошибка обработки: {error}")) + .Then(a => Console.WriteLine($"результат сохранен в {config.PicturePath}")); } private static Result ConfigureApp(Config config) diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs index 42913e40..984d4a33 100644 --- a/TagsCloudContainer.Tests/TagGeneratorShould.cs +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -15,8 +15,9 @@ public void SetRightFontSize() { {new Word("a"), 3} }; + var resultWords = Result.Ok(words); var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); - var result = generator.GenerateTags(words).First(); + var result = generator.GenerateTags(resultWords).Value.First(); result.Font.Name.Should().Be("Arial"); result.Font.Size.Should().Be(36); diff --git a/TagsCloudContainer/CloudLayout.cs b/TagsCloudContainer/CloudLayout.cs index 21371f62..0c21865d 100644 --- a/TagsCloudContainer/CloudLayout.cs +++ b/TagsCloudContainer/CloudLayout.cs @@ -61,7 +61,7 @@ public Result PutNextRectangle(Size rectangleSize) return Result.Fail("Вышли за границы рисунка"); } - throw new ArgumentException("Not Enough Points Generated"); + return Result.Fail("Not Enough Points Generated"); } public static bool IntersectsWithAnyOther(Rectangle supposed, List others) diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index 64f7e4ab..a57a2078 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -8,7 +8,8 @@ namespace TagsCloudContainer; public class PictureMaker { private readonly IPointGenerator _pointGenerator; - private readonly IEnumerable _tags; + private readonly ITagsGenerator _tagGenerator; + private readonly ITextProcessor _textProcessor; private readonly string _fileName; private readonly Point _startPoint; @@ -16,21 +17,28 @@ public PictureMaker(IPointGenerator pointGenerator, ITagsGenerator tagGenerator, ITextProcessor textProcessor, string fileName, Point startPoint) { _pointGenerator = pointGenerator; - _tags = tagGenerator.GenerateTags(textProcessor.WordFrequencies()); + _tagGenerator = tagGenerator; + _textProcessor = textProcessor; _fileName = fileName; _startPoint = startPoint; } - public void DrawPicture() + public Result DrawPicture() { var layout = new CloudLayout(_startPoint, _pointGenerator); using var image = new Bitmap(layout.Size.Width, layout.Size.Height); - foreach (var tag in _tags) + var tags = _tagGenerator.GenerateTags(_textProcessor.WordFrequencies()); + if (!tags.IsSuccess) + return Result.Fail(tags.Error); + foreach (var tag in tags.Value) { var rectangle = layout.PutNextRectangle(tag.Frame); - DrawTag(image, rectangle.GetValueOrThrow(), tag); + if (!rectangle.IsSuccess) + return Result.Fail(rectangle.Error);; + DrawTag(image, rectangle.Value, tag); } image.Save(_fileName); + return new Result(); } private static void DrawTag(Bitmap image, Rectangle rectangle, Tag tag) diff --git a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs index 421c4f02..37d359ee 100644 --- a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs +++ b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs @@ -2,6 +2,6 @@ { public interface ITagsGenerator { - IEnumerable GenerateTags(Dictionary wordsDictionary); + Result> GenerateTags(Result> wordsDictionary); } } diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index dff3a334..3727b805 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -17,11 +17,14 @@ public TagGenerator(IColorProvider colorProvider, Font defaultFont ) _defaultFont = defaultFont; } - public IEnumerable GenerateTags(Dictionary wordsDictionary) + public Result> GenerateTags(Result> wordsDictionary) { - return wordsDictionary + if (!wordsDictionary.IsSuccess) + return Result.Fail>(wordsDictionary.Error); + var tags = wordsDictionary.Value .Select(kvp => new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), _colorProvider.GetColor(), SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); + return Result.Ok(tags); } private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) diff --git a/TagsCloudContainer/TextProcessor/ITextProcessor.cs b/TagsCloudContainer/TextProcessor/ITextProcessor.cs index b22fb0fd..d227e9fb 100644 --- a/TagsCloudContainer/TextProcessor/ITextProcessor.cs +++ b/TagsCloudContainer/TextProcessor/ITextProcessor.cs @@ -2,6 +2,6 @@ { public interface ITextProcessor { - public Dictionary WordFrequencies(); + public Result> WordFrequencies(); } } diff --git a/TagsCloudContainer/TextProcessor/TextProcessor.cs b/TagsCloudContainer/TextProcessor/TextProcessor.cs index 95de04a6..a4cec4ec 100644 --- a/TagsCloudContainer/TextProcessor/TextProcessor.cs +++ b/TagsCloudContainer/TextProcessor/TextProcessor.cs @@ -7,11 +7,13 @@ namespace TagsCloudContainer.TextProcessor; public class TextProcessor(ITextProvider provider, IStringParser parser, params IWordFilter[] filters) : ITextProcessor { - public Dictionary WordFrequencies() + public Result> WordFrequencies() { - var words = parser.GetWordsFromString(provider.ReadFile().GetValueOrThrow()); - return filters.Aggregate(words, (current, filter) => filter.Process(current)) - .GroupBy(word => word) - .ToDictionary(group => group.Key, group => group.Count()); + return provider.ReadFile() + .Then(text => parser.GetWordsFromString(text)) + .Then(words => filters.Aggregate(words, (current, filter) => filter.Process(current)) + .GroupBy(word => word) + .ToDictionary(group => group.Key, group => group.Count())); + } } \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/DocTextProvider.cs b/TagsCloudContainer/TextProviders/DocTextProvider.cs index c9778b58..d2cf9736 100644 --- a/TagsCloudContainer/TextProviders/DocTextProvider.cs +++ b/TagsCloudContainer/TextProviders/DocTextProvider.cs @@ -17,8 +17,6 @@ public Result ReadFile() if (!File.Exists(_filePath)) return new Result($"File not found: {_filePath}"); using var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read); - var document = new HWPFDocument(stream); - var range = document.GetRange(); - return range.Text; + return Result.Of(() => new HWPFDocument(stream)).Then(document=> document.GetRange().Text); } } \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/DocXTextProvider.cs b/TagsCloudContainer/TextProviders/DocXTextProvider.cs index 28f55319..4c9881a8 100644 --- a/TagsCloudContainer/TextProviders/DocXTextProvider.cs +++ b/TagsCloudContainer/TextProviders/DocXTextProvider.cs @@ -17,6 +17,6 @@ public Result ReadFile() if (!File.Exists(_filePath)) return Result.Fail($"File {_filePath} does not exist"); using var document = DocX.Load(_filePath); - return document.Text; + return Result.Of(() => document.Text); } } \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/TxtTextProvider.cs b/TagsCloudContainer/TextProviders/TxtTextProvider.cs index 327502e9..1f2c2b63 100644 --- a/TagsCloudContainer/TextProviders/TxtTextProvider.cs +++ b/TagsCloudContainer/TextProviders/TxtTextProvider.cs @@ -14,6 +14,6 @@ public Result ReadFile() { return !File.Exists(_filePath) ? Result.Fail($"File {_filePath} does not exist") - : File.ReadAllText(_filePath); + : Result.Of(()=> File.ReadAllText(_filePath)); } } \ No newline at end of file From 110e46f242d55549c82a736badea62820aca80a8 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Sun, 19 Jan 2025 18:58:19 +0500 Subject: [PATCH 07/18] old tests have to pass! --- TagsCloudContainer.Tests/TagGeneratorShould.cs | 2 +- TagsCloudContainer.Tests/TextProcessorShould.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs index 984d4a33..e623c99d 100644 --- a/TagsCloudContainer.Tests/TagGeneratorShould.cs +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -17,7 +17,7 @@ public void SetRightFontSize() }; var resultWords = Result.Ok(words); var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); - var result = generator.GenerateTags(resultWords).Value.First(); + var result = generator.GenerateTags(resultWords).GetValueOrThrow().First(); result.Font.Name.Should().Be("Arial"); result.Font.Size.Should().Be(36); diff --git a/TagsCloudContainer.Tests/TextProcessorShould.cs b/TagsCloudContainer.Tests/TextProcessorShould.cs index 07a5cfa9..7408da7a 100644 --- a/TagsCloudContainer.Tests/TextProcessorShould.cs +++ b/TagsCloudContainer.Tests/TextProcessorShould.cs @@ -13,9 +13,9 @@ public void Process() var result = new TextProcessor.TextProcessor( new TxtTextProvider(@"TextFile1.txt"), new RegexParser(), new ToLowerFilter(), new BoringWordFilter(), new ShortWordFilter()).WordFrequencies(); - result.Count.Should().Be(3); + result.GetValueOrThrow().Count.Should().Be(3); - result.MaxBy(word => word.Value).Value.Should().Be(3); + result.GetValueOrThrow().MaxBy(word => word.Value).Value.Should().Be(3); } } } From 23fa57dc96460341f574768ae697651b7ab488d9 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Mon, 20 Jan 2025 21:23:09 +0500 Subject: [PATCH 08/18] ITagGenerator goes back to roots) --- TagsCloudContainer.Tests/TagGeneratorShould.cs | 3 +-- TagsCloudContainer/PictureMaker.cs | 11 ++++++----- TagsCloudContainer/TagGenerator/ITagsGenerator.cs | 2 +- TagsCloudContainer/TagGenerator/TagGenerator.cs | 7 ++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs index e623c99d..42913e40 100644 --- a/TagsCloudContainer.Tests/TagGeneratorShould.cs +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -15,9 +15,8 @@ public void SetRightFontSize() { {new Word("a"), 3} }; - var resultWords = Result.Ok(words); var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); - var result = generator.GenerateTags(resultWords).GetValueOrThrow().First(); + var result = generator.GenerateTags(words).First(); result.Font.Name.Should().Be("Arial"); result.Font.Size.Should().Be(36); diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index a57a2078..5eb8dcde 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -27,10 +27,11 @@ public Result DrawPicture() { var layout = new CloudLayout(_startPoint, _pointGenerator); using var image = new Bitmap(layout.Size.Width, layout.Size.Height); - var tags = _tagGenerator.GenerateTags(_textProcessor.WordFrequencies()); - if (!tags.IsSuccess) - return Result.Fail(tags.Error); - foreach (var tag in tags.Value) + var wordsDictionary = _textProcessor.WordFrequencies(); + if (!wordsDictionary.IsSuccess) + return Result.Fail(wordsDictionary.Error); + var tags = _tagGenerator.GenerateTags(wordsDictionary.Value); + foreach (var tag in tags) { var rectangle = layout.PutNextRectangle(tag.Frame); if (!rectangle.IsSuccess) @@ -38,7 +39,7 @@ public Result DrawPicture() DrawTag(image, rectangle.Value, tag); } image.Save(_fileName); - return new Result(); + return Result.Ok(); } private static void DrawTag(Bitmap image, Rectangle rectangle, Tag tag) diff --git a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs index 37d359ee..421c4f02 100644 --- a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs +++ b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs @@ -2,6 +2,6 @@ { public interface ITagsGenerator { - Result> GenerateTags(Result> wordsDictionary); + IEnumerable GenerateTags(Dictionary wordsDictionary); } } diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index 3727b805..dff3a334 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -17,14 +17,11 @@ public TagGenerator(IColorProvider colorProvider, Font defaultFont ) _defaultFont = defaultFont; } - public Result> GenerateTags(Result> wordsDictionary) + public IEnumerable GenerateTags(Dictionary wordsDictionary) { - if (!wordsDictionary.IsSuccess) - return Result.Fail>(wordsDictionary.Error); - var tags = wordsDictionary.Value + return wordsDictionary .Select(kvp => new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), _colorProvider.GetColor(), SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); - return Result.Ok(tags); } private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) From 6f1ae2e13efe8e1ab9bb3d3699a24cac51e84477 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Mon, 20 Jan 2025 22:37:28 +0500 Subject: [PATCH 09/18] result class core with extensions --- TagsCloudContainer/Result.cs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/TagsCloudContainer/Result.cs b/TagsCloudContainer/Result.cs index 8c469b91..75f13960 100644 --- a/TagsCloudContainer/Result.cs +++ b/TagsCloudContainer/Result.cs @@ -74,26 +74,21 @@ public static Result OfAction(Action f, string error = null) return Fail(error ?? e.Message); } } - +} +public static class ResultExtensions +{ public static Result Then( this Result input, Func continuation) { - return input.Then(inp => Of(() => continuation(inp))); - } - - public static Result Then( - this Result input, - Action continuation) - { - return input.Then(inp => OfAction(() => continuation(inp))); + return input.Then(inp => Result.Of(() => continuation(inp))); } public static Result Then( this Result input, Action continuation) { - return input.Then(inp => OfAction(() => continuation(inp))); + return input.Then(inp => Result.OfAction(() => continuation(inp))); } public static Result Then( @@ -102,7 +97,7 @@ public static Result Then( { return input.IsSuccess ? continuation(input.Value) - : Fail(input.Error); + : Result.Fail(input.Error); } public static Result OnFail( @@ -118,7 +113,7 @@ public static Result ReplaceError( Func replaceError) { if (input.IsSuccess) return input; - return Fail(replaceError(input.Error)); + return Result.Fail(replaceError(input.Error)); } public static Result RefineError( @@ -127,4 +122,5 @@ public static Result RefineError( { return input.ReplaceError(err => errorMessage + ". " + err); } -} \ No newline at end of file +} + From e12f33cb1c6c7be54e047ff754c58f14a48b325b Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Tue, 21 Jan 2025 21:20:28 +0500 Subject: [PATCH 10/18] aestetics --- Client/Program.cs | 3 +- TagsCloudContainer/PictureMaker.cs | 29 ++++++++++--------- .../TextProcessor/TextProcessor.cs | 1 - .../TextProviders/DocTextProvider.cs | 3 +- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Client/Program.cs b/Client/Program.cs index 63f7076c..da3e3835 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -25,7 +25,8 @@ private static void Run(Config config) { var container = DependencyInjection.BuildContainer(config); using var scope = container.BeginLifetimeScope(); - scope.Resolve().DrawPicture().OnFail(error => Console.WriteLine($"Ошибка обработки: {error}")) + scope.Resolve().DrawPicture() + .OnFail(error => Console.WriteLine($"Ошибка обработки: {error}")) .Then(a => Console.WriteLine($"результат сохранен в {config.PicturePath}")); } diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index 5eb8dcde..6963c2c7 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -27,20 +27,23 @@ public Result DrawPicture() { var layout = new CloudLayout(_startPoint, _pointGenerator); using var image = new Bitmap(layout.Size.Width, layout.Size.Height); - var wordsDictionary = _textProcessor.WordFrequencies(); - if (!wordsDictionary.IsSuccess) - return Result.Fail(wordsDictionary.Error); - var tags = _tagGenerator.GenerateTags(wordsDictionary.Value); - foreach (var tag in tags) - { - var rectangle = layout.PutNextRectangle(tag.Frame); - if (!rectangle.IsSuccess) - return Result.Fail(rectangle.Error);; - DrawTag(image, rectangle.Value, tag); + + return _textProcessor.WordFrequencies() + .Then(wordsDict => + { + var tags = _tagGenerator.GenerateTags(wordsDict); + foreach (var tag in tags) + { + var rectangle = layout.PutNextRectangle(tag.Frame); + if (!rectangle.IsSuccess) + return Result.Fail(rectangle.Error);; + DrawTag(image, rectangle.Value, tag); + } + image.Save(_fileName); + return Result.Ok(); + }) + .OnFail(error => Result.Fail(error)); } - image.Save(_fileName); - return Result.Ok(); - } private static void DrawTag(Bitmap image, Rectangle rectangle, Tag tag) { diff --git a/TagsCloudContainer/TextProcessor/TextProcessor.cs b/TagsCloudContainer/TextProcessor/TextProcessor.cs index a4cec4ec..773d420c 100644 --- a/TagsCloudContainer/TextProcessor/TextProcessor.cs +++ b/TagsCloudContainer/TextProcessor/TextProcessor.cs @@ -14,6 +14,5 @@ public Result> WordFrequencies() .Then(words => filters.Aggregate(words, (current, filter) => filter.Process(current)) .GroupBy(word => word) .ToDictionary(group => group.Key, group => group.Count())); - } } \ No newline at end of file diff --git a/TagsCloudContainer/TextProviders/DocTextProvider.cs b/TagsCloudContainer/TextProviders/DocTextProvider.cs index d2cf9736..3b8744ff 100644 --- a/TagsCloudContainer/TextProviders/DocTextProvider.cs +++ b/TagsCloudContainer/TextProviders/DocTextProvider.cs @@ -17,6 +17,7 @@ public Result ReadFile() if (!File.Exists(_filePath)) return new Result($"File not found: {_filePath}"); using var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read); - return Result.Of(() => new HWPFDocument(stream)).Then(document=> document.GetRange().Text); + return Result.Of(() => new HWPFDocument(stream)) + .Then(document=> document.GetRange().Text); } } \ No newline at end of file From c6874fe0fe48762ce5d5cf83da1fcb1cefbfc7dc Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Tue, 21 Jan 2025 22:37:46 +0500 Subject: [PATCH 11/18] result to colorprovider --- TagsCloudContainer.Tests/TagGeneratorShould.cs | 2 +- .../ColorProviders/ColorProvider.cs | 5 ++--- .../ColorProviders/IColorProvider.cs | 2 +- .../ColorProviders/RandomColorProvider.cs | 2 +- TagsCloudContainer/PictureMaker.cs | 3 ++- .../TagGenerator/ITagsGenerator.cs | 2 +- TagsCloudContainer/TagGenerator/TagGenerator.cs | 16 ++++++++++++---- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/TagsCloudContainer.Tests/TagGeneratorShould.cs b/TagsCloudContainer.Tests/TagGeneratorShould.cs index 42913e40..318c4489 100644 --- a/TagsCloudContainer.Tests/TagGeneratorShould.cs +++ b/TagsCloudContainer.Tests/TagGeneratorShould.cs @@ -16,7 +16,7 @@ public void SetRightFontSize() {new Word("a"), 3} }; var generator = new TagGenerator.TagGenerator(new RandomColorProvider(), new System.Drawing.Font("arial", 12)); - var result = generator.GenerateTags(words).First(); + var result = generator.GenerateTags(words).GetValueOrThrow().First(); result.Font.Name.Should().Be("Arial"); result.Font.Size.Should().Be(36); diff --git a/TagsCloudContainer/ColorProviders/ColorProvider.cs b/TagsCloudContainer/ColorProviders/ColorProvider.cs index 07b3e61e..1df55747 100644 --- a/TagsCloudContainer/ColorProviders/ColorProvider.cs +++ b/TagsCloudContainer/ColorProviders/ColorProvider.cs @@ -1,13 +1,12 @@ using System.Drawing; -using System.Runtime.CompilerServices; namespace TagsCloudContainer.ColorProviders; public class ColorProvider : IColorProvider { - [CompilerGenerated] private readonly Color _color; + private readonly Color _color; public ColorProvider(Color color) => _color = color; - public Color GetColor() => _color; + public Result GetColor() => _color.AsResult(); } \ No newline at end of file diff --git a/TagsCloudContainer/ColorProviders/IColorProvider.cs b/TagsCloudContainer/ColorProviders/IColorProvider.cs index 682ceaa1..baef5936 100644 --- a/TagsCloudContainer/ColorProviders/IColorProvider.cs +++ b/TagsCloudContainer/ColorProviders/IColorProvider.cs @@ -4,5 +4,5 @@ namespace TagsCloudContainer.ColorProviders; public interface IColorProvider { - Color GetColor(); + Result GetColor(); } \ No newline at end of file diff --git a/TagsCloudContainer/ColorProviders/RandomColorProvider.cs b/TagsCloudContainer/ColorProviders/RandomColorProvider.cs index 2d186faa..32f25d78 100644 --- a/TagsCloudContainer/ColorProviders/RandomColorProvider.cs +++ b/TagsCloudContainer/ColorProviders/RandomColorProvider.cs @@ -4,7 +4,7 @@ namespace TagsCloudContainer.ColorProviders; public class RandomColorProvider : IColorProvider { - public Color GetColor() + public Result GetColor() { return Color.FromArgb(Random.Shared.Next(50, 255), Random.Shared.Next(0, 255), Random.Shared.Next(0, 255), Random.Shared.Next(0, 255)); } diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index 6963c2c7..25b41e99 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -32,7 +32,8 @@ public Result DrawPicture() .Then(wordsDict => { var tags = _tagGenerator.GenerateTags(wordsDict); - foreach (var tag in tags) + if (!tags.IsSuccess) return new Result(tags.Error); + foreach (var tag in tags.Value) { var rectangle = layout.PutNextRectangle(tag.Frame); if (!rectangle.IsSuccess) diff --git a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs index 421c4f02..049a173b 100644 --- a/TagsCloudContainer/TagGenerator/ITagsGenerator.cs +++ b/TagsCloudContainer/TagGenerator/ITagsGenerator.cs @@ -2,6 +2,6 @@ { public interface ITagsGenerator { - IEnumerable GenerateTags(Dictionary wordsDictionary); + Result> GenerateTags(Dictionary wordsDictionary); } } diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index dff3a334..f9406e16 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -17,11 +17,19 @@ public TagGenerator(IColorProvider colorProvider, Font defaultFont ) _defaultFont = defaultFont; } - public IEnumerable GenerateTags(Dictionary wordsDictionary) + public Result> GenerateTags(Dictionary wordsDictionary) { - return wordsDictionary - .Select(kvp => new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), _colorProvider.GetColor(), - SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); + var tagsResult = wordsDictionary + .Select(kvp => + { + var color = _colorProvider.GetColor(); + if (!color.IsSuccess) return Result.Fail(color.Error); + + return Result.Ok(new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), color.Value, + SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); + }); + return Result.Ok(tagsResult.Select(t => t.Value)) + .OnFail(error => Result.Fail(error)); } private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) From 03427f615f03af035a7a678fb49418a20f371aab Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Tue, 21 Jan 2025 23:04:38 +0500 Subject: [PATCH 12/18] result for SetFrameSize --- TagsCloudContainer/TagGenerator/TagGenerator.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index f9406e16..a194716f 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -22,10 +22,15 @@ public Result> GenerateTags(Dictionary wordsDictiona var tagsResult = wordsDictionary .Select(kvp => { - var color = _colorProvider.GetColor(); - if (!color.IsSuccess) return Result.Fail(color.Error); + var colorResult = _colorProvider.GetColor(); + if (!colorResult.IsSuccess) + return Result.Fail(colorResult.Error); + + var frameSizeResult = Result.Of(() => SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics)); + if (!frameSizeResult.IsSuccess) + return Result.Fail(frameSizeResult.Error); - return Result.Ok(new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), color.Value, + return Result.Ok(new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), colorResult.Value, SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); }); return Result.Ok(tagsResult.Select(t => t.Value)) @@ -34,6 +39,10 @@ public Result> GenerateTags(Dictionary wordsDictiona private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) { + ArgumentNullException.ThrowIfNull(word); + ArgumentNullException.ThrowIfNull(font); + ArgumentNullException.ThrowIfNull(graphics); + var rect = graphics.MeasureString(word.Value, font).ToSize(); return new Size(rect.Width + frameGap, rect.Height + frameGap); } From 827c66fbebac26b1508138a0c7e4ebcb74c7ad02 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Tue, 21 Jan 2025 23:11:25 +0500 Subject: [PATCH 13/18] taggenerator is almost perfect ) --- TagsCloudContainer/TagGenerator/TagGenerator.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index a194716f..72fd0077 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -26,7 +26,11 @@ public Result> GenerateTags(Dictionary wordsDictiona if (!colorResult.IsSuccess) return Result.Fail(colorResult.Error); - var frameSizeResult = Result.Of(() => SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics)); + var fontResult = Result.Of(() => SetFont(_defaultFont, kvp.Value)); + if (!fontResult.IsSuccess) + return Result.Fail(fontResult.Error); + + var frameSizeResult = Result.Of(() => SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics)); if (!frameSizeResult.IsSuccess) return Result.Fail(frameSizeResult.Error); @@ -49,6 +53,9 @@ private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics gr private static Font SetFont(Font font, int amount) { + ArgumentNullException.ThrowIfNull(font); + if (amount <= 0) throw new ArgumentException(null, nameof(amount)); + return new Font(font.FontFamily, font.Size * amount); } } From 9ae55ec2ce56faf52931ee17f36c67ee0af79df7 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Wed, 22 Jan 2025 00:01:58 +0500 Subject: [PATCH 14/18] taggenerator now better than almost perfect ) --- .../TagGenerator/TagGenerator.cs | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index 72fd0077..aaf07104 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -26,37 +26,41 @@ public Result> GenerateTags(Dictionary wordsDictiona if (!colorResult.IsSuccess) return Result.Fail(colorResult.Error); - var fontResult = Result.Of(() => SetFont(_defaultFont, kvp.Value)); + var fontResult = SetFont(_defaultFont, kvp.Value); if (!fontResult.IsSuccess) return Result.Fail(fontResult.Error); - var frameSizeResult = Result.Of(() => SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics)); + var frameSizeResult = SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics); if (!frameSizeResult.IsSuccess) return Result.Fail(frameSizeResult.Error); - return Result.Ok(new Tag(kvp.Key, SetFont(_defaultFont, kvp.Value), colorResult.Value, - SetFrameSize(kvp.Key, SetFont(_defaultFont, kvp.Value), 1, _graphics))); + return Result.Ok(new Tag(kvp.Key, fontResult.Value, colorResult.Value, + frameSizeResult.Value)); }); return Result.Ok(tagsResult.Select(t => t.Value)) .OnFail(error => Result.Fail(error)); } - private static Size SetFrameSize(Word word, Font font, int frameGap, Graphics graphics) + private static Result SetFrameSize(Word? word, Font? font, int frameGap, Graphics? graphics) { - ArgumentNullException.ThrowIfNull(word); - ArgumentNullException.ThrowIfNull(font); - ArgumentNullException.ThrowIfNull(graphics); + if (word == null) + return Result.Fail("Word is null"); + if (font == null) + return Result.Fail("Font is null"); + if (frameGap <= 0) + return Result.Fail("Frame gap is lesser than 0"); + if (graphics == null) + return Result.Fail("Graphics is null"); var rect = graphics.MeasureString(word.Value, font).ToSize(); - return new Size(rect.Width + frameGap, rect.Height + frameGap); + return Result.Ok(new Size(rect.Width + frameGap, rect.Height + frameGap)); } - private static Font SetFont(Font font, int amount) + private static Result SetFont(Font? font, int amount) { - ArgumentNullException.ThrowIfNull(font); - if (amount <= 0) throw new ArgumentException(null, nameof(amount)); - - return new Font(font.FontFamily, font.Size * amount); + if (amount <= 0) + return Result.Fail("Amount must be greater than 0"); + return font == null ? Result.Fail("Font is null") : new Font(font.FontFamily, font.Size * amount).AsResult(); } } } From c3d34a15f60c45a5e75d4ef3ad25467c9c8c1588 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Wed, 22 Jan 2025 22:38:55 +0500 Subject: [PATCH 15/18] result for IStringParser --- TagsCloudContainer.Tests/RegexParserShould.cs | 39 +++++++++++++++++++ .../StringParsers/IStringParser.cs | 2 +- .../StringParsers/RegexParser.cs | 8 ++-- 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 TagsCloudContainer.Tests/RegexParserShould.cs diff --git a/TagsCloudContainer.Tests/RegexParserShould.cs b/TagsCloudContainer.Tests/RegexParserShould.cs new file mode 100644 index 00000000..12e5433b --- /dev/null +++ b/TagsCloudContainer.Tests/RegexParserShould.cs @@ -0,0 +1,39 @@ +using FluentAssertions; +using TagsCloudContainer.StringParsers; + +namespace TagsCloudContainer.Tests; + +[TestFixture] +public class RegexParserShould +{ + private RegexParser _parser; + + [SetUp] + public void Setup() + { + _parser = new RegexParser(); + } + + [Test] + public void ReturnsOnlyWords() + { + var input = "This parser should -+- parse all #words, except any *symbols"; + + var result = _parser.GetWordsFromString(input); + var expected = new [] {"This", "parser", "should", "parse", "all", "words", "except", "any", "symbols"}; + + result.IsSuccess.Should().BeTrue(); + result.GetValueOrThrow().ToList().Select(w => w.Value).Should().BeEquivalentTo(expected); + } + + [Test] + public void ReturnsNoWordsIfEmptyInput() + { + var input = ""; + + var result = _parser.GetWordsFromString(input); + + result.Error.Should().Be("Input cannot be empty"); + } +} + diff --git a/TagsCloudContainer/StringParsers/IStringParser.cs b/TagsCloudContainer/StringParsers/IStringParser.cs index f85d252c..bf6645d6 100644 --- a/TagsCloudContainer/StringParsers/IStringParser.cs +++ b/TagsCloudContainer/StringParsers/IStringParser.cs @@ -2,6 +2,6 @@ { public interface IStringParser { - IEnumerable GetWordsFromString(string input); + Result> GetWordsFromString(string input); } } diff --git a/TagsCloudContainer/StringParsers/RegexParser.cs b/TagsCloudContainer/StringParsers/RegexParser.cs index 9887a74f..0252bb58 100644 --- a/TagsCloudContainer/StringParsers/RegexParser.cs +++ b/TagsCloudContainer/StringParsers/RegexParser.cs @@ -5,11 +5,13 @@ namespace TagsCloudContainer.StringParsers public class RegexParser : IStringParser { private readonly Regex _regex = new("\\b(?:\\w|-)+\\b", RegexOptions.Compiled); - public IEnumerable GetWordsFromString(string input) + public Result> GetWordsFromString(string input) { - return _regex.Matches(input) + if (string.IsNullOrWhiteSpace(input)) + return Result.Fail>("Input cannot be empty"); + return Result.Ok(_regex.Matches(input) .Cast() - .Select(w => new Word(w.Value)); + .Select(w => new Word(w.Value))); } } } From ae3ab558c0d00881116de21452b1fe3d143e6c40 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Thu, 23 Jan 2025 12:05:30 +0500 Subject: [PATCH 16/18] refactor PictureMaker with ForEach extension for result --- TagsCloudContainer/PictureMaker.cs | 24 ++++++++---------------- TagsCloudContainer/Result.cs | 8 ++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/TagsCloudContainer/PictureMaker.cs b/TagsCloudContainer/PictureMaker.cs index 25b41e99..34cf7620 100644 --- a/TagsCloudContainer/PictureMaker.cs +++ b/TagsCloudContainer/PictureMaker.cs @@ -27,24 +27,16 @@ public Result DrawPicture() { var layout = new CloudLayout(_startPoint, _pointGenerator); using var image = new Bitmap(layout.Size.Width, layout.Size.Height); - + return _textProcessor.WordFrequencies() - .Then(wordsDict => + .Then(wordsDict => _tagGenerator.GenerateTags(wordsDict)) + .ForEach(tag => { - var tags = _tagGenerator.GenerateTags(wordsDict); - if (!tags.IsSuccess) return new Result(tags.Error); - foreach (var tag in tags.Value) - { - var rectangle = layout.PutNextRectangle(tag.Frame); - if (!rectangle.IsSuccess) - return Result.Fail(rectangle.Error);; - DrawTag(image, rectangle.Value, tag); - } - image.Save(_fileName); - return Result.Ok(); - }) - .OnFail(error => Result.Fail(error)); - } + var rectange = layout.PutNextRectangle(tag.Frame) + .Then(rect => DrawTag(image, rect, tag)) + .Then(_ => image.Save(_fileName)); + }); + } private static void DrawTag(Bitmap image, Rectangle rectangle, Tag tag) { diff --git a/TagsCloudContainer/Result.cs b/TagsCloudContainer/Result.cs index 75f13960..da5b56b7 100644 --- a/TagsCloudContainer/Result.cs +++ b/TagsCloudContainer/Result.cs @@ -122,5 +122,13 @@ public static Result RefineError( { return input.ReplaceError(err => errorMessage + ". " + err); } + + public static Result ForEach(this Result> input, Action action) + { + if (!input.IsSuccess) return Result.Fail(input.Error); + foreach (var item in input.Value) + action(item); + return Result.Ok(); + } } From 364001fa2b1b2c4fe36a196d2e7f4b6355a9cbf7 Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Thu, 23 Jan 2025 12:57:17 +0500 Subject: [PATCH 17/18] refactor RegexParser --- TagsCloudContainer/StringParsers/RegexParser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/TagsCloudContainer/StringParsers/RegexParser.cs b/TagsCloudContainer/StringParsers/RegexParser.cs index 0252bb58..cfa20f22 100644 --- a/TagsCloudContainer/StringParsers/RegexParser.cs +++ b/TagsCloudContainer/StringParsers/RegexParser.cs @@ -10,7 +10,6 @@ public Result> GetWordsFromString(string input) if (string.IsNullOrWhiteSpace(input)) return Result.Fail>("Input cannot be empty"); return Result.Ok(_regex.Matches(input) - .Cast() .Select(w => new Word(w.Value))); } } From 011abf24c72a878276223e8712af87ac266ff6dd Mon Sep 17 00:00:00 2001 From: Dmitriy Bessarab Date: Thu, 23 Jan 2025 12:58:08 +0500 Subject: [PATCH 18/18] remake TagGenerator --- .../TagGenerator/TagGenerator.cs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/TagsCloudContainer/TagGenerator/TagGenerator.cs b/TagsCloudContainer/TagGenerator/TagGenerator.cs index aaf07104..fd8978ce 100644 --- a/TagsCloudContainer/TagGenerator/TagGenerator.cs +++ b/TagsCloudContainer/TagGenerator/TagGenerator.cs @@ -19,26 +19,25 @@ public TagGenerator(IColorProvider colorProvider, Font defaultFont ) public Result> GenerateTags(Dictionary wordsDictionary) { - var tagsResult = wordsDictionary - .Select(kvp => - { - var colorResult = _colorProvider.GetColor(); - if (!colorResult.IsSuccess) - return Result.Fail(colorResult.Error); - - var fontResult = SetFont(_defaultFont, kvp.Value); - if (!fontResult.IsSuccess) - return Result.Fail(fontResult.Error); - - var frameSizeResult = SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics); - if (!frameSizeResult.IsSuccess) - return Result.Fail(frameSizeResult.Error); + var tagsResults = new List>(); - return Result.Ok(new Tag(kvp.Key, fontResult.Value, colorResult.Value, - frameSizeResult.Value)); - }); - return Result.Ok(tagsResult.Select(t => t.Value)) - .OnFail(error => Result.Fail(error)); + foreach (var kvp in wordsDictionary) + { + var colorResult = _colorProvider.GetColor(); + if (!colorResult.IsSuccess) + return Result.Fail>(colorResult.Error); + + var fontResult = SetFont(_defaultFont, kvp.Value); + if (!fontResult.IsSuccess) + return Result.Fail>(fontResult.Error); + + var frameSizeResult = SetFrameSize(kvp.Key, fontResult.Value, 1, _graphics); + if (!frameSizeResult.IsSuccess) + return Result.Fail>(frameSizeResult.Error); + + tagsResults.Add(Result.Ok(new Tag(kvp.Key, fontResult.Value, colorResult.Value, frameSizeResult.Value))); + } + return Result.Ok(tagsResults.Select(t => t.Value)); } private static Result SetFrameSize(Word? word, Font? font, int frameGap, Graphics? graphics)