From 826a55423b48150ab3b4c9c92b7641bdc6f31068 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Thu, 8 Feb 2024 17:25:31 +0500 Subject: [PATCH 1/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B5=D0=B4=D1=8B=D0=B4=D1=83=D1=89=D1=83=D1=8E?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BB=D0=B0=D0=BA=D0=B0=20=D1=82=D0=B5=D0=B3=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Applications/ConsoleApplication.cs | 14 ++ TagCloudResult/Applications/IApplication.cs | 7 + TagCloudResult/Container.cs | 33 +++++ TagCloudResult/Drawer/Drawer.cs | 21 +++ TagCloudResult/Drawer/IDrawer.cs | 9 ++ TagCloudResult/Examples/1.png | Bin 0 -> 25606 bytes TagCloudResult/Examples/Custom_Image_Size.png | Bin 0 -> 18103 bytes TagCloudResult/Examples/DifferentColors.png | Bin 0 -> 24424 bytes TagCloudResult/Examples/DifferentFormat.jpeg | Bin 0 -> 63552 bytes TagCloudResult/Examples/Different_Font.png | Bin 0 -> 25100 bytes .../Examples/Reversed_Spiral_Scale.png | Bin 0 -> 25621 bytes TagCloudResult/Excluded.txt | 1 + TagCloudResult/Layouter/ArchimedeanSpiral.cs | 21 +++ .../Layouter/CircularCloudLayouter.cs | 81 ++++++++++++ TagCloudResult/Layouter/ILayouter.cs | 9 ++ TagCloudResult/Layouter/IPointGenerator.cs | 10 ++ .../Layouter/IRectanglesGenerator.cs | 7 + TagCloudResult/Layouter/RectangleData.cs | 18 +++ .../Layouter/RectanglesGenerator.cs | 30 +++++ TagCloudResult/Program.cs | 17 +++ TagCloudResult/Settings.cs | 52 ++++++++ TagCloudResult/TagCloudResult.csproj | 16 +++ TagCloudResult/Text.txt | 54 ++++++++ .../TextProcessing/FileTextReader.cs | 19 +++ .../TextProcessing/ITextProcessor.cs | 7 + TagCloudResult/TextProcessing/ITextReader.cs | 7 + .../TextProcessing/TextProcessor.cs | 14 ++ .../ArchimedianSpiral_Should.cs | 32 +++++ .../CircularCloudLayouter_Should.cs | 121 ++++++++++++++++++ .../TagCloudResult_Tests.csproj | 26 ++++ .../TestDataArchimedeanSpiral.cs | 26 ++++ .../TestDataCircularCloudLayouter.cs | 19 +++ TagCloudResult_Tests/TestDrawer.cs | 25 ++++ fp.sln | 12 ++ 34 files changed, 708 insertions(+) create mode 100644 TagCloudResult/Applications/ConsoleApplication.cs create mode 100644 TagCloudResult/Applications/IApplication.cs create mode 100644 TagCloudResult/Container.cs create mode 100644 TagCloudResult/Drawer/Drawer.cs create mode 100644 TagCloudResult/Drawer/IDrawer.cs create mode 100644 TagCloudResult/Examples/1.png create mode 100644 TagCloudResult/Examples/Custom_Image_Size.png create mode 100644 TagCloudResult/Examples/DifferentColors.png create mode 100644 TagCloudResult/Examples/DifferentFormat.jpeg create mode 100644 TagCloudResult/Examples/Different_Font.png create mode 100644 TagCloudResult/Examples/Reversed_Spiral_Scale.png create mode 100644 TagCloudResult/Excluded.txt create mode 100644 TagCloudResult/Layouter/ArchimedeanSpiral.cs create mode 100644 TagCloudResult/Layouter/CircularCloudLayouter.cs create mode 100644 TagCloudResult/Layouter/ILayouter.cs create mode 100644 TagCloudResult/Layouter/IPointGenerator.cs create mode 100644 TagCloudResult/Layouter/IRectanglesGenerator.cs create mode 100644 TagCloudResult/Layouter/RectangleData.cs create mode 100644 TagCloudResult/Layouter/RectanglesGenerator.cs create mode 100644 TagCloudResult/Program.cs create mode 100644 TagCloudResult/Settings.cs create mode 100644 TagCloudResult/TagCloudResult.csproj create mode 100644 TagCloudResult/Text.txt create mode 100644 TagCloudResult/TextProcessing/FileTextReader.cs create mode 100644 TagCloudResult/TextProcessing/ITextProcessor.cs create mode 100644 TagCloudResult/TextProcessing/ITextReader.cs create mode 100644 TagCloudResult/TextProcessing/TextProcessor.cs create mode 100644 TagCloudResult_Tests/ArchimedianSpiral_Should.cs create mode 100644 TagCloudResult_Tests/CircularCloudLayouter_Should.cs create mode 100644 TagCloudResult_Tests/TagCloudResult_Tests.csproj create mode 100644 TagCloudResult_Tests/TestDataArchimedeanSpiral.cs create mode 100644 TagCloudResult_Tests/TestDataCircularCloudLayouter.cs create mode 100644 TagCloudResult_Tests/TestDrawer.cs diff --git a/TagCloudResult/Applications/ConsoleApplication.cs b/TagCloudResult/Applications/ConsoleApplication.cs new file mode 100644 index 000000000..91a5641fc --- /dev/null +++ b/TagCloudResult/Applications/ConsoleApplication.cs @@ -0,0 +1,14 @@ +using TagCloudDi.Drawer; + +namespace TagCloudDi.Applications +{ + public class ConsoleApplication(IDrawer drawer, Settings settings) : IApplication + { + public void Run() + { + var image = drawer.GetImage(); + image.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); + Console.WriteLine($"Saved to {settings.SavePath + '.' + settings.ImageFormat.ToLower()}"); + } + } +} diff --git a/TagCloudResult/Applications/IApplication.cs b/TagCloudResult/Applications/IApplication.cs new file mode 100644 index 000000000..617aaace4 --- /dev/null +++ b/TagCloudResult/Applications/IApplication.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.Applications +{ + public interface IApplication + { + void Run(); + } +} diff --git a/TagCloudResult/Container.cs b/TagCloudResult/Container.cs new file mode 100644 index 000000000..228df5f48 --- /dev/null +++ b/TagCloudResult/Container.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using Autofac; +using TagCloudDi.Applications; +using TagCloudDi.Drawer; +using TagCloudDi.Layouter; +using TagCloudDi.TextProcessing; +using IContainer = Autofac.IContainer; + +namespace TagCloudDi +{ + public static class Container + { + public static IContainer SetupContainer(Settings settings) + { + var builder = new ContainerBuilder(); + builder.Register(c => settings); + builder.Register(c => + new ArchimedeanSpiral(new Point( + c.Resolve().ImageWidth / 2, + c.Resolve().ImageHeight / 2), + c.Resolve())) + .As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + return builder.Build(); + } + } +} diff --git a/TagCloudResult/Drawer/Drawer.cs b/TagCloudResult/Drawer/Drawer.cs new file mode 100644 index 000000000..d178c4553 --- /dev/null +++ b/TagCloudResult/Drawer/Drawer.cs @@ -0,0 +1,21 @@ +using System.Drawing; +using TagCloudDi.Layouter; + +namespace TagCloudDi.Drawer +{ + public class Drawer(Settings settings, IRectanglesGenerator rectanglesGenerator) : IDrawer + { + public Image GetImage() + { + var image = new Bitmap(settings.ImageWidth, settings.ImageHeight); + using var gr = Graphics.FromImage(image); + gr.Clear(Color.FromName(settings.BackColor)); + var brush = new SolidBrush(Color.FromName(settings.TextColor)); + foreach (var rectangleData in rectanglesGenerator.GetRectanglesData()) + using (var font = new Font(settings.FontName, rectangleData.fontSize, FontStyle.Regular)) + gr.DrawString(rectangleData.word, font, brush, rectangleData.rectangle); + + return image; + } + } +} diff --git a/TagCloudResult/Drawer/IDrawer.cs b/TagCloudResult/Drawer/IDrawer.cs new file mode 100644 index 000000000..b65f11b41 --- /dev/null +++ b/TagCloudResult/Drawer/IDrawer.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagCloudDi.Drawer +{ + public interface IDrawer + { + public Image GetImage(); + } +} diff --git a/TagCloudResult/Examples/1.png b/TagCloudResult/Examples/1.png new file mode 100644 index 0000000000000000000000000000000000000000..0a361e0ec0c0764ac17b54b60274eeaa0de14721 GIT binary patch literal 25606 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 Q!?SeSd*D~X9^a(@4dr*O5&!@I literal 0 HcmV?d00001 diff --git a/TagCloudResult/Examples/Custom_Image_Size.png b/TagCloudResult/Examples/Custom_Image_Size.png new file mode 100644 index 0000000000000000000000000000000000000000..bac453065ff8cbf20cef5821cdf35c9a2f0878b2 GIT binary patch literal 18103 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 a!oU>p!eU}!2r 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 WB-Z7iijfD4w=*}e#mdBI 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 literal 0 HcmV?d00001 diff --git a/TagCloudResult/Excluded.txt b/TagCloudResult/Excluded.txt new file mode 100644 index 000000000..101cc4699 --- /dev/null +++ b/TagCloudResult/Excluded.txt @@ -0,0 +1 @@ +Exclude \ No newline at end of file diff --git a/TagCloudResult/Layouter/ArchimedeanSpiral.cs b/TagCloudResult/Layouter/ArchimedeanSpiral.cs new file mode 100644 index 000000000..4588641d8 --- /dev/null +++ b/TagCloudResult/Layouter/ArchimedeanSpiral.cs @@ -0,0 +1,21 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class ArchimedeanSpiral(Point centerPoint, Settings settings) : IPointGenerator + { + private double angle { get; set; } + private const double DeltaAngle = Math.PI / 180; + private readonly int scale = settings.SpiralScale; + public Point CenterPoint => centerPoint; + + public Point GetNextPoint() + { + var newX = (int)(centerPoint.X + scale * angle * Math.Cos(angle)); + var newY = (int)(centerPoint.Y + scale * angle * Math.Sin(angle)); + angle += DeltaAngle; + + return new Point(newX, newY); + } + } +} diff --git a/TagCloudResult/Layouter/CircularCloudLayouter.cs b/TagCloudResult/Layouter/CircularCloudLayouter.cs new file mode 100644 index 000000000..9de36d861 --- /dev/null +++ b/TagCloudResult/Layouter/CircularCloudLayouter.cs @@ -0,0 +1,81 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class CircularCloudLayouter : ILayouter + { + private readonly IPointGenerator pointGenerator; + public readonly List Rectangles = []; + + public CircularCloudLayouter(IPointGenerator pointGenerator) + { + this.pointGenerator = pointGenerator; + } + + public Rectangle PutNextRectangle(Size rectangleSize) + { + if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) + throw new ArgumentException( + $"rectangleSize with zero or negative height or width is prohibited!", + nameof(rectangleSize) + ); + while (true) + { + var nextPoint = pointGenerator.GetNextPoint(); + var newPoint = new Point(nextPoint.X - rectangleSize.Width / 2, nextPoint.Y - rectangleSize.Height / 2); + var rectangle = new Rectangle(newPoint, rectangleSize); + if (IsIntersectsWithOthers(rectangle)) continue; + rectangle = GetCloserToCenterRectangle(rectangle); + Rectangles.Add(rectangle); + break; + } + + return Rectangles[^1]; + } + + private bool IsIntersectsWithOthers(Rectangle rectangle) => + Rectangles.Any(x => x.IntersectsWith(rectangle)); + + private Rectangle GetCloserToCenterRectangle(Rectangle rectangle) + { + var directions = GetDirection(rectangle); + foreach (var direction in directions) + { + var newRectangle = GetMovedRectangle(rectangle, direction.X, direction.Y); + while (!IsIntersectsWithOthers(newRectangle)) + { + if (pointGenerator.CenterPoint.X - newRectangle.Size.Width / 2 == newRectangle.X + || pointGenerator.CenterPoint.Y - newRectangle.Size.Height / 2 == newRectangle.Y) + break; + rectangle = newRectangle; + newRectangle = GetMovedRectangle(rectangle, direction.X, direction.Y); + } + } + + return rectangle; + } + + private List<(int X, int Y)> GetDirection(Rectangle rectangle) + { + var horizontalDiffer = pointGenerator.CenterPoint.X - rectangle.Size.Width / 2 - rectangle.X; + var verticalDiffer = pointGenerator.CenterPoint.Y - rectangle.Size.Height / 2 - rectangle.Y; + var directions = new List<(int X, int Y)>(); + if (horizontalDiffer != 0 && verticalDiffer != 0) + directions.Add((horizontalDiffer > 0 ? 1 : -1, verticalDiffer > 0 ? 1 : -1)); + if (horizontalDiffer != 0) + directions.Add((horizontalDiffer > 0 ? 1 : -1, 0)); + if (verticalDiffer != 0) + directions.Add((0, verticalDiffer > 0 ? 1 : -1)); + return directions; + } + + private static Rectangle GetMovedRectangle(Rectangle rectangle, int xDelta, int yDelta) => + new( + new Point( + rectangle.X + xDelta, + rectangle.Y + yDelta + ), + rectangle.Size + ); + } +} diff --git a/TagCloudResult/Layouter/ILayouter.cs b/TagCloudResult/Layouter/ILayouter.cs new file mode 100644 index 000000000..0f6dc92e1 --- /dev/null +++ b/TagCloudResult/Layouter/ILayouter.cs @@ -0,0 +1,9 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public interface ILayouter + { + public Rectangle PutNextRectangle(Size rectangleSize); + } +} diff --git a/TagCloudResult/Layouter/IPointGenerator.cs b/TagCloudResult/Layouter/IPointGenerator.cs new file mode 100644 index 000000000..01e76030e --- /dev/null +++ b/TagCloudResult/Layouter/IPointGenerator.cs @@ -0,0 +1,10 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public interface IPointGenerator + { + public Point GetNextPoint(); + public Point CenterPoint { get; } + } +} diff --git a/TagCloudResult/Layouter/IRectanglesGenerator.cs b/TagCloudResult/Layouter/IRectanglesGenerator.cs new file mode 100644 index 000000000..9ec7d0a7b --- /dev/null +++ b/TagCloudResult/Layouter/IRectanglesGenerator.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.Layouter +{ + public interface IRectanglesGenerator + { + public IEnumerable GetRectanglesData(); + } +} diff --git a/TagCloudResult/Layouter/RectangleData.cs b/TagCloudResult/Layouter/RectangleData.cs new file mode 100644 index 000000000..bb2017b70 --- /dev/null +++ b/TagCloudResult/Layouter/RectangleData.cs @@ -0,0 +1,18 @@ +using System.Drawing; + +namespace TagCloudDi.Layouter +{ + public class RectangleData + { + public readonly Rectangle rectangle; + public readonly string word; + public readonly float fontSize; + + public RectangleData(Rectangle rectangle, string word, float fontSize) + { + this.fontSize = fontSize; + this.word = word; + this.rectangle = rectangle; + } + } +} diff --git a/TagCloudResult/Layouter/RectanglesGenerator.cs b/TagCloudResult/Layouter/RectanglesGenerator.cs new file mode 100644 index 000000000..3d01e6600 --- /dev/null +++ b/TagCloudResult/Layouter/RectanglesGenerator.cs @@ -0,0 +1,30 @@ +using System.Drawing; +using TagCloudDi.TextProcessing; + +namespace TagCloudDi.Layouter +{ + public class RectanglesGenerator(ITextProcessor textProcessor, Settings settings, ILayouter layouter) : IRectanglesGenerator + { + public IEnumerable GetRectanglesData() + { + var frequencies = textProcessor.GetWordsFrequency(); + var totalAmount = frequencies.Sum(x => x.Value); + return frequencies + .OrderByDescending(x => x.Value) + .Select(x => + { + using var font = new Font(settings.FontName, settings.FontSize * ( + x.Value * 100 / totalAmount), FontStyle.Regular); + return new RectangleData(layouter.PutNextRectangle(GetTextSize(x.Key, font)), x.Key, font.Size); + }) + .ToList(); + } + + private Size GetTextSize(string text, Font font) + { + using var temporaryBitmap = new Bitmap(1, 1); + using var temporaryGraphics = Graphics.FromImage(temporaryBitmap); + return Size.Ceiling(temporaryGraphics.MeasureString(text, font)); + } + } +} diff --git a/TagCloudResult/Program.cs b/TagCloudResult/Program.cs new file mode 100644 index 000000000..dfe29ab7f --- /dev/null +++ b/TagCloudResult/Program.cs @@ -0,0 +1,17 @@ +using Autofac; +using CommandLine; +using TagCloudDi.Applications; + +namespace TagCloudDi +{ + abstract class MainClass + { + public static void Main(string[] args) + { + var settings = Parser.Default.ParseArguments(args).Value; + var container = Container.SetupContainer(settings); + var app = container.Resolve(); + app.Run(); + } + } +} diff --git a/TagCloudResult/Settings.cs b/TagCloudResult/Settings.cs new file mode 100644 index 000000000..eeaac6521 --- /dev/null +++ b/TagCloudResult/Settings.cs @@ -0,0 +1,52 @@ +using System.Drawing.Imaging; +using CommandLine; + +namespace TagCloudDi +{ + public class Settings + { + [Option('f', "FontName", Required = false, Default = "Arial")] + public string FontName { get; set; } + + [Option('z', "FontSize", Required = false, Default = 5)] + public int FontSize { get; set; } + + [Option('t', "TextPath", Required = true)] + public string TextPath { get; set; } + + [Option('e', "ExcludedWordsPath", Required = true)] + public string ExcludedWordsPath { get; set; } + + [Option('p', "SpiralScale", Required = false, Default = 1)] + public int SpiralScale { get; set; } + + [Option('y', "ImageHeight", Required = false, Default = 1080)] + public int ImageHeight { get; set; } + + [Option('x', "ImageWidth", Required = false, Default = 1920)] + public int ImageWidth { get; set; } + + [Option('s', "SavePathWithName", Required = true)] + public string SavePath { get; set; } + + [Option('c', "TextColor", Required = false, Default = "White")] + public string TextColor { get; set; } + + [Option('b', "BackgroundColor", Required = false, Default = "Black")] + public string BackColor { get; set; } + + [Option('i', "ImageFormat", Required = false, Default = "png")] + public string ImageFormat { get; set; } + + public ImageFormat GetFormat() => ImageFormat.ToLower() switch + { + "bmp" => System.Drawing.Imaging.ImageFormat.Bmp, + "gif" => System.Drawing.Imaging.ImageFormat.Gif, + "png" => System.Drawing.Imaging.ImageFormat.Png, + "tiff" => System.Drawing.Imaging.ImageFormat.Tiff, + "jpeg" => System.Drawing.Imaging.ImageFormat.Jpeg, + _ => throw new NotImplementedException() + }; + + } +} diff --git a/TagCloudResult/TagCloudResult.csproj b/TagCloudResult/TagCloudResult.csproj new file mode 100644 index 000000000..68bf1cad1 --- /dev/null +++ b/TagCloudResult/TagCloudResult.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/TagCloudResult/Text.txt b/TagCloudResult/Text.txt new file mode 100644 index 000000000..1d819d9e5 --- /dev/null +++ b/TagCloudResult/Text.txt @@ -0,0 +1,54 @@ +Some +Some +Some +Some +Some +Some +Exclude +Some +Words +Exclude +Words +Words +keyboard +keyboard +keyboard +keyboard +keyboard +keyboard +Words +Words +Words +Words +Words +Words +Words +Very +Interesting +Very +Interesting +Very +Interesting +Very +Interesting +Very +Horse +Horse +Horse +Horse +Mouse +Mouse +beer +beer +beer +airplane +airplane +airplane +road +road +road +train +train +gnome +gnome +gnome \ No newline at end of file diff --git a/TagCloudResult/TextProcessing/FileTextReader.cs b/TagCloudResult/TextProcessing/FileTextReader.cs new file mode 100644 index 000000000..7014079b9 --- /dev/null +++ b/TagCloudResult/TextProcessing/FileTextReader.cs @@ -0,0 +1,19 @@ +namespace TagCloudDi.TextProcessing +{ + public class FileTextReader : ITextReader + { + public IEnumerable GetWordsFrom(string filePath) + { + var words = new List(); + using var sr = new StreamReader(filePath); + var line = sr.ReadLine(); + while (line != null) + { + words.Add(line!.ToLower()); + line = sr.ReadLine(); + } + + return words; + } + } +} diff --git a/TagCloudResult/TextProcessing/ITextProcessor.cs b/TagCloudResult/TextProcessing/ITextProcessor.cs new file mode 100644 index 000000000..a3b754449 --- /dev/null +++ b/TagCloudResult/TextProcessing/ITextProcessor.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.TextProcessing +{ + public interface ITextProcessor + { + public Dictionary GetWordsFrequency(); + } +} diff --git a/TagCloudResult/TextProcessing/ITextReader.cs b/TagCloudResult/TextProcessing/ITextReader.cs new file mode 100644 index 000000000..0c39fab64 --- /dev/null +++ b/TagCloudResult/TextProcessing/ITextReader.cs @@ -0,0 +1,7 @@ +namespace TagCloudDi.TextProcessing +{ + public interface ITextReader + { + IEnumerable GetWordsFrom(string resource); + } +} diff --git a/TagCloudResult/TextProcessing/TextProcessor.cs b/TagCloudResult/TextProcessing/TextProcessor.cs new file mode 100644 index 000000000..11a57024b --- /dev/null +++ b/TagCloudResult/TextProcessing/TextProcessor.cs @@ -0,0 +1,14 @@ +namespace TagCloudDi.TextProcessing +{ + public class TextProcessor(Settings settings, ITextReader fileTextReader) : ITextProcessor + { + public Dictionary GetWordsFrequency() + { + var excludedWords = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath).ToHashSet(); + return fileTextReader.GetWordsFrom(settings.TextPath) + .Where(t => t.Length > 3 && !excludedWords.Contains(t)) + .GroupBy(x => x) + .ToDictionary(key => key.Key, amount => amount.Count()); + } + } +} diff --git a/TagCloudResult_Tests/ArchimedianSpiral_Should.cs b/TagCloudResult_Tests/ArchimedianSpiral_Should.cs new file mode 100644 index 000000000..d1f904055 --- /dev/null +++ b/TagCloudResult_Tests/ArchimedianSpiral_Should.cs @@ -0,0 +1,32 @@ +using System.Drawing; +using FluentAssertions; +using NUnit.Framework; +using TagCloudDi; +using TagCloudDi.Layouter; + +namespace TagCloudDi_Tests +{ + [TestFixture] + public class ArchimedeanSpiral_Should + { + private readonly Settings settings = new() { SpiralScale = 1 }; + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void ReturnCenterPoint_WhenFirstTime_GetNextPoint(Point point) + { + var spiral = new ArchimedeanSpiral(point, settings); + spiral.GetNextPoint().Should().BeEquivalentTo(point); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), + nameof(TestDataArchimedeanSpiral.DifferentIterationsAdded_ExpectedPoints))] + public void ReturnsCorrectPoint_When(int iterations, Point expectedPoint) + { + var spiral = new ArchimedeanSpiral(new Point(), settings); + for (var i = 0; i < iterations; i++) + spiral.GetNextPoint(); + + spiral.GetNextPoint().Should().BeEquivalentTo(expectedPoint); + } + } +} diff --git a/TagCloudResult_Tests/CircularCloudLayouter_Should.cs b/TagCloudResult_Tests/CircularCloudLayouter_Should.cs new file mode 100644 index 000000000..ec818399e --- /dev/null +++ b/TagCloudResult_Tests/CircularCloudLayouter_Should.cs @@ -0,0 +1,121 @@ +using System.Drawing; +using System.Reflection; +using FluentAssertions; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using TagCloudDi; +using TagCloudDi.Layouter; + +namespace TagCloudDi_Tests +{ + class CircularCloudLayouter_Should + { + private static CircularCloudLayouter? layouter; + private static readonly Settings settings = new() { SpiralScale = 1 }; + + private string imagePath = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\FailedLayout.png"; + + private IPointGenerator? generator; + + [TearDown] + public void TagCloudVisualizerCircularCloudLayouterTearDown() + { + if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed && layouter is not null) + { + var failImage = Test_Drawer.GetImage(new Size(1920, 1080), layouter.Rectangles); + failImage.Save(imagePath); + Console.WriteLine($"Tag cloud visualization saved to file <{imagePath}>"); + } + + layouter = null; + generator = null; + } + + [TestCaseSource(typeof(TestDataCircularCloudLayouter), + nameof(TestDataCircularCloudLayouter.ZeroOrLessHeightOrWidth_Size))] + public void Throw_WhenPutNewRectangle_WidthOrHeightLessEqualsZero(Size size) + { + generator = new ArchimedeanSpiral(new Point(), settings); + var action = new Action(() => new CircularCloudLayouter(generator).PutNextRectangle(size)); + action.Should().Throw() + .Which.Message.Should().Contain("zero or negative height or width"); + } + + [Test] + public void RectanglesEmpty_AfterCreation() + { + generator = new ArchimedeanSpiral(new Point(), settings); + layouter = new CircularCloudLayouter(generator); + layouter.Rectangles.Should().BeEmpty(); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void Add_FirstRectangle_ToCenter(Point center) + { + var generator = new ArchimedeanSpiral(center, settings); + layouter = new CircularCloudLayouter(generator); + layouter.PutNextRectangle(new Size(10, 2)); + layouter.Rectangles.Should().HaveCount(1) + .And.AllBeEquivalentTo(new Rectangle( + new Point(center.X - 10 / 2, center.Y - 2 / 2), new Size(10, 2))); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void AddSeveralRectangles_Correctly(Point centerPoint) + { + var amount = 25; + layouter = CreateLayouter_With_SeveralRectangles(amount, centerPoint); + layouter.Rectangles.Should().HaveCount(amount); + } + + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] + public void AddSeveralRectangles_DoNotIntersect(Point centerPoint) + { + layouter = CreateLayouter_With_SeveralRectangles(25, centerPoint); + var rectangles = layouter.Rectangles; + for (var i = 1; i < rectangles.Count; i++) + rectangles.Skip(i).All(x => !rectangles[i - 1].IntersectsWith(x)).Should().Be(true); + } + + [Test] + public void DensityTest() + { + var centerPoint = new Point(960, 540); + layouter = CreateLayouter_With_SeveralRectangles(4000, centerPoint); + var rectanglesSquare = 0; + var radius = 0; + foreach (var rectangle in layouter.Rectangles) + { + rectanglesSquare += rectangle.Width * rectangle.Height; + + var x = Math.Max( + Math.Abs(centerPoint.X - rectangle.X), + rectangle.X + rectangle.Width - centerPoint.X + ); + var y = Math.Max( + Math.Abs(centerPoint.Y - rectangle.Y), + rectangle.Y + rectangle.Height - centerPoint.Y + ); + radius = Math.Max(radius, (int)Math.Sqrt(x * x + y * y)); + } + + var circleSquare = Math.PI * radius * radius; + (rectanglesSquare / circleSquare).Should().BeGreaterOrEqualTo(0.75); + } + + private static CircularCloudLayouter CreateLayouter_With_SeveralRectangles(int amount, Point center) + { + var generator = new ArchimedeanSpiral(center, settings); + var newLayouter = new CircularCloudLayouter(generator); + + for (var i = amount; i > 0; i--) + { + var width = i % 40 + 10; + newLayouter.PutNextRectangle(new Size(width, width / 5)); + } + + return newLayouter; + } + } +} \ No newline at end of file diff --git a/TagCloudResult_Tests/TagCloudResult_Tests.csproj b/TagCloudResult_Tests/TagCloudResult_Tests.csproj new file mode 100644 index 000000000..8365b9ffb --- /dev/null +++ b/TagCloudResult_Tests/TagCloudResult_Tests.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + false + true + TagCloudResult + + + + + + + + + + + + + + + + diff --git a/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs b/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs new file mode 100644 index 000000000..fc7a84d2a --- /dev/null +++ b/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs @@ -0,0 +1,26 @@ +using System.Drawing; +using NUnit.Framework; + +namespace TagCloudDi_Tests +{ + public class TestDataArchimedeanSpiral + { + public static IEnumerable Different_CenterPoints() + { + yield return new TestCaseData(new Point(0, 0)).SetName("(0, 0) center"); + yield return new TestCaseData(new Point(343, 868)).SetName("(343, 868) center"); + yield return new TestCaseData(new Point(960, 540)).SetName("(960, 540) center"); + } + + public static IEnumerable DifferentIterationsAdded_ExpectedPoints() + { + yield return new TestCaseData(0, new Point(0, 0)).SetName("0 iterations, central point"); + yield return new TestCaseData(90, new Point(0, (int)(Math.PI / 2))).SetName("90 iterations, half PI"); + yield return new TestCaseData(180, new Point((int)(-Math.PI), 0)).SetName("180 iterations, PI"); + yield return new TestCaseData(270, new Point(0, (int)(-Math.PI * 3 / 2))).SetName("270 iterations, 3/2 PI"); + yield return new TestCaseData(360, new Point((int)(2 * Math.PI), 0)).SetName("360 iterations, double PI"); + yield return new TestCaseData(450, new Point(0, (int)(Math.PI * 5 / 2))).SetName("450 iterations, 5/2 PI"); + yield return new TestCaseData(540, new Point((int)(-3 * Math.PI), 0)).SetName("540 iterations, triple PI"); + } + } +} diff --git a/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs b/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs new file mode 100644 index 000000000..7b792ed4f --- /dev/null +++ b/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs @@ -0,0 +1,19 @@ +using System.Drawing; +using NUnit.Framework; + +namespace TagCloudDi_Tests +{ + public class TestDataCircularCloudLayouter + { + public static IEnumerable ZeroOrLessHeightOrWidth_Size() + { + yield return new TestCaseData(new Size(0, 1)).SetName("Zero_Width_Size"); + yield return new TestCaseData(new Size(1, 0)).SetName("Zero_Height_Size"); + yield return new TestCaseData(new Size(0, 0)).SetName("Zero_Width_And_Height_Size"); + yield return new TestCaseData(new Size(int.MinValue, 1)).SetName("Negative_Width_Size"); + yield return new TestCaseData(new Size(1, int.MinValue)).SetName("Negative_Height_Size"); + yield return new TestCaseData(new Size(int.MinValue, int.MinValue)) + .SetName("Negative_Width_And_Height_Size"); + } + } +} diff --git a/TagCloudResult_Tests/TestDrawer.cs b/TagCloudResult_Tests/TestDrawer.cs new file mode 100644 index 000000000..8990b2701 --- /dev/null +++ b/TagCloudResult_Tests/TestDrawer.cs @@ -0,0 +1,25 @@ +using System.Drawing; + +namespace TagCloudDi_Tests +{ + public class Test_Drawer + { + public static Image GetImage(Size size, IEnumerable rectangles) + { + if (size.Width <= 0 || size.Height <= 0) + throw new ArgumentException("size width and height should be positive", "size"); + var image = new Bitmap(size.Width, size.Height); + using (var gr = Graphics.FromImage(image)) + { + gr.Clear(Color.Black); + using (var brush = new SolidBrush(Color.White)) + { + foreach (var rectangle in rectangles) + gr.FillRectangle(brush, rectangle); + } + } + + return image; + } + } +} diff --git a/fp.sln b/fp.sln index a592ceee3..c09b6cb2e 100644 --- a/fp.sln +++ b/fp.sln @@ -10,6 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{754C EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Summator", "Summator\Summator.csproj", "{2E64584B-4FBC-4E7D-AEF2-003E8251307A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloudResult_Tests", "TagCloudResult_Tests\TagCloudResult_Tests.csproj", "{52FCFD75-6696-487D-852F-4231EC391EDD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagCloudResult", "TagCloudResult\TagCloudResult.csproj", "{282CBD74-4025-4966-A6FF-BE2BCABE5B75}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +36,14 @@ Global {2E64584B-4FBC-4E7D-AEF2-003E8251307A}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E64584B-4FBC-4E7D-AEF2-003E8251307A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E64584B-4FBC-4E7D-AEF2-003E8251307A}.Release|Any CPU.Build.0 = Release|Any CPU + {52FCFD75-6696-487D-852F-4231EC391EDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52FCFD75-6696-487D-852F-4231EC391EDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52FCFD75-6696-487D-852F-4231EC391EDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52FCFD75-6696-487D-852F-4231EC391EDD}.Release|Any CPU.Build.0 = Release|Any CPU + {282CBD74-4025-4966-A6FF-BE2BCABE5B75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {282CBD74-4025-4966-A6FF-BE2BCABE5B75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {282CBD74-4025-4966-A6FF-BE2BCABE5B75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {282CBD74-4025-4966-A6FF-BE2BCABE5B75}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {2E64584B-4FBC-4E7D-AEF2-003E8251307A} = {754C1CC8-A8B6-46C6-B35C-8A43B80111A0} From 2231b2f831f9a35169edf7e995ba509216b9a5c6 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Thu, 8 Feb 2024 17:30:25 +0500 Subject: [PATCH 2/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20Result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloudResult/Result.cs | 124 ++++++++++++++ TagCloudResult_Tests/Result_Should.cs | 161 ++++++++++++++++++ .../TagCloudResult_Tests.csproj | 1 + 3 files changed, 286 insertions(+) create mode 100644 TagCloudResult/Result.cs create mode 100644 TagCloudResult_Tests/Result_Should.cs diff --git a/TagCloudResult/Result.cs b/TagCloudResult/Result.cs new file mode 100644 index 000000000..78143b3e8 --- /dev/null +++ b/TagCloudResult/Result.cs @@ -0,0 +1,124 @@ +namespace TagCloudResult +{ + 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, + 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 diff --git a/TagCloudResult_Tests/Result_Should.cs b/TagCloudResult_Tests/Result_Should.cs new file mode 100644 index 000000000..d2cf16252 --- /dev/null +++ b/TagCloudResult_Tests/Result_Should.cs @@ -0,0 +1,161 @@ +using FakeItEasy; +using FluentAssertions; +using NUnit.Framework; + +namespace TagCloudResult +{ + [TestFixture] + public class Result_Should + { + [Test] + public void Create_Ok() + { + var r = Result.Ok(42); + r.IsSuccess.Should().BeTrue(); + r.GetValueOrThrow().Should().Be(42); + } + + [Test] + public void Create_Fail() + { + var r = Result.Fail("123"); + + r.IsSuccess.Should().BeFalse(); + r.Error.Should().Be("123"); + } + + [Test] + public void ReturnsFail_FromResultOf_OnException() + { + var res = Result.Of(() => { throw new Exception("123"); }); + + res.Should().BeEquivalentTo(Result.Fail("123")); + } + + [Test] + public void ReturnsFailWithCustomMessage_FromResultOf_OnException() + { + var res = Result.Of(() => { throw new Exception("123"); }, "42"); + + res.Should().BeEquivalentTo(Result.Fail("42")); + } + + [Test] + public void ReturnsOk_FromResultOf_WhenNoException() + { + var res = Result.Of(() => 42); + + res.Should().BeEquivalentTo(Result.Ok(42)); + } + + [Test] + public void RunThen_WhenOk() + { + var res = Result.Ok(42) + .Then(n => n + 10); + res.Should().BeEquivalentTo(Result.Ok(52)); + } + + [Test] + public void SkipThen_WhenFail() + { + var fail = Result.Fail("������"); + var called = false; + fail.Then(n => + { + called = true; + return n; + }); + called.Should().BeFalse(); + } + + [Test] + public void Then_ReturnsFail_OnException() + { + Func continuation = n => + { + throw new Exception("123"); + }; + var res = Result.Ok(42) + .Then(continuation); + res.Should().BeEquivalentTo(Result.Fail("123")); + } + + [Test] + public void RunOnFail_WhenFail() + { + var fail = Result.Fail("������"); + var errorHandler = A.Fake>(); + + var res = fail.OnFail(errorHandler); + + A.CallTo(() => errorHandler(null)).WithAnyArguments().MustHaveHappened(); + res.Should().BeEquivalentTo(fail); + } + + [Test] + public void SkipOnFail_WhenOk() + { + var ok = Result.Ok(42); + + var res = ok.OnFail(v => { Assert.Fail("Should not be called"); }); + + res.Should().BeEquivalentTo(ok); + } + + [Test] + public void RunThen_WhenOk_Scenario() + { + var res = + Result.Ok("1358571172") + .Then(int.Parse) + .Then(i => Convert.ToString(i, 16)) + .Then(hex => Guid.Parse(hex + hex + hex + hex)); + res.Should().BeEquivalentTo(Result.Ok(Guid.Parse("50FA26A450FA26A450FA26A450FA26A4"))); + } + + [Test] + public void RunThen_WhenOk_ComplexScenario() + { + var parsed = Result.Ok("1358571172").Then(int.Parse); + var res = parsed + .Then(i => Convert.ToString(i, 16)) + .Then(hex => parsed.GetValueOrThrow() + " -> " + Guid.Parse(hex + hex + hex + hex)); + res.Should().BeEquivalentTo(Result.Ok("1358571172 -> 50fa26a4-50fa-26a4-50fa-26a450fa26a4")); + } + + [Test] + public void ReplaceError_IfFail() + { + Result.Fail("error") + .ReplaceError(e => "replaced") + .Should().BeEquivalentTo(Result.Fail("replaced")); + } + + [Test] + public void ReplaceError_DoNothing_IfSuccess() + { + Result.Ok(42) + .ReplaceError(e => "replaced") + .Should().BeEquivalentTo(Result.Ok(42)); + } + + [Test] + public void ReplaceError_DontReplace_IfCalledBeforeError() + { + Result.Ok(42) + .ReplaceError(e => "replaced") + .Then(n => Result.Fail("error")) + .Should().BeEquivalentTo(Result.Fail("error")); + } + + [Test] + public void RefineError_AddErrorMessageBeforePreviousErrorText() + { + var calculation = Result.Fail("No connection"); + calculation + .RefineError("Posting results to db") + .Should().BeEquivalentTo(Result.Fail("Posting results to db. No connection")); + } + } +} \ No newline at end of file diff --git a/TagCloudResult_Tests/TagCloudResult_Tests.csproj b/TagCloudResult_Tests/TagCloudResult_Tests.csproj index 8365b9ffb..a7e942a9a 100644 --- a/TagCloudResult_Tests/TagCloudResult_Tests.csproj +++ b/TagCloudResult_Tests/TagCloudResult_Tests.csproj @@ -11,6 +11,7 @@ + From 4d9c535807e9aee6aa67ac6f3f99ba396733a099 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Thu, 8 Feb 2024 19:06:57 +0500 Subject: [PATCH 3/6] =?UTF-8?q?=D0=98=D0=BD=D1=82=D0=B5=D0=B3=D1=80=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20Result,=20=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BB=20=D0=B4=D0=BE=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D0=BD=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B8=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BD=D0=B5=D0=B9=D0=BC?= =?UTF-8?q?=D1=81=D0=BF=D0=B5=D0=B9=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Applications/ConsoleApplication.cs | 14 ++++-- TagCloudResult/Applications/IApplication.cs | 2 +- TagCloudResult/Container.cs | 10 ++-- TagCloudResult/Drawer/Drawer.cs | 32 ++++++++++--- TagCloudResult/Drawer/IDrawer.cs | 4 +- TagCloudResult/Examples/Save.png | Bin 0 -> 24969 bytes TagCloudResult/Layouter/ArchimedeanSpiral.cs | 2 +- .../Layouter/CircularCloudLayouter.cs | 13 ++---- TagCloudResult/Layouter/ILayouter.cs | 4 +- TagCloudResult/Layouter/IPointGenerator.cs | 2 +- .../Layouter/IRectanglesGenerator.cs | 4 +- TagCloudResult/Layouter/RectangleData.cs | 2 +- .../Layouter/RectanglesGenerator.cs | 44 ++++++++++++------ TagCloudResult/Program.cs | 4 +- TagCloudResult/Result.cs | 2 +- TagCloudResult/Settings.cs | 11 ++--- .../TextProcessing/FileTextReader.cs | 10 ++-- .../TextProcessing/ITextProcessor.cs | 4 +- TagCloudResult/TextProcessing/ITextReader.cs | 4 +- .../TextProcessing/TextProcessor.cs | 20 ++++++-- .../ArchimedianSpiral_Should.cs | 7 ++- .../CircularCloudLayouter_Should.cs | 17 ++++--- TagCloudResult_Tests/Result_Should.cs | 7 +-- .../TagCloudResult_Tests.csproj | 2 +- .../TestDataArchimedeanSpiral.cs | 2 +- .../TestDataCircularCloudLayouter.cs | 2 +- TagCloudResult_Tests/TestDrawer.cs | 4 +- TagCloudResult_Tests/TextProcessor_Should.cs | 39 ++++++++++++++++ 28 files changed, 176 insertions(+), 92 deletions(-) create mode 100644 TagCloudResult/Examples/Save.png create mode 100644 TagCloudResult_Tests/TextProcessor_Should.cs diff --git a/TagCloudResult/Applications/ConsoleApplication.cs b/TagCloudResult/Applications/ConsoleApplication.cs index 91a5641fc..900ec116d 100644 --- a/TagCloudResult/Applications/ConsoleApplication.cs +++ b/TagCloudResult/Applications/ConsoleApplication.cs @@ -1,13 +1,19 @@ -using TagCloudDi.Drawer; +using TagCloudResult.Drawer; -namespace TagCloudDi.Applications +namespace TagCloudResult.Applications { public class ConsoleApplication(IDrawer drawer, Settings settings) : IApplication { public void Run() { - var image = drawer.GetImage(); - image.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); + var imageResult = drawer.GetImage(); + if (!imageResult.IsSuccess) + { + Console.WriteLine(imageResult.Error); + return; + } + + imageResult.Value.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); Console.WriteLine($"Saved to {settings.SavePath + '.' + settings.ImageFormat.ToLower()}"); } } diff --git a/TagCloudResult/Applications/IApplication.cs b/TagCloudResult/Applications/IApplication.cs index 617aaace4..5d3c414a7 100644 --- a/TagCloudResult/Applications/IApplication.cs +++ b/TagCloudResult/Applications/IApplication.cs @@ -1,4 +1,4 @@ -namespace TagCloudDi.Applications +namespace TagCloudResult.Applications { public interface IApplication { diff --git a/TagCloudResult/Container.cs b/TagCloudResult/Container.cs index 228df5f48..e1836355e 100644 --- a/TagCloudResult/Container.cs +++ b/TagCloudResult/Container.cs @@ -1,12 +1,12 @@ using System.Drawing; using Autofac; -using TagCloudDi.Applications; -using TagCloudDi.Drawer; -using TagCloudDi.Layouter; -using TagCloudDi.TextProcessing; +using TagCloudResult.Applications; +using TagCloudResult.Drawer; +using TagCloudResult.Layouter; +using TagCloudResult.TextProcessing; using IContainer = Autofac.IContainer; -namespace TagCloudDi +namespace TagCloudResult { public static class Container { diff --git a/TagCloudResult/Drawer/Drawer.cs b/TagCloudResult/Drawer/Drawer.cs index d178c4553..9047417fa 100644 --- a/TagCloudResult/Drawer/Drawer.cs +++ b/TagCloudResult/Drawer/Drawer.cs @@ -1,17 +1,37 @@ using System.Drawing; -using TagCloudDi.Layouter; +using TagCloudResult.Layouter; -namespace TagCloudDi.Drawer +namespace TagCloudResult.Drawer { public class Drawer(Settings settings, IRectanglesGenerator rectanglesGenerator) : IDrawer { - public Image GetImage() + private Result GetColorFromName(string name) { + var colorResult = Result.Of(() => Color.FromName(name)); + return !colorResult.IsSuccess + ? Result.Fail($"No such color") + : Result.Ok(colorResult.Value); + } + + public Result GetImage() + { + var backColorResult = GetColorFromName(settings.BackColor) + .RefineError("Wrong background color"); + if (!backColorResult.IsSuccess) + return Result.Fail(backColorResult.Error); + var colorResult = GetColorFromName(settings.TextColor) + .RefineError("Wrong text color"); + if (!colorResult.IsSuccess) + return Result.Fail(colorResult.Error); + var rectanglesDataResult = rectanglesGenerator.GetRectanglesData(); + if (!rectanglesDataResult.IsSuccess) + return Result.Fail(rectanglesDataResult.Error); + var image = new Bitmap(settings.ImageWidth, settings.ImageHeight); using var gr = Graphics.FromImage(image); - gr.Clear(Color.FromName(settings.BackColor)); - var brush = new SolidBrush(Color.FromName(settings.TextColor)); - foreach (var rectangleData in rectanglesGenerator.GetRectanglesData()) + gr.Clear(backColorResult.Value); + var brush = new SolidBrush(colorResult.Value); + foreach (var rectangleData in rectanglesDataResult.Value) using (var font = new Font(settings.FontName, rectangleData.fontSize, FontStyle.Regular)) gr.DrawString(rectangleData.word, font, brush, rectangleData.rectangle); diff --git a/TagCloudResult/Drawer/IDrawer.cs b/TagCloudResult/Drawer/IDrawer.cs index b65f11b41..c7466a8c4 100644 --- a/TagCloudResult/Drawer/IDrawer.cs +++ b/TagCloudResult/Drawer/IDrawer.cs @@ -1,9 +1,9 @@ using System.Drawing; -namespace TagCloudDi.Drawer +namespace TagCloudResult.Drawer { public interface IDrawer { - public Image GetImage(); + public Result GetImage(); } } diff --git a/TagCloudResult/Examples/Save.png b/TagCloudResult/Examples/Save.png new file mode 100644 index 0000000000000000000000000000000000000000..d485a4b65f60d72dd96bfd4b1714bdeff856275b GIT binary patch literal 24969 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≶g#!^7nEm*;p-$ETiZN($PB`AT4a? z$*2m1s$cjgPKzr_cFbSJzR2H7)N4E@qKkv+f|-t? zgpDhq6}{TVrI*W{H$mYE@^d=vmV1Q*Q&_pA6glW%z_KkDGvDT*M6CjwxP%X#T78~A z`}HiX&C^RK;!)BU%~p9$8qA=_R#x8}wH1|#bUsZ%kILO!%CV~gba;b{%tyap80Ran zy4dWceH62?;yRk!7)YzX|0C}MQpx&mr`{I_1lFg#r8}3JCLVnv2sE5G(={}S6YCia z=RbWEj3RRj#%N11vu(K!n^CLG!llWlM8x4lX}FICsVgcYAxKiQKl6cL0kC^3xUef@MP;^KnM^2ozAHz+(R*0X%wL0^#B9RCr#Sn3iO!!b2J>i1!8ZydHZ z{O!hXxt942p`Fw;|8w~bsAP8?IVP=ZvNzMAATv&@-zR3Pi$UtWth>?b^^)Si-HB4? zu4odQn9yzPtWOezVXiq>zmxv>EjUTmw>*hh>n1phvcKX~sD}<=Hs1P3>qS0FNw!%- z4cZ^3<@;SkXz_>~izp;!uX{v4FWhQwzQNfS&CHpS{TLRd&1hrXOqLMo5@9cCKj@ zEbi@qPj}+JTlNRZy4SKSJ)Xl|jBnD8*6DH@5BbpPW zzbD~xmg{fW;LE}FD*L=kJjbh>%g|hk=0Mn+O&F%8fdU9tXzOwsP>?5kN?%|%-Ob+$ zF;Pl{mgc=PQejWtf&uk`8#(6P0-d>T=5!!Qt=n}%N!h(-R9wY>yX}0lTFpvP6kp72 zZAVJf(DG;-WLfBEZ10l4S}s<}vVzT&o7YuwS|82IRhX2fj%*E{ef9BnneD`hm?i~q zD6r2|@JRhqoe8m~gTWFuH?uJd%Cf#-w=;LSI30pCTCxrBt49a|1u|BS-1Dk8dnU~{ zx?{4Br2T$X>z@skBy7x@li_pgR)phw`KZ+%+QO4LmV$77Y~)C1=**iqo1$Ih(k~@6 zk!AJqVr>=GW>dm!@3MN>mo`Vl$-3_- zN{m5gm_3}!R}`MkncOC;wTa&>C}Qn8M;0B@0|s-AX@W&d2%~A{T35N(o~hCxDpNb=c!60dYPBQ zkOKqt@b*l+t@aACiS3(3!dclz{i=5{8QS6aT`!7DH8Y029AB#{SljuYPpOS9ZrVZL z*#eF-uD#$c%QDHBIL}zO<~At^`^VyAQLh7Q-|u`?`6 zPq5O%+!KJDF>A!BPQOMB0@a_o9#vSAKYuL|*n_6EmX1hEFy6N;uhw1)M zH@8S^zOZJ*yLMmU$r`r0JGUqkmE!Em-f#}EgS_S7xw9F0?~bM&F&#}O z7(ZbSHcWkNEfXg2QEp5d&+=!FgyV#}gk3+Ek4B#;^9BSkuI(q4z_oGVctao3)p5n>L$U<$8noD#Z{ zgkW6A1kP``vmN+a%*v49%vE6wH@nT4ACA#J-5eS7Kc{>aFu~`piqC3ek;qTzw(Q21U8t33~=HaigH$=(?c&eTL_-d*S& zAgq>&pc&PjUFeBMdw?wivJ1RF_Btdqb-c=okZARKx-Mv;J1Bx4->eQIe^|8mDA9YQ zCb(BKzr2iKJ$JlQZ@8-cAdp!Dx$SwbFVb~ti)QWjbD1^dM&l1&N6(1LOudt%W7<`F zlier+r9X)tb-t{w{wBNR^elLpNzoBK)0DscmIgzv-vlrsTcY_l} znLb&~NgD0pU|q*bMHXeMp{sbVRtAtK*mfLeY@+U0Bfg&g-UffR*frT z-MWQ>4;?B1OeS*d;#d!F^LU~8%r|c`Q%rd|HMpm{8fU)w*M5`|4wE+KEz8X!uk zp_c{gj?BhPE~H*<@$w6h3`xJxMaJ#>4sYtl_|>@m-#}W&C~8pq2CUXiLQ}f?#XiDe z$=h9dPJIlD^cq61mZexrP}A+h@6u>od+CtXQD{)brR?%K@XVWT=^k-kIjF=M51kn6 zvIb)Xn&n4774?YD>jU!1Wg^pBArKaumkoHK^ydq*p!k?1!{By3sGF+BC_i)6N*L!g ztm#!ROlQnxd z7Qo2MKS|_#PXC>#!mXt@Yp^=_rde>Z_NUrly{N|XP-9|>r>r2HyAMLwrxlX>F!#fx^>MUT(G-YgL*yXNJ74vUnpL-BM}<+eLoMm)mn?+f$>By;Y)o*8 zKd0-u#umshy?n~&{ms67Xg*Q80(Q`UAr7jh=NCj#`83e{2k+T1%OQfUaiXkqc0h(V zV5t?u^S@g}x0!6J90CtDW%GpR%O#=i@Z$C_MB<}vZdIBapX#S$rgl(1&1ahL(ePSV z!1UCzZ91<~p$QQ!(esseu`yW-7wBQJryt_n22u0%kXrEk{ z#FjR&Z|kA_G#uww)m(kNRxTw6if?cUf6Q+1vsv~xVV4FoyM)e|3TbG z8I;s;g<5>oRuG#!M4_MX_s(*u)xpD|y{iZKta^Pf-E)d}9N7*~V-TOQRLT&t6Bp_6 znVMxP4;h0EQ#&TRbZX7}rX-VAQB_@ZaYJ2sF#A(%01{m4$dGPpo}DEbowuqDWw<#W zBdT4k2?EEvOEx|%pC&=`Cp&amnUkt7_Ib0Ngm1&LvZ`+ghk6!ndy%IwBa1fr%!1yj zef^}D3Hc=HqgnXb4c4;3R~+>SO8+A?*&+*9bVij|FrXT-?@nH3N%;@nx7?v6`(9lZ zWuRewE#F+YIyVjv4ncAk^5NkK!t=e8X0=9=XZd%kX|2H4iZhSMXuCZj@uK(<2T(?7=jBOxBo`Xa-wy z;f13#`3`*Z5&Z*PCyu!&rCXQmC=Eq7E%<~g$Gx)1Z+Wb@b&S5TuRO#aN-A7GCoRho zO_WY0-Asg*if1Y}b$d_M%+Dv))^Xpn+fa&2by~CvB#&Kz%RcmqP{Elxjyd!K^?I;DK`n>g zjpnu63Y9gb=9b&1=Qg8ccx!`6+vr7W>ukWcr;y(hi;j5r`@jY_SIs9}Z#i2TUWy(& zBFclW1pM`9IBy)ZZ)$m)fA1__EMk~dH>yB!Zv)yEcXGgfC&WQQBfZ!%MfjK;{-Cej zjsza8yZ+qdvLN-Db1W7J!b8rjfws`spOt}8fd0Sv%_%qVV+?#j%E@q2%4OO!O^ykW z1bMs^=Stbu2{z>F>_IrBNuNiZYxoH+8vlIjtllX;tO_3dS^CR?2KMye(WH2a&3;XXd6;_gmI8`4wwFbLbU>SS>dEU2jvR!2A z+;L%KF2?=BJ4)c3*-1wk?zWX#{a%3sw}iE>>8xyG6Ig6oA=pz~sx9n&DfXpC>NfXe3a??N2Ys zK`FUwvsp7j5jShi^QZ(tO4#Gt_4CJNA}FGe`O)S;S9fEazgUr{=s-g#6n_0K5$ zvDV-?1#)VreshY>3r}-}8#7CBmYMw&jiBzPl(Uk#&7^Y`3-^kv`h6y{jBEoMG$DJ3mA-h|tX@|b!{+kx0XFEg)3ADMxTCFsGzf+#wDy*k>$A3St- zYSnpZJ(Ji*=1)Vl!eljBJ|Wn+3X5+C`_2cCBVH8bY(!HxNK<_@Vcf8Z8WCxpaPn0A zRLbvj_0elrKV#~M>a0#TcQ$QaLg%SNLK208cCS;5YnOPyK8ovJHjc%`x_neQ7 z+rV+~W7W^Z-wwx!x}TYeZpprtYk6rR$KnY4yR+VDab!=!wc}to!iY+?J@Yz4{a|W5 zr3Qs3fB5)!bZ(?nkauZTFl(K+U+>If`2ub_*K)}7mkpp?BYOHQ{ROCjw zKuN*@bQYm*L?f@Rpo>264JzC2t#&;{SNJ~f(?3WFmpch?W40Y@M#&v&O z?i%^yCo*`@zpl8{s2O=$NCU~(r*=Wzdt=0B=8iXNRY-23X|4HD5o0M~ zk=T%ey$zW=706aC^odwPC#WT4V_3jn+kCT@;ROS7(XiV?+_8W8hK9&g!IQNZU}P% z$L7=4;_Psqt6p(4#8vWS(DTmco_aS$&U?L%To?pr;Nk*Vwrti_u`>M}vt2YI%Fe_& zRfrzjNZ)vhi(V(;2KrPR{(^n-v# zidxI3^Z%2$0Y=2^>jde((0guzK%rS}EJ!veaGxVce4SnpL>o8bRyhZ^U!N3KOz@Gd zqvd;>rHOsMVP<46hCtk0?%EOlfn%)@moY%Wz8XGzvNq{>-A|Ong5*H!<37Xl-Z;ZA zoSQ%RY;byK;wG&NOO#JLQ}FkOx}z%Za#L37$*}WBK!I@9gnT;li(ZzD2SQ#$fSNeu%md zRt(=>sx0WxUxlT9@g4f-E@0KqhD-$pb z{Q5c+jKWu}U=+SO1A@X=XZY$2U$ZFU1OM&IgbvsGN{-UWgr6gjdU*Ipryt4>`Cj}V D_GIrf literal 0 HcmV?d00001 diff --git a/TagCloudResult/Layouter/ArchimedeanSpiral.cs b/TagCloudResult/Layouter/ArchimedeanSpiral.cs index 4588641d8..c23db8782 100644 --- a/TagCloudResult/Layouter/ArchimedeanSpiral.cs +++ b/TagCloudResult/Layouter/ArchimedeanSpiral.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public class ArchimedeanSpiral(Point centerPoint, Settings settings) : IPointGenerator { diff --git a/TagCloudResult/Layouter/CircularCloudLayouter.cs b/TagCloudResult/Layouter/CircularCloudLayouter.cs index 9de36d861..4ba939d25 100644 --- a/TagCloudResult/Layouter/CircularCloudLayouter.cs +++ b/TagCloudResult/Layouter/CircularCloudLayouter.cs @@ -1,24 +1,21 @@ using System.Drawing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public class CircularCloudLayouter : ILayouter { private readonly IPointGenerator pointGenerator; - public readonly List Rectangles = []; + public List Rectangles { get; } = []; public CircularCloudLayouter(IPointGenerator pointGenerator) { this.pointGenerator = pointGenerator; } - public Rectangle PutNextRectangle(Size rectangleSize) + public Result PutNextRectangle(Size rectangleSize) { if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) - throw new ArgumentException( - $"rectangleSize with zero or negative height or width is prohibited!", - nameof(rectangleSize) - ); + return Result.Fail($"rectangleSize with zero or negative height or width is prohibited!"); while (true) { var nextPoint = pointGenerator.GetNextPoint(); @@ -30,7 +27,7 @@ public Rectangle PutNextRectangle(Size rectangleSize) break; } - return Rectangles[^1]; + return Result.Ok(Rectangles[^1]); } private bool IsIntersectsWithOthers(Rectangle rectangle) => diff --git a/TagCloudResult/Layouter/ILayouter.cs b/TagCloudResult/Layouter/ILayouter.cs index 0f6dc92e1..c39476212 100644 --- a/TagCloudResult/Layouter/ILayouter.cs +++ b/TagCloudResult/Layouter/ILayouter.cs @@ -1,9 +1,9 @@ using System.Drawing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public interface ILayouter { - public Rectangle PutNextRectangle(Size rectangleSize); + public Result PutNextRectangle(Size rectangleSize); } } diff --git a/TagCloudResult/Layouter/IPointGenerator.cs b/TagCloudResult/Layouter/IPointGenerator.cs index 01e76030e..6ec1e134b 100644 --- a/TagCloudResult/Layouter/IPointGenerator.cs +++ b/TagCloudResult/Layouter/IPointGenerator.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public interface IPointGenerator { diff --git a/TagCloudResult/Layouter/IRectanglesGenerator.cs b/TagCloudResult/Layouter/IRectanglesGenerator.cs index 9ec7d0a7b..7e8148faa 100644 --- a/TagCloudResult/Layouter/IRectanglesGenerator.cs +++ b/TagCloudResult/Layouter/IRectanglesGenerator.cs @@ -1,7 +1,7 @@ -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public interface IRectanglesGenerator { - public IEnumerable GetRectanglesData(); + public Result> GetRectanglesData(); } } diff --git a/TagCloudResult/Layouter/RectangleData.cs b/TagCloudResult/Layouter/RectangleData.cs index bb2017b70..73efd6daa 100644 --- a/TagCloudResult/Layouter/RectangleData.cs +++ b/TagCloudResult/Layouter/RectangleData.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { public class RectangleData { diff --git a/TagCloudResult/Layouter/RectanglesGenerator.cs b/TagCloudResult/Layouter/RectanglesGenerator.cs index 3d01e6600..30cf2f359 100644 --- a/TagCloudResult/Layouter/RectanglesGenerator.cs +++ b/TagCloudResult/Layouter/RectanglesGenerator.cs @@ -1,26 +1,40 @@ using System.Drawing; -using TagCloudDi.TextProcessing; +using TagCloudResult.TextProcessing; -namespace TagCloudDi.Layouter +namespace TagCloudResult.Layouter { - public class RectanglesGenerator(ITextProcessor textProcessor, Settings settings, ILayouter layouter) : IRectanglesGenerator + public class RectanglesGenerator(ITextProcessor textProcessor, Settings settings, ILayouter layouter) + : IRectanglesGenerator { - public IEnumerable GetRectanglesData() + public Result> GetRectanglesData() { var frequencies = textProcessor.GetWordsFrequency(); - var totalAmount = frequencies.Sum(x => x.Value); - return frequencies - .OrderByDescending(x => x.Value) - .Select(x => - { - using var font = new Font(settings.FontName, settings.FontSize * ( - x.Value * 100 / totalAmount), FontStyle.Regular); - return new RectangleData(layouter.PutNextRectangle(GetTextSize(x.Key, font)), x.Key, font.Size); - }) - .ToList(); + if (!frequencies.IsSuccess) + return Result.Fail>(frequencies.Error); + + var fontResult = Result.Of(() => new Font(settings.FontName, settings.FontSize, FontStyle.Regular)) + .ReplaceError(x => $"Font with name {settings.FontName} does not exist or not installed"); + if (!fontResult.IsSuccess) + return Result.Fail>(fontResult.Error); + + var totalAmount = frequencies.Value.Sum(x => x.Value); + var result = new List(); + foreach (var frequency in frequencies.Value.OrderByDescending(x => x.Value)) + { + using var font = new Font( + settings.FontName, settings.FontSize * (frequency.Value * 100 / totalAmount), FontStyle.Regular + ); + var rectangleResult = layouter.PutNextRectangle(GetTextSize(frequency.Key, font)); + if (!rectangleResult.IsSuccess) + return Result.Fail>(rectangleResult.Error); + + result.Add(new RectangleData(rectangleResult.Value, frequency.Key, font.Size)); + } + + return Result.Ok(result as IEnumerable); } - private Size GetTextSize(string text, Font font) + private static Size GetTextSize(string text, Font font) { using var temporaryBitmap = new Bitmap(1, 1); using var temporaryGraphics = Graphics.FromImage(temporaryBitmap); diff --git a/TagCloudResult/Program.cs b/TagCloudResult/Program.cs index dfe29ab7f..f9e147f2a 100644 --- a/TagCloudResult/Program.cs +++ b/TagCloudResult/Program.cs @@ -1,8 +1,8 @@ using Autofac; using CommandLine; -using TagCloudDi.Applications; +using TagCloudResult.Applications; -namespace TagCloudDi +namespace TagCloudResult { abstract class MainClass { diff --git a/TagCloudResult/Result.cs b/TagCloudResult/Result.cs index 78143b3e8..64262fba5 100644 --- a/TagCloudResult/Result.cs +++ b/TagCloudResult/Result.cs @@ -121,4 +121,4 @@ public static Result RefineError( return input.ReplaceError(err => errorMessage + ". " + err); } } -} \ No newline at end of file +} diff --git a/TagCloudResult/Settings.cs b/TagCloudResult/Settings.cs index eeaac6521..86365b30f 100644 --- a/TagCloudResult/Settings.cs +++ b/TagCloudResult/Settings.cs @@ -1,7 +1,7 @@ using System.Drawing.Imaging; using CommandLine; -namespace TagCloudDi +namespace TagCloudResult { public class Settings { @@ -28,16 +28,16 @@ public class Settings [Option('s', "SavePathWithName", Required = true)] public string SavePath { get; set; } - + [Option('c', "TextColor", Required = false, Default = "White")] public string TextColor { get; set; } - + [Option('b', "BackgroundColor", Required = false, Default = "Black")] public string BackColor { get; set; } - + [Option('i', "ImageFormat", Required = false, Default = "png")] public string ImageFormat { get; set; } - + public ImageFormat GetFormat() => ImageFormat.ToLower() switch { "bmp" => System.Drawing.Imaging.ImageFormat.Bmp, @@ -47,6 +47,5 @@ public class Settings "jpeg" => System.Drawing.Imaging.ImageFormat.Jpeg, _ => throw new NotImplementedException() }; - } } diff --git a/TagCloudResult/TextProcessing/FileTextReader.cs b/TagCloudResult/TextProcessing/FileTextReader.cs index 7014079b9..c5729735a 100644 --- a/TagCloudResult/TextProcessing/FileTextReader.cs +++ b/TagCloudResult/TextProcessing/FileTextReader.cs @@ -1,10 +1,14 @@ -namespace TagCloudDi.TextProcessing +namespace TagCloudResult.TextProcessing { public class FileTextReader : ITextReader { - public IEnumerable GetWordsFrom(string filePath) + public Result> GetWordsFrom(string filePath) { var words = new List(); + if (!File.Exists(filePath)) + return Result.Fail>( + $"Cant't find file with this path {Path.GetFullPath(filePath)}" + ); using var sr = new StreamReader(filePath); var line = sr.ReadLine(); while (line != null) @@ -13,7 +17,7 @@ public IEnumerable GetWordsFrom(string filePath) line = sr.ReadLine(); } - return words; + return Result.Ok(words as IEnumerable); } } } diff --git a/TagCloudResult/TextProcessing/ITextProcessor.cs b/TagCloudResult/TextProcessing/ITextProcessor.cs index a3b754449..aac311124 100644 --- a/TagCloudResult/TextProcessing/ITextProcessor.cs +++ b/TagCloudResult/TextProcessing/ITextProcessor.cs @@ -1,7 +1,7 @@ -namespace TagCloudDi.TextProcessing +namespace TagCloudResult.TextProcessing { public interface ITextProcessor { - public Dictionary GetWordsFrequency(); + public Result> GetWordsFrequency(); } } diff --git a/TagCloudResult/TextProcessing/ITextReader.cs b/TagCloudResult/TextProcessing/ITextReader.cs index 0c39fab64..9b9d6a77a 100644 --- a/TagCloudResult/TextProcessing/ITextReader.cs +++ b/TagCloudResult/TextProcessing/ITextReader.cs @@ -1,7 +1,7 @@ -namespace TagCloudDi.TextProcessing +namespace TagCloudResult.TextProcessing { public interface ITextReader { - IEnumerable GetWordsFrom(string resource); + Result> GetWordsFrom(string resource); } } diff --git a/TagCloudResult/TextProcessing/TextProcessor.cs b/TagCloudResult/TextProcessing/TextProcessor.cs index 11a57024b..4a2c3a4fd 100644 --- a/TagCloudResult/TextProcessing/TextProcessor.cs +++ b/TagCloudResult/TextProcessing/TextProcessor.cs @@ -1,14 +1,24 @@ -namespace TagCloudDi.TextProcessing +namespace TagCloudResult.TextProcessing { public class TextProcessor(Settings settings, ITextReader fileTextReader) : ITextProcessor { - public Dictionary GetWordsFrequency() + public Result> GetWordsFrequency() { - var excludedWords = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath).ToHashSet(); - return fileTextReader.GetWordsFrom(settings.TextPath) + var excludedWordsResult = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath) + .RefineError("Can't read excluded words"); + if (!excludedWordsResult.IsSuccess) + return Result.Fail>(excludedWordsResult.Error); + + var wordsResult = fileTextReader.GetWordsFrom(settings.TextPath) + .RefineError("Can't read words"); + if (!wordsResult.IsSuccess) + return Result.Fail>(wordsResult.Error); + + var excludedWords = excludedWordsResult.Value.ToHashSet(); + return Result.Ok(fileTextReader.GetWordsFrom(settings.TextPath).Value .Where(t => t.Length > 3 && !excludedWords.Contains(t)) .GroupBy(x => x) - .ToDictionary(key => key.Key, amount => amount.Count()); + .ToDictionary(key => key.Key, amount => amount.Count())); } } } diff --git a/TagCloudResult_Tests/ArchimedianSpiral_Should.cs b/TagCloudResult_Tests/ArchimedianSpiral_Should.cs index d1f904055..c8465723a 100644 --- a/TagCloudResult_Tests/ArchimedianSpiral_Should.cs +++ b/TagCloudResult_Tests/ArchimedianSpiral_Should.cs @@ -1,16 +1,15 @@ using System.Drawing; using FluentAssertions; using NUnit.Framework; -using TagCloudDi; -using TagCloudDi.Layouter; +using TagCloudResult.Layouter; -namespace TagCloudDi_Tests +namespace TagCloudResult { [TestFixture] public class ArchimedeanSpiral_Should { private readonly Settings settings = new() { SpiralScale = 1 }; - + [TestCaseSource(typeof(TestDataArchimedeanSpiral), nameof(TestDataArchimedeanSpiral.Different_CenterPoints))] public void ReturnCenterPoint_WhenFirstTime_GetNextPoint(Point point) { diff --git a/TagCloudResult_Tests/CircularCloudLayouter_Should.cs b/TagCloudResult_Tests/CircularCloudLayouter_Should.cs index ec818399e..0eae08059 100644 --- a/TagCloudResult_Tests/CircularCloudLayouter_Should.cs +++ b/TagCloudResult_Tests/CircularCloudLayouter_Should.cs @@ -3,10 +3,9 @@ using FluentAssertions; using NUnit.Framework; using NUnit.Framework.Interfaces; -using TagCloudDi; -using TagCloudDi.Layouter; +using TagCloudResult.Layouter; -namespace TagCloudDi_Tests +namespace TagCloudResult { class CircularCloudLayouter_Should { @@ -37,9 +36,9 @@ public void TagCloudVisualizerCircularCloudLayouterTearDown() public void Throw_WhenPutNewRectangle_WidthOrHeightLessEqualsZero(Size size) { generator = new ArchimedeanSpiral(new Point(), settings); - var action = new Action(() => new CircularCloudLayouter(generator).PutNextRectangle(size)); - action.Should().Throw() - .Which.Message.Should().Contain("zero or negative height or width"); + var result = new CircularCloudLayouter(generator).PutNextRectangle(size); + result.IsSuccess.Should().Be(false); + result.Error.Should().Contain("zero or negative height or width"); } [Test] @@ -55,7 +54,7 @@ public void Add_FirstRectangle_ToCenter(Point center) { var generator = new ArchimedeanSpiral(center, settings); layouter = new CircularCloudLayouter(generator); - layouter.PutNextRectangle(new Size(10, 2)); + layouter.PutNextRectangle(new Size(10, 2)).IsSuccess.Should().Be(true); layouter.Rectangles.Should().HaveCount(1) .And.AllBeEquivalentTo(new Rectangle( new Point(center.X - 10 / 2, center.Y - 2 / 2), new Size(10, 2))); @@ -92,7 +91,7 @@ public void DensityTest() var x = Math.Max( Math.Abs(centerPoint.X - rectangle.X), rectangle.X + rectangle.Width - centerPoint.X - ); + ); var y = Math.Max( Math.Abs(centerPoint.Y - rectangle.Y), rectangle.Y + rectangle.Height - centerPoint.Y @@ -118,4 +117,4 @@ private static CircularCloudLayouter CreateLayouter_With_SeveralRectangles(int a return newLayouter; } } -} \ No newline at end of file +} diff --git a/TagCloudResult_Tests/Result_Should.cs b/TagCloudResult_Tests/Result_Should.cs index d2cf16252..057e5c3da 100644 --- a/TagCloudResult_Tests/Result_Should.cs +++ b/TagCloudResult_Tests/Result_Should.cs @@ -72,10 +72,7 @@ public void SkipThen_WhenFail() [Test] public void Then_ReturnsFail_OnException() { - Func continuation = n => - { - throw new Exception("123"); - }; + Func continuation = n => { throw new Exception("123"); }; var res = Result.Ok(42) .Then(continuation); res.Should().BeEquivalentTo(Result.Fail("123")); @@ -158,4 +155,4 @@ public void RefineError_AddErrorMessageBeforePreviousErrorText() .Should().BeEquivalentTo(Result.Fail("Posting results to db. No connection")); } } -} \ No newline at end of file +} diff --git a/TagCloudResult_Tests/TagCloudResult_Tests.csproj b/TagCloudResult_Tests/TagCloudResult_Tests.csproj index a7e942a9a..931faf9f7 100644 --- a/TagCloudResult_Tests/TagCloudResult_Tests.csproj +++ b/TagCloudResult_Tests/TagCloudResult_Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs b/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs index fc7a84d2a..00574855f 100644 --- a/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs +++ b/TagCloudResult_Tests/TestDataArchimedeanSpiral.cs @@ -1,7 +1,7 @@ using System.Drawing; using NUnit.Framework; -namespace TagCloudDi_Tests +namespace TagCloudResult { public class TestDataArchimedeanSpiral { diff --git a/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs b/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs index 7b792ed4f..c02c62207 100644 --- a/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs +++ b/TagCloudResult_Tests/TestDataCircularCloudLayouter.cs @@ -1,7 +1,7 @@ using System.Drawing; using NUnit.Framework; -namespace TagCloudDi_Tests +namespace TagCloudResult { public class TestDataCircularCloudLayouter { diff --git a/TagCloudResult_Tests/TestDrawer.cs b/TagCloudResult_Tests/TestDrawer.cs index 8990b2701..6ec1170cf 100644 --- a/TagCloudResult_Tests/TestDrawer.cs +++ b/TagCloudResult_Tests/TestDrawer.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace TagCloudDi_Tests +namespace TagCloudResult { public class Test_Drawer { @@ -18,7 +18,7 @@ public static Image GetImage(Size size, IEnumerable rectangles) gr.FillRectangle(brush, rectangle); } } - + return image; } } diff --git a/TagCloudResult_Tests/TextProcessor_Should.cs b/TagCloudResult_Tests/TextProcessor_Should.cs new file mode 100644 index 000000000..d9bcce66d --- /dev/null +++ b/TagCloudResult_Tests/TextProcessor_Should.cs @@ -0,0 +1,39 @@ +using FakeItEasy; +using FluentAssertions; +using NUnit.Framework; +using TagCloudResult.TextProcessing; + +namespace TagCloudResult +{ + public class TextProcessor_Should + { + private ITextReader reader; + private TextProcessor textProcessor; + private Settings settings; + + [SetUp] + public void Setup() + { + reader = A.Fake(); + settings = A.Fake(); + A.CallTo(() => settings.TextPath).Returns("file"); + A.CallTo(() => settings.ExcludedWordsPath).Returns("ex_file"); + A.CallTo(() => reader.GetWordsFrom("file")) + .Returns(Result.Ok(new List { "words" } as IEnumerable)); + A.CallTo(() => reader.GetWordsFrom("ex_file")) + .Returns(Result.Ok(new List { "ex_words" } as IEnumerable)); + textProcessor = new TextProcessor(settings, reader); + } + + [Test] + public void ReturnFail_WhenErrorInWordsFile() + { + A.CallTo(() => reader.GetWordsFrom("file")) + .Returns(Result.Fail>("No File")); + + var result = textProcessor.GetWordsFrequency(); + result.IsSuccess.Should().Be(false); + result.Error.Should().Contain("No File"); + } + } +} From e3dff963c8fa290b559db0bdb8457032375f961f Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Fri, 9 Feb 2024 17:37:22 +0500 Subject: [PATCH 4/6] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D1=82=D1=81=D1=82=D1=83=D0=BF=D1=8B,=20=D0=B8?= =?UTF-8?q?=D0=B7=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=D1=81=D1=8F=20=D0=BE=D1=82?= =?UTF-8?q?=20None,=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B0=20Result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Applications/ConsoleApplication.cs | 9 +- TagCloudResult/Applications/IApplication.cs | 2 +- TagCloudResult/Drawer/Drawer.cs | 20 +-- .../Layouter/CircularCloudLayouter.cs | 4 +- .../Layouter/RectanglesGenerator.cs | 10 +- TagCloudResult/Program.cs | 12 +- TagCloudResult/Result.cs | 123 ++++-------------- TagCloudResult/ResultIs.cs | 105 +++++++++++++++ .../TextProcessing/FileTextReader.cs | 4 +- .../TextProcessing/TextProcessor.cs | 6 +- TagCloudResult_Tests/Result_Should.cs | 59 +++++---- TagCloudResult_Tests/TextProcessor_Should.cs | 39 ------ 12 files changed, 187 insertions(+), 206 deletions(-) create mode 100644 TagCloudResult/ResultIs.cs delete mode 100644 TagCloudResult_Tests/TextProcessor_Should.cs diff --git a/TagCloudResult/Applications/ConsoleApplication.cs b/TagCloudResult/Applications/ConsoleApplication.cs index 900ec116d..b10e0b9de 100644 --- a/TagCloudResult/Applications/ConsoleApplication.cs +++ b/TagCloudResult/Applications/ConsoleApplication.cs @@ -4,17 +4,16 @@ namespace TagCloudResult.Applications { public class ConsoleApplication(IDrawer drawer, Settings settings) : IApplication { - public void Run() + public Result Run() { var imageResult = drawer.GetImage(); if (!imageResult.IsSuccess) - { - Console.WriteLine(imageResult.Error); - return; - } + return Result.Fail(imageResult.Error); imageResult.Value.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); Console.WriteLine($"Saved to {settings.SavePath + '.' + settings.ImageFormat.ToLower()}"); + + return Result.Ok(); } } } diff --git a/TagCloudResult/Applications/IApplication.cs b/TagCloudResult/Applications/IApplication.cs index 5d3c414a7..7eafba6b7 100644 --- a/TagCloudResult/Applications/IApplication.cs +++ b/TagCloudResult/Applications/IApplication.cs @@ -2,6 +2,6 @@ { public interface IApplication { - void Run(); + Result Run(); } } diff --git a/TagCloudResult/Drawer/Drawer.cs b/TagCloudResult/Drawer/Drawer.cs index 9047417fa..dee7023be 100644 --- a/TagCloudResult/Drawer/Drawer.cs +++ b/TagCloudResult/Drawer/Drawer.cs @@ -5,27 +5,17 @@ namespace TagCloudResult.Drawer { public class Drawer(Settings settings, IRectanglesGenerator rectanglesGenerator) : IDrawer { - private Result GetColorFromName(string name) - { - var colorResult = Result.Of(() => Color.FromName(name)); - return !colorResult.IsSuccess - ? Result.Fail($"No such color") - : Result.Ok(colorResult.Value); - } - public Result GetImage() { - var backColorResult = GetColorFromName(settings.BackColor) - .RefineError("Wrong background color"); + var backColorResult = ResultIs.Of(() => Color.FromName(settings.BackColor), "Wrong background color"); if (!backColorResult.IsSuccess) - return Result.Fail(backColorResult.Error); - var colorResult = GetColorFromName(settings.TextColor) - .RefineError("Wrong text color"); + return backColorResult.Fail(); + var colorResult = ResultIs.Of(() => Color.FromName(settings.TextColor), "Wrong text color"); if (!colorResult.IsSuccess) - return Result.Fail(colorResult.Error); + return colorResult.Fail(); var rectanglesDataResult = rectanglesGenerator.GetRectanglesData(); if (!rectanglesDataResult.IsSuccess) - return Result.Fail(rectanglesDataResult.Error); + return rectanglesDataResult.Fail(); var image = new Bitmap(settings.ImageWidth, settings.ImageHeight); using var gr = Graphics.FromImage(image); diff --git a/TagCloudResult/Layouter/CircularCloudLayouter.cs b/TagCloudResult/Layouter/CircularCloudLayouter.cs index 4ba939d25..d6d6c42a4 100644 --- a/TagCloudResult/Layouter/CircularCloudLayouter.cs +++ b/TagCloudResult/Layouter/CircularCloudLayouter.cs @@ -15,7 +15,7 @@ public CircularCloudLayouter(IPointGenerator pointGenerator) public Result PutNextRectangle(Size rectangleSize) { if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) - return Result.Fail($"rectangleSize with zero or negative height or width is prohibited!"); + return ResultIs.Fail($"rectangleSize with zero or negative height or width is prohibited!"); while (true) { var nextPoint = pointGenerator.GetNextPoint(); @@ -27,7 +27,7 @@ public Result PutNextRectangle(Size rectangleSize) break; } - return Result.Ok(Rectangles[^1]); + return ResultIs.Ok(Rectangles[^1]); } private bool IsIntersectsWithOthers(Rectangle rectangle) => diff --git a/TagCloudResult/Layouter/RectanglesGenerator.cs b/TagCloudResult/Layouter/RectanglesGenerator.cs index 30cf2f359..c2fe82af8 100644 --- a/TagCloudResult/Layouter/RectanglesGenerator.cs +++ b/TagCloudResult/Layouter/RectanglesGenerator.cs @@ -10,12 +10,12 @@ public Result> GetRectanglesData() { var frequencies = textProcessor.GetWordsFrequency(); if (!frequencies.IsSuccess) - return Result.Fail>(frequencies.Error); + return ResultIs.Fail>(frequencies.Error); - var fontResult = Result.Of(() => new Font(settings.FontName, settings.FontSize, FontStyle.Regular)) + var fontResult = ResultIs.Of(() => new Font(settings.FontName, settings.FontSize, FontStyle.Regular)) .ReplaceError(x => $"Font with name {settings.FontName} does not exist or not installed"); if (!fontResult.IsSuccess) - return Result.Fail>(fontResult.Error); + return ResultIs.Fail>(fontResult.Error); var totalAmount = frequencies.Value.Sum(x => x.Value); var result = new List(); @@ -26,12 +26,12 @@ public Result> GetRectanglesData() ); var rectangleResult = layouter.PutNextRectangle(GetTextSize(frequency.Key, font)); if (!rectangleResult.IsSuccess) - return Result.Fail>(rectangleResult.Error); + return ResultIs.Fail>(rectangleResult.Error); result.Add(new RectangleData(rectangleResult.Value, frequency.Key, font.Size)); } - return Result.Ok(result as IEnumerable); + return ResultIs.Ok(result as IEnumerable); } private static Size GetTextSize(string text, Font font) diff --git a/TagCloudResult/Program.cs b/TagCloudResult/Program.cs index f9e147f2a..32c6a3694 100644 --- a/TagCloudResult/Program.cs +++ b/TagCloudResult/Program.cs @@ -8,10 +8,14 @@ abstract class MainClass { public static void Main(string[] args) { - var settings = Parser.Default.ParseArguments(args).Value; - var container = Container.SetupContainer(settings); - var app = container.Resolve(); - app.Run(); + var settingsResult = Parser.Default.ParseArguments(args); + if (settingsResult.Errors.Any()) + return; + var appResult = ResultIs.Of(() => Container.SetupContainer(settingsResult.Value).Resolve()) + .RefineError("Impossible to build app") + .Then(x => x.Run()); + if (!appResult.IsSuccess) + Console.WriteLine(appResult.Error); } } } diff --git a/TagCloudResult/Result.cs b/TagCloudResult/Result.cs index 64262fba5..9559d790d 100644 --- a/TagCloudResult/Result.cs +++ b/TagCloudResult/Result.cs @@ -1,124 +1,47 @@ namespace TagCloudResult { - public class None + public class Result { - private None() + public static Result Ok() { + return new Result(null!); } + + protected Result(string error) + { + Error = error; + } + public static Result Fail(string error) => new(error); + + public bool IsSuccess => Error == null!; + public string Error { get; } } - public struct Result + public class Result: Result { - public Result(string error, T value = default(T)) + public Result(string error, T value = default(T)): base(error) { - Error = error; Value = value; } + public static implicit operator Result(T v) { - return Result.Ok(v); + return ResultIs.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, - 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) + + + public Result Fail() { - return input.ReplaceError(err => errorMessage + ". " + err); + return new Result(Error); } } } diff --git a/TagCloudResult/ResultIs.cs b/TagCloudResult/ResultIs.cs new file mode 100644 index 000000000..de60b271c --- /dev/null +++ b/TagCloudResult/ResultIs.cs @@ -0,0 +1,105 @@ +namespace TagCloudResult +{ + public static class ResultIs + { + public static Result AsResult(this T value) + { + return Ok(value); + } + + public static Result Ok(T value) + { + return new Result(null, value); + } + + 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 Result.Ok(); + } + catch (Exception e) + { + return Result.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, + 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 ReplaceError( + this Result input, + Func replaceError) + { + return input.IsSuccess ? input : Result.Fail(replaceError(input.Error)); + } + + public static Result RefineError( + this Result input, + string errorMessage) + { + return input.ReplaceError(err => errorMessage + ". " + err); + } + + public static Result RefineError( + this Result input, + string errorMessage) + { + return input.ReplaceError(err => errorMessage + ". " + err); + } + } +} diff --git a/TagCloudResult/TextProcessing/FileTextReader.cs b/TagCloudResult/TextProcessing/FileTextReader.cs index c5729735a..84cde8383 100644 --- a/TagCloudResult/TextProcessing/FileTextReader.cs +++ b/TagCloudResult/TextProcessing/FileTextReader.cs @@ -6,7 +6,7 @@ public Result> GetWordsFrom(string filePath) { var words = new List(); if (!File.Exists(filePath)) - return Result.Fail>( + return ResultIs.Fail>( $"Cant't find file with this path {Path.GetFullPath(filePath)}" ); using var sr = new StreamReader(filePath); @@ -17,7 +17,7 @@ public Result> GetWordsFrom(string filePath) line = sr.ReadLine(); } - return Result.Ok(words as IEnumerable); + return ResultIs.Ok(words as IEnumerable); } } } diff --git a/TagCloudResult/TextProcessing/TextProcessor.cs b/TagCloudResult/TextProcessing/TextProcessor.cs index 4a2c3a4fd..d47cb7e9c 100644 --- a/TagCloudResult/TextProcessing/TextProcessor.cs +++ b/TagCloudResult/TextProcessing/TextProcessor.cs @@ -7,15 +7,15 @@ public Result> GetWordsFrequency() var excludedWordsResult = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath) .RefineError("Can't read excluded words"); if (!excludedWordsResult.IsSuccess) - return Result.Fail>(excludedWordsResult.Error); + return ResultIs.Fail>(excludedWordsResult.Error); var wordsResult = fileTextReader.GetWordsFrom(settings.TextPath) .RefineError("Can't read words"); if (!wordsResult.IsSuccess) - return Result.Fail>(wordsResult.Error); + return ResultIs.Fail>(wordsResult.Error); var excludedWords = excludedWordsResult.Value.ToHashSet(); - return Result.Ok(fileTextReader.GetWordsFrom(settings.TextPath).Value + return ResultIs.Ok(fileTextReader.GetWordsFrom(settings.TextPath).Value .Where(t => t.Length > 3 && !excludedWords.Contains(t)) .GroupBy(x => x) .ToDictionary(key => key.Key, amount => amount.Count())); diff --git a/TagCloudResult_Tests/Result_Should.cs b/TagCloudResult_Tests/Result_Should.cs index 057e5c3da..20796093b 100644 --- a/TagCloudResult_Tests/Result_Should.cs +++ b/TagCloudResult_Tests/Result_Should.cs @@ -10,7 +10,7 @@ public class Result_Should [Test] public void Create_Ok() { - var r = Result.Ok(42); + var r = ResultIs.Ok(42); r.IsSuccess.Should().BeTrue(); r.GetValueOrThrow().Should().Be(42); } @@ -18,7 +18,7 @@ public void Create_Ok() [Test] public void Create_Fail() { - var r = Result.Fail("123"); + var r = ResultIs.Fail("123"); r.IsSuccess.Should().BeFalse(); r.Error.Should().Be("123"); @@ -27,39 +27,39 @@ public void Create_Fail() [Test] public void ReturnsFail_FromResultOf_OnException() { - var res = Result.Of(() => { throw new Exception("123"); }); + var res = ResultIs.Of(() => { throw new Exception("123"); }); - res.Should().BeEquivalentTo(Result.Fail("123")); + res.Should().BeEquivalentTo(ResultIs.Fail("123")); } [Test] public void ReturnsFailWithCustomMessage_FromResultOf_OnException() { - var res = Result.Of(() => { throw new Exception("123"); }, "42"); + var res = ResultIs.Of(() => { throw new Exception("123"); }, "42"); - res.Should().BeEquivalentTo(Result.Fail("42")); + res.Should().BeEquivalentTo(ResultIs.Fail("42")); } [Test] public void ReturnsOk_FromResultOf_WhenNoException() { - var res = Result.Of(() => 42); + var res = ResultIs.Of(() => 42); - res.Should().BeEquivalentTo(Result.Ok(42)); + res.Should().BeEquivalentTo(ResultIs.Ok(42)); } [Test] public void RunThen_WhenOk() { - var res = Result.Ok(42) + var res = ResultIs.Ok(42) .Then(n => n + 10); - res.Should().BeEquivalentTo(Result.Ok(52)); + res.Should().BeEquivalentTo(ResultIs.Ok(52)); } [Test] public void SkipThen_WhenFail() { - var fail = Result.Fail("������"); + var fail = ResultIs.Fail("������"); var called = false; fail.Then(n => { @@ -73,15 +73,15 @@ public void SkipThen_WhenFail() public void Then_ReturnsFail_OnException() { Func continuation = n => { throw new Exception("123"); }; - var res = Result.Ok(42) + var res = ResultIs.Ok(42) .Then(continuation); - res.Should().BeEquivalentTo(Result.Fail("123")); + res.Should().BeEquivalentTo(ResultIs.Fail("123")); } [Test] public void RunOnFail_WhenFail() { - var fail = Result.Fail("������"); + var fail = ResultIs.Fail("������"); var errorHandler = A.Fake>(); var res = fail.OnFail(errorHandler); @@ -93,7 +93,7 @@ public void RunOnFail_WhenFail() [Test] public void SkipOnFail_WhenOk() { - var ok = Result.Ok(42); + var ok = ResultIs.Ok(42); var res = ok.OnFail(v => { Assert.Fail("Should not be called"); }); @@ -104,55 +104,54 @@ public void SkipOnFail_WhenOk() public void RunThen_WhenOk_Scenario() { var res = - Result.Ok("1358571172") + ResultIs.Ok("1358571172") .Then(int.Parse) .Then(i => Convert.ToString(i, 16)) .Then(hex => Guid.Parse(hex + hex + hex + hex)); - res.Should().BeEquivalentTo(Result.Ok(Guid.Parse("50FA26A450FA26A450FA26A450FA26A4"))); + res.Should().BeEquivalentTo(ResultIs.Ok(Guid.Parse("50FA26A450FA26A450FA26A450FA26A4"))); } [Test] public void RunThen_WhenOk_ComplexScenario() { - var parsed = Result.Ok("1358571172").Then(int.Parse); + var parsed = ResultIs.Ok("1358571172").Then(int.Parse); var res = parsed .Then(i => Convert.ToString(i, 16)) .Then(hex => parsed.GetValueOrThrow() + " -> " + Guid.Parse(hex + hex + hex + hex)); - res.Should().BeEquivalentTo(Result.Ok("1358571172 -> 50fa26a4-50fa-26a4-50fa-26a450fa26a4")); + res.Should().BeEquivalentTo(ResultIs.Ok("1358571172 -> 50fa26a4-50fa-26a4-50fa-26a450fa26a4")); } [Test] public void ReplaceError_IfFail() { - Result.Fail("error") + Result.Fail("error") .ReplaceError(e => "replaced") - .Should().BeEquivalentTo(Result.Fail("replaced")); + .Should().BeEquivalentTo(Result.Fail("replaced")); } [Test] public void ReplaceError_DoNothing_IfSuccess() { - Result.Ok(42) + ResultIs.Ok(42) .ReplaceError(e => "replaced") - .Should().BeEquivalentTo(Result.Ok(42)); + .Should().BeEquivalentTo(ResultIs.Ok(42)); } [Test] public void ReplaceError_DontReplace_IfCalledBeforeError() { - Result.Ok(42) + ResultIs.Ok(42) .ReplaceError(e => "replaced") - .Then(n => Result.Fail("error")) - .Should().BeEquivalentTo(Result.Fail("error")); + .Then(n => ResultIs.Fail("error")) + .Should().BeEquivalentTo(ResultIs.Fail("error")); } [Test] public void RefineError_AddErrorMessageBeforePreviousErrorText() { - var calculation = Result.Fail("No connection"); - calculation - .RefineError("Posting results to db") - .Should().BeEquivalentTo(Result.Fail("Posting results to db. No connection")); + var calculation = Result.Fail("No connection"); + calculation.RefineError("Posting results to db") + .Should().BeEquivalentTo(Result.Fail("Posting results to db. No connection")); } } } diff --git a/TagCloudResult_Tests/TextProcessor_Should.cs b/TagCloudResult_Tests/TextProcessor_Should.cs deleted file mode 100644 index d9bcce66d..000000000 --- a/TagCloudResult_Tests/TextProcessor_Should.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FakeItEasy; -using FluentAssertions; -using NUnit.Framework; -using TagCloudResult.TextProcessing; - -namespace TagCloudResult -{ - public class TextProcessor_Should - { - private ITextReader reader; - private TextProcessor textProcessor; - private Settings settings; - - [SetUp] - public void Setup() - { - reader = A.Fake(); - settings = A.Fake(); - A.CallTo(() => settings.TextPath).Returns("file"); - A.CallTo(() => settings.ExcludedWordsPath).Returns("ex_file"); - A.CallTo(() => reader.GetWordsFrom("file")) - .Returns(Result.Ok(new List { "words" } as IEnumerable)); - A.CallTo(() => reader.GetWordsFrom("ex_file")) - .Returns(Result.Ok(new List { "ex_words" } as IEnumerable)); - textProcessor = new TextProcessor(settings, reader); - } - - [Test] - public void ReturnFail_WhenErrorInWordsFile() - { - A.CallTo(() => reader.GetWordsFrom("file")) - .Returns(Result.Fail>("No File")); - - var result = textProcessor.GetWordsFrequency(); - result.IsSuccess.Should().Be(false); - result.Error.Should().Contain("No File"); - } - } -} From 98e46f1a0441ef0e3259b69a737ad7ddb3daa623 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Fri, 9 Feb 2024 17:39:34 +0500 Subject: [PATCH 5/6] =?UTF-8?q?=D0=A5=D0=BE=D1=82=D1=84=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TagCloudResult/Result.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/TagCloudResult/Result.cs b/TagCloudResult/Result.cs index 9559d790d..d7a908425 100644 --- a/TagCloudResult/Result.cs +++ b/TagCloudResult/Result.cs @@ -29,7 +29,6 @@ public static implicit operator Result(T v) return ResultIs.Ok(v); } - internal T Value { get; } public T GetValueOrThrow() @@ -38,7 +37,6 @@ public T GetValueOrThrow() throw new InvalidOperationException($"No value. Only Error {Error}"); } - public Result Fail() { return new Result(Error); From 2e69e75daec6155563f998d648488728a195d713 Mon Sep 17 00:00:00 2001 From: Crazy1beatch Date: Fri, 9 Feb 2024 21:38:06 +0500 Subject: [PATCH 6/6] =?UTF-8?q?=D0=98=D0=B7=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=D1=81=D1=8F=20=D0=BE=D1=82=20extension=20=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=B4=D0=BE=D0=B2=20=D0=B2=20Result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Applications/ConsoleApplication.cs | 4 +- TagCloudResult/Drawer/Drawer.cs | 4 +- .../Layouter/CircularCloudLayouter.cs | 4 +- .../Layouter/RectanglesGenerator.cs | 10 +- TagCloudResult/Program.cs | 3 +- TagCloudResult/Result.cs | 91 +++++++++++---- TagCloudResult/ResultIs.cs | 105 ------------------ .../TextProcessing/FileTextReader.cs | 4 +- .../TextProcessing/TextProcessor.cs | 8 +- TagCloudResult_Tests/Result_Should.cs | 53 +++++---- 10 files changed, 112 insertions(+), 174 deletions(-) delete mode 100644 TagCloudResult/ResultIs.cs diff --git a/TagCloudResult/Applications/ConsoleApplication.cs b/TagCloudResult/Applications/ConsoleApplication.cs index b10e0b9de..9cd4f6752 100644 --- a/TagCloudResult/Applications/ConsoleApplication.cs +++ b/TagCloudResult/Applications/ConsoleApplication.cs @@ -8,11 +8,11 @@ public Result Run() { var imageResult = drawer.GetImage(); if (!imageResult.IsSuccess) - return Result.Fail(imageResult.Error); + return imageResult.Fail(); imageResult.Value.Save($"{settings.SavePath}.{settings.ImageFormat.ToLower()}", settings.GetFormat()); Console.WriteLine($"Saved to {settings.SavePath + '.' + settings.ImageFormat.ToLower()}"); - + return Result.Ok(); } } diff --git a/TagCloudResult/Drawer/Drawer.cs b/TagCloudResult/Drawer/Drawer.cs index dee7023be..82f20f2b0 100644 --- a/TagCloudResult/Drawer/Drawer.cs +++ b/TagCloudResult/Drawer/Drawer.cs @@ -7,10 +7,10 @@ public class Drawer(Settings settings, IRectanglesGenerator rectanglesGenerator) { public Result GetImage() { - var backColorResult = ResultIs.Of(() => Color.FromName(settings.BackColor), "Wrong background color"); + var backColorResult = Result.Of(() => Color.FromName(settings.BackColor), "Wrong background color"); if (!backColorResult.IsSuccess) return backColorResult.Fail(); - var colorResult = ResultIs.Of(() => Color.FromName(settings.TextColor), "Wrong text color"); + var colorResult = Result.Of(() => Color.FromName(settings.TextColor), "Wrong text color"); if (!colorResult.IsSuccess) return colorResult.Fail(); var rectanglesDataResult = rectanglesGenerator.GetRectanglesData(); diff --git a/TagCloudResult/Layouter/CircularCloudLayouter.cs b/TagCloudResult/Layouter/CircularCloudLayouter.cs index d6d6c42a4..b5853584a 100644 --- a/TagCloudResult/Layouter/CircularCloudLayouter.cs +++ b/TagCloudResult/Layouter/CircularCloudLayouter.cs @@ -15,7 +15,7 @@ public CircularCloudLayouter(IPointGenerator pointGenerator) public Result PutNextRectangle(Size rectangleSize) { if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0) - return ResultIs.Fail($"rectangleSize with zero or negative height or width is prohibited!"); + return Result.Fail($"rectangleSize with zero or negative height or width is prohibited!"); while (true) { var nextPoint = pointGenerator.GetNextPoint(); @@ -27,7 +27,7 @@ public Result PutNextRectangle(Size rectangleSize) break; } - return ResultIs.Ok(Rectangles[^1]); + return Rectangles[^1]; } private bool IsIntersectsWithOthers(Rectangle rectangle) => diff --git a/TagCloudResult/Layouter/RectanglesGenerator.cs b/TagCloudResult/Layouter/RectanglesGenerator.cs index c2fe82af8..edeaa91d1 100644 --- a/TagCloudResult/Layouter/RectanglesGenerator.cs +++ b/TagCloudResult/Layouter/RectanglesGenerator.cs @@ -10,12 +10,12 @@ public Result> GetRectanglesData() { var frequencies = textProcessor.GetWordsFrequency(); if (!frequencies.IsSuccess) - return ResultIs.Fail>(frequencies.Error); + return frequencies.Fail>(); - var fontResult = ResultIs.Of(() => new Font(settings.FontName, settings.FontSize, FontStyle.Regular)) + var fontResult = Result.Of(() => new Font(settings.FontName, settings.FontSize, FontStyle.Regular)) .ReplaceError(x => $"Font with name {settings.FontName} does not exist or not installed"); if (!fontResult.IsSuccess) - return ResultIs.Fail>(fontResult.Error); + return fontResult.Fail>(); var totalAmount = frequencies.Value.Sum(x => x.Value); var result = new List(); @@ -26,12 +26,12 @@ public Result> GetRectanglesData() ); var rectangleResult = layouter.PutNextRectangle(GetTextSize(frequency.Key, font)); if (!rectangleResult.IsSuccess) - return ResultIs.Fail>(rectangleResult.Error); + return rectangleResult.Fail>(); result.Add(new RectangleData(rectangleResult.Value, frequency.Key, font.Size)); } - return ResultIs.Ok(result as IEnumerable); + return result; } private static Size GetTextSize(string text, Font font) diff --git a/TagCloudResult/Program.cs b/TagCloudResult/Program.cs index 32c6a3694..d4c3fa56b 100644 --- a/TagCloudResult/Program.cs +++ b/TagCloudResult/Program.cs @@ -11,7 +11,8 @@ public static void Main(string[] args) var settingsResult = Parser.Default.ParseArguments(args); if (settingsResult.Errors.Any()) return; - var appResult = ResultIs.Of(() => Container.SetupContainer(settingsResult.Value).Resolve()) + var appResult = Result + .Of(() => Container.SetupContainer(settingsResult.Value).Resolve()) .RefineError("Impossible to build app") .Then(x => x.Run()); if (!appResult.IsSuccess) diff --git a/TagCloudResult/Result.cs b/TagCloudResult/Result.cs index d7a908425..1fbee7f21 100644 --- a/TagCloudResult/Result.cs +++ b/TagCloudResult/Result.cs @@ -2,44 +2,87 @@ { public class Result { - public static Result Ok() - { - return new Result(null!); - } + public bool IsSuccess => Error == null!; + public string Error { get; } protected Result(string error) { Error = error; } + + public Result RefineError(string errorMessage) => ReplaceError(err => errorMessage + ". " + err); + + public Result ReplaceError(Func replaceError) => + IsSuccess ? this : Fail(replaceError(Error)); + + public static Result Of(Func func, string error = null!) => Result.Of(func, error); + public static Result Fail(string error) => new(error); + public static Result Fail(Result result) => new(result.Error); public static Result Fail(string error) => new(error); - - public bool IsSuccess => Error == null!; - public string Error { get; } + public static Result Ok() => new(null!); + public static Result Ok(T value) => new(null!, value); } - public class Result: Result + public class Result : Result { - public Result(string error, T value = default(T)): base(error) - { - Value = value; - } - - public static implicit operator Result(T v) + internal T Value { get; } + public Result(string error, T value = default(T)) : base(error) => Value = value; + + public static implicit operator Result(T v) => Ok(v); + + public T GetValueOrThrow() => + IsSuccess ? Value : throw new InvalidOperationException($"No value. Only Error {Error}"); + + public static Result Ok(T value) => new(null!, value); + public new static Result Fail(string e) => new(e); + public Result Fail() => Result.Fail(Error); + public Result Fail() => Result.Fail(Error); + + public static Result Of(Func f, string? error = null) { - return ResultIs.Ok(v); + try + { + return Ok(f()); + } + catch (Exception e) + { + return Fail(error ?? e.Message); + } } - - internal T Value { get; } - - public T GetValueOrThrow() + + public static Result OfAction(Action f, string? error = null) { - if (IsSuccess) return Value; - throw new InvalidOperationException($"No value. Only Error {Error}"); + try + { + f(); + return Result.Ok(); + } + catch (Exception e) + { + return Result.Fail(error ?? e.Message); + } } - - public Result Fail() + + public Result Then(Func continuation) => + Then(inp => Of(() => continuation(inp))); + + public Result Then(Action continuation) => + Then(inp => OfAction(() => continuation(inp))); + + public Result Then(Func> continuation) => + IsSuccess ? continuation(Value) : Result.Fail(Error); + + public Result OnFail( + Action handleError) { - return new Result(Error); + if (!IsSuccess) handleError(Error); + return this; } + + public new Result ReplaceError(Func replaceError) => + IsSuccess ? this : Fail(replaceError(Error)); + + public new Result RefineError(string errorMessage) => + ReplaceError(err => errorMessage + ". " + err); } } diff --git a/TagCloudResult/ResultIs.cs b/TagCloudResult/ResultIs.cs deleted file mode 100644 index de60b271c..000000000 --- a/TagCloudResult/ResultIs.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace TagCloudResult -{ - public static class ResultIs - { - public static Result AsResult(this T value) - { - return Ok(value); - } - - public static Result Ok(T value) - { - return new Result(null, value); - } - - 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 Result.Ok(); - } - catch (Exception e) - { - return Result.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, - 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 ReplaceError( - this Result input, - Func replaceError) - { - return input.IsSuccess ? input : Result.Fail(replaceError(input.Error)); - } - - public static Result RefineError( - this Result input, - string errorMessage) - { - return input.ReplaceError(err => errorMessage + ". " + err); - } - - public static Result RefineError( - this Result input, - string errorMessage) - { - return input.ReplaceError(err => errorMessage + ". " + err); - } - } -} diff --git a/TagCloudResult/TextProcessing/FileTextReader.cs b/TagCloudResult/TextProcessing/FileTextReader.cs index 84cde8383..76bea8d94 100644 --- a/TagCloudResult/TextProcessing/FileTextReader.cs +++ b/TagCloudResult/TextProcessing/FileTextReader.cs @@ -6,7 +6,7 @@ public Result> GetWordsFrom(string filePath) { var words = new List(); if (!File.Exists(filePath)) - return ResultIs.Fail>( + return Result.Fail>( $"Cant't find file with this path {Path.GetFullPath(filePath)}" ); using var sr = new StreamReader(filePath); @@ -17,7 +17,7 @@ public Result> GetWordsFrom(string filePath) line = sr.ReadLine(); } - return ResultIs.Ok(words as IEnumerable); + return words; } } } diff --git a/TagCloudResult/TextProcessing/TextProcessor.cs b/TagCloudResult/TextProcessing/TextProcessor.cs index d47cb7e9c..9bd180ca7 100644 --- a/TagCloudResult/TextProcessing/TextProcessor.cs +++ b/TagCloudResult/TextProcessing/TextProcessor.cs @@ -7,18 +7,18 @@ public Result> GetWordsFrequency() var excludedWordsResult = fileTextReader.GetWordsFrom(settings.ExcludedWordsPath) .RefineError("Can't read excluded words"); if (!excludedWordsResult.IsSuccess) - return ResultIs.Fail>(excludedWordsResult.Error); + return excludedWordsResult.Fail>(); var wordsResult = fileTextReader.GetWordsFrom(settings.TextPath) .RefineError("Can't read words"); if (!wordsResult.IsSuccess) - return ResultIs.Fail>(wordsResult.Error); + return wordsResult.Fail>(); var excludedWords = excludedWordsResult.Value.ToHashSet(); - return ResultIs.Ok(fileTextReader.GetWordsFrom(settings.TextPath).Value + return fileTextReader.GetWordsFrom(settings.TextPath).Value .Where(t => t.Length > 3 && !excludedWords.Contains(t)) .GroupBy(x => x) - .ToDictionary(key => key.Key, amount => amount.Count())); + .ToDictionary(key => key.Key, amount => amount.Count()); } } } diff --git a/TagCloudResult_Tests/Result_Should.cs b/TagCloudResult_Tests/Result_Should.cs index 20796093b..d119c0fc6 100644 --- a/TagCloudResult_Tests/Result_Should.cs +++ b/TagCloudResult_Tests/Result_Should.cs @@ -10,7 +10,7 @@ public class Result_Should [Test] public void Create_Ok() { - var r = ResultIs.Ok(42); + var r = Result.Ok(42); r.IsSuccess.Should().BeTrue(); r.GetValueOrThrow().Should().Be(42); } @@ -18,7 +18,7 @@ public void Create_Ok() [Test] public void Create_Fail() { - var r = ResultIs.Fail("123"); + var r = Result.Fail("123"); r.IsSuccess.Should().BeFalse(); r.Error.Should().Be("123"); @@ -27,39 +27,38 @@ public void Create_Fail() [Test] public void ReturnsFail_FromResultOf_OnException() { - var res = ResultIs.Of(() => { throw new Exception("123"); }); + var res = Result.Of(() => { throw new Exception("123"); }); - res.Should().BeEquivalentTo(ResultIs.Fail("123")); + res.Should().BeEquivalentTo(Result.Fail("123")); } [Test] public void ReturnsFailWithCustomMessage_FromResultOf_OnException() { - var res = ResultIs.Of(() => { throw new Exception("123"); }, "42"); + var res = Result.Of(() => { throw new Exception("123"); }, "42"); - res.Should().BeEquivalentTo(ResultIs.Fail("42")); + res.Should().BeEquivalentTo(Result.Fail("42")); } [Test] public void ReturnsOk_FromResultOf_WhenNoException() { - var res = ResultIs.Of(() => 42); - - res.Should().BeEquivalentTo(ResultIs.Ok(42)); + var res = Result.Of(() => 42); + res.Should().BeEquivalentTo(Result.Ok(42)); } [Test] public void RunThen_WhenOk() { - var res = ResultIs.Ok(42) + var res = Result.Ok(42) .Then(n => n + 10); - res.Should().BeEquivalentTo(ResultIs.Ok(52)); + res.Should().BeEquivalentTo(Result.Ok(52)); } [Test] public void SkipThen_WhenFail() { - var fail = ResultIs.Fail("������"); + var fail = Result.Fail("������"); var called = false; fail.Then(n => { @@ -72,28 +71,28 @@ public void SkipThen_WhenFail() [Test] public void Then_ReturnsFail_OnException() { - Func continuation = n => { throw new Exception("123"); }; - var res = ResultIs.Ok(42) + Func continuation = n => throw new Exception("123"); + var res = Result.Ok(42) .Then(continuation); - res.Should().BeEquivalentTo(ResultIs.Fail("123")); + res.Should().BeEquivalentTo(Result.Fail("123")); } [Test] public void RunOnFail_WhenFail() { - var fail = ResultIs.Fail("������"); + var fail = Result.Fail("������"); var errorHandler = A.Fake>(); var res = fail.OnFail(errorHandler); - A.CallTo(() => errorHandler(null)).WithAnyArguments().MustHaveHappened(); + A.CallTo(() => errorHandler(null!)).WithAnyArguments().MustHaveHappened(); res.Should().BeEquivalentTo(fail); } [Test] public void SkipOnFail_WhenOk() { - var ok = ResultIs.Ok(42); + var ok = Result.Ok(42); var res = ok.OnFail(v => { Assert.Fail("Should not be called"); }); @@ -104,21 +103,21 @@ public void SkipOnFail_WhenOk() public void RunThen_WhenOk_Scenario() { var res = - ResultIs.Ok("1358571172") + Result.Ok("1358571172") .Then(int.Parse) .Then(i => Convert.ToString(i, 16)) .Then(hex => Guid.Parse(hex + hex + hex + hex)); - res.Should().BeEquivalentTo(ResultIs.Ok(Guid.Parse("50FA26A450FA26A450FA26A450FA26A4"))); + res.Should().BeEquivalentTo(Result.Ok(Guid.Parse("50FA26A450FA26A450FA26A450FA26A4"))); } [Test] public void RunThen_WhenOk_ComplexScenario() { - var parsed = ResultIs.Ok("1358571172").Then(int.Parse); + var parsed = Result.Ok("1358571172").Then(int.Parse); var res = parsed .Then(i => Convert.ToString(i, 16)) .Then(hex => parsed.GetValueOrThrow() + " -> " + Guid.Parse(hex + hex + hex + hex)); - res.Should().BeEquivalentTo(ResultIs.Ok("1358571172 -> 50fa26a4-50fa-26a4-50fa-26a450fa26a4")); + res.Should().BeEquivalentTo(Result.Ok("1358571172 -> 50fa26a4-50fa-26a4-50fa-26a450fa26a4")); } [Test] @@ -132,18 +131,18 @@ public void ReplaceError_IfFail() [Test] public void ReplaceError_DoNothing_IfSuccess() { - ResultIs.Ok(42) + Result.Ok(42) .ReplaceError(e => "replaced") - .Should().BeEquivalentTo(ResultIs.Ok(42)); + .Should().BeEquivalentTo(Result.Ok(42)); } [Test] public void ReplaceError_DontReplace_IfCalledBeforeError() { - ResultIs.Ok(42) + Result.Ok(42) .ReplaceError(e => "replaced") - .Then(n => ResultIs.Fail("error")) - .Should().BeEquivalentTo(ResultIs.Fail("error")); + .Then(n => Result.Fail("error")) + .Should().BeEquivalentTo(Result.Fail("error")); } [Test]