-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP/QRCoder2] Fluent API experimentation PR #558
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Payloads | ||
{ | ||
public interface IConfigurableEccLevel | ||
{ | ||
QRCodeGenerator.ECCLevel EccLevel { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Payloads | ||
{ | ||
public interface IConfigurableEciMode | ||
{ | ||
QRCodeGenerator.EciMode EciMode { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Payloads | ||
{ | ||
public interface IConfigurableVersion | ||
{ | ||
int Version { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Payloads | ||
{ | ||
public interface IPayload | ||
{ | ||
QRCodeData ToMatrix(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
|
||
namespace QRCoder.Builders.Payloads.Implementations | ||
{ | ||
public class EmailPayload : PayloadBase | ||
{ | ||
public EmailPayload(string address) | ||
{ | ||
_address = address; | ||
} | ||
|
||
private string _address { get; set; } | ||
private string _subject { get; set; } | ||
private string _body { get; set; } | ||
private PayloadGenerator.Mail.MailEncoding _encoding { get; set; } = PayloadGenerator.Mail.MailEncoding.MAILTO; | ||
|
||
public EmailPayload WithSubject(string subject) | ||
{ | ||
_subject = subject; | ||
return this; | ||
} | ||
|
||
public EmailPayload WithBody(string body) | ||
{ | ||
_body = body; | ||
return this; | ||
} | ||
|
||
public EmailPayload WithEncoding(PayloadGenerator.Mail.MailEncoding encoding) | ||
{ | ||
if (encoding != PayloadGenerator.Mail.MailEncoding.MAILTO && encoding != PayloadGenerator.Mail.MailEncoding.SMTP && encoding != PayloadGenerator.Mail.MailEncoding.MATMSG) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(encoding)); | ||
} | ||
_encoding = encoding; | ||
return this; | ||
} | ||
|
||
protected override string Value => new PayloadGenerator.Mail(_address, _subject, _body, _encoding).ToString(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace QRCoder.Builders.Payloads.Implementations | ||
{ | ||
public class StringPayload : PayloadBase | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use this class for any payload that immediately serializes to a string value. At least if we design it like |
||
{ | ||
private string _data; | ||
|
||
public StringPayload(string data) | ||
{ | ||
_data = data; | ||
} | ||
|
||
protected override string Value => _data; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
namespace QRCoder.Builders.Payloads.Implementations | ||
{ | ||
public class WiFiPayload : PayloadBase | ||
{ | ||
private string _ssid { get; set; } | ||
private string _password { get; set; } | ||
private PayloadGenerator.WiFi.Authentication _authentication { get; set; } | ||
private bool _isHiddenSSID { get; set; } | ||
private bool _isHexStrings { get; set; } | ||
|
||
public WiFiPayload(string ssid) | ||
{ | ||
_ssid = ssid; | ||
_password = ""; | ||
_authentication = PayloadGenerator.WiFi.Authentication.nopass; | ||
} | ||
|
||
public WiFiPayload(string ssid, string password, PayloadGenerator.WiFi.Authentication authentication) | ||
{ | ||
_ssid = ssid; | ||
_password = password; | ||
_authentication = authentication; | ||
} | ||
|
||
public WiFiPayload WithHiddenSSID() | ||
{ | ||
_isHiddenSSID = true; | ||
return this; | ||
} | ||
|
||
public WiFiPayload WithHexStrings() | ||
{ | ||
_isHexStrings = true; | ||
return this; | ||
} | ||
|
||
protected override string Value | ||
{ | ||
get | ||
{ | ||
var wifi = new PayloadGenerator.WiFi(_ssid, _password, _authentication, _isHiddenSSID, _isHexStrings); | ||
return wifi.ToString(); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
namespace QRCoder.Builders.Payloads | ||
{ | ||
public abstract class PayloadBase : IPayload, IConfigurableEccLevel, IConfigurableEciMode, IConfigurableVersion | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea here is that while most all payloads can inherit from |
||
{ | ||
protected virtual QRCodeGenerator.ECCLevel EccLevel { get; set; } = QRCodeGenerator.ECCLevel.Default; | ||
QRCodeGenerator.ECCLevel IConfigurableEccLevel.EccLevel { get => EccLevel; set => EccLevel = value; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These interfaces are always explicitly implemented so that they do not appear on the intellisense list of accessible members for the class. Rather, the extension method will appear, which uses generics to return the same type. If that's confusing, for example, this code would break the fluent syntax: public PayloadBase WithEciMode(EciMode value)
{
_eciMode = value;
} Because it returns the base class type ( |
||
|
||
protected virtual QRCodeGenerator.EciMode EciMode { get; set; } = QRCodeGenerator.EciMode.Default; | ||
QRCodeGenerator.EciMode IConfigurableEciMode.EciMode { get => EciMode; set => EciMode = value; } | ||
|
||
protected virtual int Version { get; set; } = -1; | ||
int IConfigurableVersion.Version { get => Version; set => Version = value; } | ||
|
||
protected abstract string Value { get; } | ||
|
||
public virtual QRCodeData ToMatrix() | ||
{ | ||
return QRCodeGenerator.GenerateQrCode(Value, EccLevel, false, false, EciMode, Version); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using QRCoder.Builders.Payloads; | ||
using QRCoder.Builders.Renderers; | ||
using QRCoder.Builders.Renderers.Implementations; | ||
|
||
namespace QRCoder | ||
{ | ||
public static class PayloadExtensions | ||
{ | ||
public static T WithErrorCorrection<T>(this T payload, QRCodeGenerator.ECCLevel eccLevel) | ||
where T : IConfigurableEccLevel | ||
{ | ||
payload.EccLevel = eccLevel; | ||
return payload; | ||
} | ||
|
||
public static T WithEciMode<T>(this T payload, QRCodeGenerator.EciMode eciMode) | ||
where T : IConfigurableEciMode | ||
{ | ||
payload.EciMode = eciMode; | ||
return payload; | ||
} | ||
|
||
public static T WithVersion<T>(this T payload, int version) | ||
where T : IConfigurableVersion | ||
{ | ||
payload.Version = version; | ||
return payload; | ||
} | ||
|
||
public static T RenderWith<T>(this IPayload payload) | ||
where T : IRenderer, new() | ||
{ | ||
var renderer = new T(); | ||
renderer.Payload = payload; | ||
return renderer; | ||
} | ||
Comment on lines
+30
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
public static T RenderWith<T>(this IPayload payload, int pixelsPerModule) | ||
where T : IRenderer, IConfigurablePixelsPerModule, new() | ||
{ | ||
var renderer = new T(); | ||
renderer.Payload = payload; | ||
renderer.PixelsPerModule = pixelsPerModule; | ||
return renderer; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Renderers | ||
{ | ||
public interface IConfigurablePixelsPerModule | ||
{ | ||
int PixelsPerModule { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace QRCoder.Builders.Renderers | ||
{ | ||
public interface IConfigurableQuietZones | ||
{ | ||
bool QuietZone { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using QRCoder.Builders.Payloads; | ||
|
||
namespace QRCoder.Builders.Renderers | ||
{ | ||
public interface IRenderer | ||
{ | ||
IPayload Payload { set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.IO; | ||
|
||
namespace QRCoder.Builders.Renderers | ||
{ | ||
public interface IStreamRenderer | ||
{ | ||
MemoryStream ToStream(); | ||
} | ||
Comment on lines
+5
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Concept is that all byte-array-type renderers (or ones that can serialize to a byte array) implement this interface. If they use a |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.IO; | ||
|
||
namespace QRCoder.Builders.Renderers | ||
{ | ||
public interface ITextRenderer | ||
{ | ||
string ToString(); | ||
} | ||
Comment on lines
+5
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Concept is that all string-based renderers (Ascii, Svg) implement this interface. Another option would be to have it implement |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
namespace QRCoder.Builders.Renderers.Implementations | ||
{ | ||
public class AsciiRenderer : RendererBase, ITextRenderer | ||
{ | ||
private string _darkString = "██"; | ||
private string _lightString = " "; | ||
private int _repeatPerModule = 1; | ||
private string _endOfLine = System.Environment.NewLine; | ||
private bool _inverseDarkLight = false; | ||
|
||
public AsciiRenderer WithText(string darkString, string lightString) | ||
{ | ||
_darkString = darkString; | ||
_lightString = lightString; | ||
return this; | ||
} | ||
|
||
public AsciiRenderer WithRepeatPerModule(int repeatPerModule) | ||
{ | ||
_repeatPerModule = repeatPerModule; | ||
return this; | ||
} | ||
|
||
public AsciiRenderer WithEndOfLine(string endOfLine) | ||
{ | ||
_endOfLine = endOfLine; | ||
return this; | ||
} | ||
|
||
public AsciiRenderer WithInverseDarkLight() | ||
{ | ||
_inverseDarkLight = true; | ||
return this; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return new AsciiQRCode(QrCodeData).GetGraphic( | ||
_repeatPerModule, | ||
_inverseDarkLight ? _lightString : _darkString, | ||
_inverseDarkLight ? _darkString : _lightString, | ||
QuietZone, | ||
_endOfLine); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.IO; | ||
using QRCoder.Builders.Payloads; | ||
|
||
namespace QRCoder.Builders.Renderers.Implementations | ||
{ | ||
public class PngRenderer : RendererBase, IConfigurablePixelsPerModule, IStreamRenderer | ||
{ | ||
private int _pixelsPerModule = 10; | ||
private byte[] _darkColor; | ||
private byte[] _lightColor; | ||
|
||
int IConfigurablePixelsPerModule.PixelsPerModule { get => _pixelsPerModule; set => _pixelsPerModule = value; } | ||
|
||
#if !NETSTANDARD1_3 | ||
public PngRenderer WithColors(System.Drawing.Color darkColor, System.Drawing.Color lightColor) | ||
{ | ||
_darkColor = new byte[] { darkColor.R, darkColor.G, darkColor.B, darkColor.A }; | ||
_lightColor = new byte[] { lightColor.R, lightColor.G, lightColor.B, lightColor.A }; | ||
return this; | ||
} | ||
#endif | ||
|
||
public PngRenderer WithColors(byte[] darkColor, byte[] lightColor) | ||
{ | ||
_darkColor = darkColor; | ||
_lightColor = lightColor; | ||
return this; | ||
} | ||
|
||
public byte[] ToArray() | ||
{ | ||
if (_darkColor == null && _lightColor == null) | ||
return new PngByteQRCode(QrCodeData).GetGraphic(_pixelsPerModule, QuietZone); | ||
return new PngByteQRCode(QrCodeData).GetGraphic(_pixelsPerModule, _darkColor, _lightColor, QuietZone); | ||
} | ||
|
||
public MemoryStream ToStream() | ||
{ | ||
var arr = ToArray(); | ||
return new MemoryStream(arr, 0, arr.Length, false, true); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#if !NETSTANDARD1_3 | ||
using System.Drawing; | ||
|
||
namespace QRCoder.Builders.Renderers.Implementations | ||
{ | ||
public class SvgRenderer : RendererBase, IConfigurablePixelsPerModule, ITextRenderer | ||
{ | ||
private int _pixelsPerModule = 10; | ||
private Color _darkColor; | ||
private Color _lightColor; | ||
private SvgQRCode.SvgLogo _logo; | ||
private SvgQRCode.SizingMode _sizingMode = SvgQRCode.SizingMode.WidthHeightAttribute; | ||
|
||
int IConfigurablePixelsPerModule.PixelsPerModule { get => _pixelsPerModule; set => _pixelsPerModule = value; } | ||
|
||
public SvgRenderer WithColors(Color darkColor, Color lightColor) | ||
{ | ||
_darkColor = darkColor; | ||
_lightColor = lightColor; | ||
return this; | ||
} | ||
|
||
public SvgRenderer WithLogo(SvgQRCode.SvgLogo logo) | ||
{ | ||
_logo = logo; | ||
return this; | ||
} | ||
|
||
public SvgRenderer WithSizingMode(SvgQRCode.SizingMode sizingMode) | ||
{ | ||
_sizingMode = sizingMode; | ||
return this; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
return new SvgQRCode(QrCodeData).GetGraphic( | ||
_pixelsPerModule, _darkColor, _lightColor, QuietZone, _sizingMode, _logo); | ||
} | ||
} | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sample where the ctor only has the email address, and the subject/body are provided by extension methods. Realistically, I'm thinking that the required or common parameters always appear in the constructor (or as an overload), but all of the optional/rare options (like
MailEncoding
) are extension methods.