diff --git a/Source/Bogus/Binder.cs b/Source/Bogus/Binder.cs index 27af4e11..bf269059 100644 --- a/Source/Bogus/Binder.cs +++ b/Source/Bogus/Binder.cs @@ -5,117 +5,116 @@ using System.Runtime.CompilerServices; using Bogus.Platform; -namespace Bogus +namespace Bogus; + +/// +/// A binder is used in Faker[T] for extracting MemberInfo from T +/// that are candidates for property/field faking. +/// +public interface IBinder { /// - /// A binder is used in Faker[T] for extracting MemberInfo from T - /// that are candidates for property/field faking. + /// Given T, the method must return a Dictionary[string,MemberInfo] where + /// string is the field/property name and MemberInfo is the reflected + /// member info of the field/property that will be used for invoking + /// and setting values. The returned Dictionary must encompass the full + /// set of viable properties/fields that can be faked on T. + /// + /// The full set of MemberInfos for injection. + Dictionary GetMembers(Type t); +} + +/// +/// The default binder used in Faker[T] for extracting MemberInfo from T +/// that are candidates for property/field faking. +/// +public class Binder : IBinder +{ + /// + /// The binding flags to use when reflecting over T. + /// + protected internal BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; + + /// + /// Construct a binder with default binding flags. Public/internal properties and public/internal fields. /// - public interface IBinder + public Binder() { - /// - /// Given T, the method must return a Dictionary[string,MemberInfo] where - /// string is the field/property name and MemberInfo is the reflected - /// member info of the field/property that will be used for invoking - /// and setting values. The returned Dictionary must encompass the full - /// set of viable properties/fields that can be faked on T. - /// - /// The full set of MemberInfos for injection. - Dictionary GetMembers(Type t); } /// - /// The default binder used in Faker[T] for extracting MemberInfo from T - /// that are candidates for property/field faking. + /// Construct a binder with custom binding flags. /// - public class Binder : IBinder + public Binder(BindingFlags bindingFlags) { - /// - /// The binding flags to use when reflecting over T. - /// - protected internal BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; - - /// - /// Construct a binder with default binding flags. Public/internal properties and public/internal fields. - /// - public Binder() - { - } - - /// - /// Construct a binder with custom binding flags. - /// - public Binder(BindingFlags bindingFlags) - { - BindingFlags = bindingFlags; - } - + BindingFlags = bindingFlags; + } + - /// - /// Given T, the method will return a Dictionary[string,MemberInfo] where - /// string is the field/property name and MemberInfo is the reflected - /// member info of the field/property that will be used for invocation - /// and setting values. The returned Dictionary must encompass the full - /// set of viable properties/fields that can be faked on T. - /// - /// The full set of MemberInfos for injection. - public virtual Dictionary GetMembers(Type t) - { - var allReflectedMembers = t.GetAllMembers(this.BindingFlags) - .Select(m => UseBaseTypeDeclaredPropertyInfo(t, m)); + /// + /// Given T, the method will return a Dictionary[string,MemberInfo] where + /// string is the field/property name and MemberInfo is the reflected + /// member info of the field/property that will be used for invocation + /// and setting values. The returned Dictionary must encompass the full + /// set of viable properties/fields that can be faked on T. + /// + /// The full set of MemberInfos for injection. + public virtual Dictionary GetMembers(Type t) + { + var allReflectedMembers = t.GetAllMembers(this.BindingFlags) + .Select(m => UseBaseTypeDeclaredPropertyInfo(t, m)); - var settableMembers = allReflectedMembers - .Where(m => + var settableMembers = allReflectedMembers + .Where(m => + { + if( m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any() ) { - if( m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any() ) - { - //no compiler generated stuff - return false; - } + //no compiler generated stuff + return false; + } - if( m is PropertyInfo pi ) - { - return pi.CanWrite; - } + if( m is PropertyInfo pi ) + { + return pi.CanWrite; + } - if( m is FieldInfo fi ) - { - //No private fields. - //GitHub Issue #13 - return !fi.IsPrivate; - } + if( m is FieldInfo fi ) + { + //No private fields. + //GitHub Issue #13 + return !fi.IsPrivate; + } - return false; - }); + return false; + }); - var settableMembersByName = settableMembers.GroupBy(mi => mi.Name); + var settableMembersByName = settableMembers.GroupBy(mi => mi.Name); - //Issue #70 we could get back multiple keys - //when reflecting over a type. Consider: - // - // ClassA { public int Value {get;set} } - // DerivedA : ClassA { public new int Value {get;set;} } - // - //So, when reflecting over DerivedA, grab the first - //reflected MemberInfo that was returned from - //reflection; the second one was the inherited - //ClassA.Value. - return settableMembersByName.ToDictionary(k => k.Key, g => g.First()); - } + //Issue #70 we could get back multiple keys + //when reflecting over a type. Consider: + // + // ClassA { public int Value {get;set} } + // DerivedA : ClassA { public new int Value {get;set;} } + // + //So, when reflecting over DerivedA, grab the first + //reflected MemberInfo that was returned from + //reflection; the second one was the inherited + //ClassA.Value. + return settableMembersByName.ToDictionary(k => k.Key, g => g.First()); + } - //Issue #389 - Use Declaring Base Type PropertyInfo instead of a DerivedA's - //PropertyInfo because DerivedA's PropertyInfo could say property is not - //write-able. - protected virtual MemberInfo UseBaseTypeDeclaredPropertyInfo(Type t, MemberInfo m) + //Issue #389 - Use Declaring Base Type PropertyInfo instead of a DerivedA's + //PropertyInfo because DerivedA's PropertyInfo could say property is not + //write-able. + protected virtual MemberInfo UseBaseTypeDeclaredPropertyInfo(Type t, MemberInfo m) + { + if( m is PropertyInfo {CanWrite: false} && m.DeclaringType is not null && m.DeclaringType != t ) { - if( m is PropertyInfo {CanWrite: false} && m.DeclaringType is not null && m.DeclaringType != t ) - { - var newPropInfo = m.DeclaringType.GetProperty(m.Name, this.BindingFlags); - if( newPropInfo is not null ) - return newPropInfo; - } - - return m; + var newPropInfo = m.DeclaringType.GetProperty(m.Name, this.BindingFlags); + if( newPropInfo is not null ) + return newPropInfo; } + + return m; } } \ No newline at end of file diff --git a/Source/Bogus/BogusException.cs b/Source/Bogus/BogusException.cs index 527cc68f..61fd5f49 100644 --- a/Source/Bogus/BogusException.cs +++ b/Source/Bogus/BogusException.cs @@ -1,22 +1,21 @@ using System; -namespace Bogus +namespace Bogus; + +/// +/// General exception for Bogus. +/// +public class BogusException : Exception { - /// - /// General exception for Bogus. - /// - public class BogusException : Exception + public BogusException() { - public BogusException() - { - } + } - public BogusException(string message) : base(message) - { - } + public BogusException(string message) : base(message) + { + } - public BogusException(string message, Exception innerException) : base(message, innerException) - { - } + public BogusException(string message, Exception innerException) : base(message, innerException) + { } } \ No newline at end of file diff --git a/Source/Bogus/Bson/BArray.cs b/Source/Bogus/Bson/BArray.cs index 2010bac6..82270d87 100644 --- a/Source/Bogus/Bson/BArray.cs +++ b/Source/Bogus/Bson/BArray.cs @@ -3,43 +3,42 @@ using System.Collections; using System.Collections.Generic; -namespace Bogus.Bson +namespace Bogus.Bson; + +public class BArray : BValue, IEnumerable { - public class BArray : BValue, IEnumerable - { - private readonly List items = new List(); + private readonly List items = new List(); - public BArray() : base(BValueType.Array) - { - } + public BArray() : base(BValueType.Array) + { + } - public override BValue this[int index] - { - get => items[index]; - set => items[index] = value; - } + public override BValue this[int index] + { + get => items[index]; + set => items[index] = value; + } - public bool HasValues => items.Count > 0; + public bool HasValues => items.Count > 0; - public int Count => items.Count; + public int Count => items.Count; - public override void Add(BValue v) => items.Add(v); + public override void Add(BValue v) => items.Add(v); - public int IndexOf(BValue item) => items.IndexOf(item); + public int IndexOf(BValue item) => items.IndexOf(item); - public void Insert(int index, BValue item) => items.Insert(index, item); + public void Insert(int index, BValue item) => items.Insert(index, item); - public bool Remove(BValue v) => items.Remove(v); + public bool Remove(BValue v) => items.Remove(v); - public void RemoveAt(int index) => items.RemoveAt(index); + public void RemoveAt(int index) => items.RemoveAt(index); - public override void Clear() => items.Clear(); + public override void Clear() => items.Clear(); - public virtual bool Contains(BValue v) => items.Contains(v); + public virtual bool Contains(BValue v) => items.Contains(v); - IEnumerator IEnumerable.GetEnumerator() - { - return items.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return items.GetEnumerator(); } } \ No newline at end of file diff --git a/Source/Bogus/Bson/BObject.cs b/Source/Bogus/Bson/BObject.cs index 4f82a104..85a8738d 100644 --- a/Source/Bogus/Bson/BObject.cs +++ b/Source/Bogus/Bson/BObject.cs @@ -3,48 +3,47 @@ using System.Collections; using System.Collections.Generic; -namespace Bogus.Bson +namespace Bogus.Bson; + +public class BObject : BValue, IEnumerable { - public class BObject : BValue, IEnumerable - { - private Dictionary map = new Dictionary(); + private Dictionary map = new Dictionary(); - public BObject() : base(BValueType.Object) - { - } + public BObject() : base(BValueType.Object) + { + } - public ICollection Keys => map.Keys; + public ICollection Keys => map.Keys; - public ICollection Values => map.Values; + public ICollection Values => map.Values; - public int Count => map.Count; + public int Count => map.Count; - public override BValue this[string key] + public override BValue this[string key] + { + get { - get - { - map.TryGetValue(key, out BValue val); - return val; - } - set => map[key] = value; + map.TryGetValue(key, out BValue val); + return val; } + set => map[key] = value; + } - public override void Clear() => map.Clear(); + public override void Clear() => map.Clear(); - public override void Add(string key, BValue value) => map.Add(key, value); + public override void Add(string key, BValue value) => map.Add(key, value); - public override bool Contains(BValue v) => map.ContainsValue(v); + public override bool Contains(BValue v) => map.ContainsValue(v); - public override bool ContainsKey(string key) => map.ContainsKey(key); + public override bool ContainsKey(string key) => map.ContainsKey(key); - public bool Remove(string key) => map.Remove(key); + public bool Remove(string key) => map.Remove(key); - public bool TryGetValue(string key, out BValue value) => map.TryGetValue(key, out value); + public bool TryGetValue(string key, out BValue value) => map.TryGetValue(key, out value); - IEnumerator IEnumerable.GetEnumerator() - { - return map.GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return map.GetEnumerator(); } } \ No newline at end of file diff --git a/Source/Bogus/Bson/BValue.cs b/Source/Bogus/Bson/BValue.cs index ba4d7d74..5987c180 100644 --- a/Source/Bogus/Bson/BValue.cs +++ b/Source/Bogus/Bson/BValue.cs @@ -3,244 +3,243 @@ using System; using System.Text; -namespace Bogus.Bson +namespace Bogus.Bson; + +/// +/// Most, if not all of this BSON implementation was copied from https://github.com/kernys/Kernys.Bson. +/// Just polished it up a bit for Bogus in 2017/C# 7.1. +/// +public class BValue { - /// - /// Most, if not all of this BSON implementation was copied from https://github.com/kernys/Kernys.Bson. - /// Just polished it up a bit for Bogus in 2017/C# 7.1. - /// - public class BValue - { - private BValueType valueType; - private Double _double; - private string _string; - private byte[] _binary; - private bool _bool; - private DateTime _dateTime; - private Int32 _int32; - private Int64 _int64; - - public BValueType ValueType => valueType; - - public Double DoubleValue + private BValueType valueType; + private Double _double; + private string _string; + private byte[] _binary; + private bool _bool; + private DateTime _dateTime; + private Int32 _int32; + private Int64 _int64; + + public BValueType ValueType => valueType; + + public Double DoubleValue + { + get { - get + switch( this.valueType ) { - switch( this.valueType ) - { - case BValueType.Int32: - return _int32; - case BValueType.Int64: - return _int64; - case BValueType.Double: - return _double; - case BValueType.None: - return float.NaN; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to double"); + case BValueType.Int32: + return _int32; + case BValueType.Int64: + return _int64; + case BValueType.Double: + return _double; + case BValueType.None: + return float.NaN; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to double"); } + } - public Int32 Int32Value + public Int32 Int32Value + { + get { - get + switch( this.valueType ) { - switch( this.valueType ) - { - case BValueType.Int32: - return _int32; - case BValueType.Int64: - return (Int32)_int64; - case BValueType.Double: - return (Int32)_double; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to Int32"); + case BValueType.Int32: + return _int32; + case BValueType.Int64: + return (Int32)_int64; + case BValueType.Double: + return (Int32)_double; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to Int32"); } + } - public Int64 Int64Value + public Int64 Int64Value + { + get { - get + switch( this.valueType ) { - switch( this.valueType ) - { - case BValueType.Int32: - return _int32; - case BValueType.Int64: - return _int64; - case BValueType.Double: - return (Int64)_double; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to Int64"); + case BValueType.Int32: + return _int32; + case BValueType.Int64: + return _int64; + case BValueType.Double: + return (Int64)_double; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to Int64"); } + } - public byte[] BinaryValue + public byte[] BinaryValue + { + get { - get + switch( valueType ) { - switch( valueType ) - { - case BValueType.Binary: - return _binary; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to binary"); + case BValueType.Binary: + return _binary; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to binary"); } + } - public DateTime DateTimeValue + public DateTime DateTimeValue + { + get { - get + switch( valueType ) { - switch( valueType ) - { - case BValueType.UTCDateTime: - return _dateTime; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to DateTime"); + case BValueType.UTCDateTime: + return _dateTime; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to DateTime"); } + } - public String StringValue + public String StringValue + { + get { - get + switch( valueType ) { - switch( valueType ) - { - case BValueType.Int32: - return Convert.ToString(_int32); - case BValueType.Int64: - return Convert.ToString(_int64); - case BValueType.Double: - return Convert.ToString(_double); - case BValueType.String: - return _string != null ? _string.TrimEnd((char)0) : null; - case BValueType.Binary: - return Encoding.UTF8.GetString(_binary).TrimEnd((char)0); - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to string"); + case BValueType.Int32: + return Convert.ToString(_int32); + case BValueType.Int64: + return Convert.ToString(_int64); + case BValueType.Double: + return Convert.ToString(_double); + case BValueType.String: + return _string != null ? _string.TrimEnd((char)0) : null; + case BValueType.Binary: + return Encoding.UTF8.GetString(_binary).TrimEnd((char)0); } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to string"); } + } - public bool BoolValue + public bool BoolValue + { + get { - get + switch( valueType ) { - switch( valueType ) - { - case BValueType.Boolean: - return _bool; - } - - throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to bool"); + case BValueType.Boolean: + return _bool; } + + throw new Exception($"Original type is {this.valueType}. Cannot convert from {this.valueType} to bool"); } + } - public bool IsNone => valueType == BValueType.None; + public bool IsNone => valueType == BValueType.None; - public virtual BValue this[string key] - { - get { return null; } - set { } - } + public virtual BValue this[string key] + { + get { return null; } + set { } + } - public virtual BValue this[int index] - { - get { return null; } - set { } - } + public virtual BValue this[int index] + { + get { return null; } + set { } + } - public virtual void Clear() { } - public virtual void Add(string key, BValue value) { } - public virtual void Add(BValue value) { } - public virtual bool Contains(BValue v) { return false; } - public virtual bool ContainsKey(string key) { return false; } + public virtual void Clear() { } + public virtual void Add(string key, BValue value) { } + public virtual void Add(BValue value) { } + public virtual bool Contains(BValue v) { return false; } + public virtual bool ContainsKey(string key) { return false; } - public static implicit operator BValue(double v) => new BValue(v); + public static implicit operator BValue(double v) => new BValue(v); - public static implicit operator BValue(Int32 v) => new BValue(v); + public static implicit operator BValue(Int32 v) => new BValue(v); - public static implicit operator BValue(Int64 v) => new BValue(v); + public static implicit operator BValue(Int64 v) => new BValue(v); - public static implicit operator BValue(byte[] v) => new BValue(v); + public static implicit operator BValue(byte[] v) => new BValue(v); - public static implicit operator BValue(DateTime v) => new BValue(v); + public static implicit operator BValue(DateTime v) => new BValue(v); - public static implicit operator BValue(string v) => new BValue(v); + public static implicit operator BValue(string v) => new BValue(v); - public static implicit operator double(BValue v) => v.DoubleValue; + public static implicit operator double(BValue v) => v.DoubleValue; - public static implicit operator Int32(BValue v) => v.Int32Value; + public static implicit operator Int32(BValue v) => v.Int32Value; - public static implicit operator Int64(BValue v) => v.Int64Value; + public static implicit operator Int64(BValue v) => v.Int64Value; - public static implicit operator byte[] (BValue v) => v.BinaryValue; + public static implicit operator byte[] (BValue v) => v.BinaryValue; - public static implicit operator DateTime(BValue v) => v.DateTimeValue; + public static implicit operator DateTime(BValue v) => v.DateTimeValue; - public static implicit operator string(BValue v) => v.StringValue; + public static implicit operator string(BValue v) => v.StringValue; - protected BValue(BValueType valueType) - { - this.valueType = valueType; - } + protected BValue(BValueType valueType) + { + this.valueType = valueType; + } - public BValue() - { - this.valueType = BValueType.None; - } + public BValue() + { + this.valueType = BValueType.None; + } - public BValue(double v) - { - this.valueType = BValueType.Double; - _double = v; - } + public BValue(double v) + { + this.valueType = BValueType.Double; + _double = v; + } - public BValue(String v) - { - this.valueType = BValueType.String; - _string = v; - } + public BValue(String v) + { + this.valueType = BValueType.String; + _string = v; + } - public BValue(byte[] v) - { - this.valueType = BValueType.Binary; - _binary = v; - } + public BValue(byte[] v) + { + this.valueType = BValueType.Binary; + _binary = v; + } - public BValue(bool v) - { - this.valueType = BValueType.Boolean; - _bool = v; - } + public BValue(bool v) + { + this.valueType = BValueType.Boolean; + _bool = v; + } - public BValue(DateTime dt) - { - this.valueType = BValueType.UTCDateTime; - _dateTime = dt; - } + public BValue(DateTime dt) + { + this.valueType = BValueType.UTCDateTime; + _dateTime = dt; + } - public BValue(Int32 v) - { - this.valueType = BValueType.Int32; - _int32 = v; - } + public BValue(Int32 v) + { + this.valueType = BValueType.Int32; + _int32 = v; + } - public BValue(Int64 v) - { - this.valueType = BValueType.Int64; - _int64 = v; - } + public BValue(Int64 v) + { + this.valueType = BValueType.Int64; + _int64 = v; + } - public static bool operator ==(BValue a, object b) => ReferenceEquals(a, b); + public static bool operator ==(BValue a, object b) => ReferenceEquals(a, b); - public static bool operator !=(BValue a, object b) => !(a == b); - } + public static bool operator !=(BValue a, object b) => !(a == b); } \ No newline at end of file diff --git a/Source/Bogus/Bson/BValueType.cs b/Source/Bogus/Bson/BValueType.cs index fe55dd25..eefa5ca7 100644 --- a/Source/Bogus/Bson/BValueType.cs +++ b/Source/Bogus/Bson/BValueType.cs @@ -1,18 +1,17 @@ #pragma warning disable 1591 -namespace Bogus.Bson +namespace Bogus.Bson; + +public enum BValueType { - public enum BValueType - { - Double, - String, - Array, - Binary, - Boolean, - UTCDateTime, - None, - Int32, - Int64, - Object - } + Double, + String, + Array, + Binary, + Boolean, + UTCDateTime, + None, + Int32, + Int64, + Object } \ No newline at end of file diff --git a/Source/Bogus/Bson/Bson.cs b/Source/Bogus/Bson/Bson.cs index 76f605f9..889ac3a1 100644 --- a/Source/Bogus/Bson/Bson.cs +++ b/Source/Bogus/Bson/Bson.cs @@ -4,319 +4,318 @@ using System.IO; using System.Text; -namespace Bogus.Bson +namespace Bogus.Bson; + +public class Bson { - public class Bson + private readonly MemoryStream stream; + private BinaryReader reader; + + public static BObject Load(byte[] buf) { - private readonly MemoryStream stream; - private BinaryReader reader; + var bson = new Bson(buf); - public static BObject Load(byte[] buf) - { - var bson = new Bson(buf); + return bson.DecodeDocument(); + } - return bson.DecodeDocument(); - } + private Bson(byte[] buf) + { + stream = new MemoryStream(buf); + reader = new BinaryReader(stream); + } + + private BValue DecodeElement(out string name) + { + byte elementType = reader.ReadByte(); - private Bson(byte[] buf) + if( elementType == 0x01 ) { - stream = new MemoryStream(buf); - reader = new BinaryReader(stream); + // Double + name = DecodeCString(); + return new BValue(reader.ReadDouble()); } - - private BValue DecodeElement(out string name) + if( elementType == 0x02 ) { - byte elementType = reader.ReadByte(); - - if( elementType == 0x01 ) - { - // Double - name = DecodeCString(); - return new BValue(reader.ReadDouble()); - } - if( elementType == 0x02 ) - { - // String - name = DecodeCString(); - return new BValue(DecodeString()); - } - if( elementType == 0x03 ) - { - // Document - name = DecodeCString(); - return DecodeDocument(); - } - if( elementType == 0x04 ) - { - // Array - name = DecodeCString(); - return DecodeArray(); - } - if( elementType == 0x05 ) - { - // Binary - name = DecodeCString(); - int length = reader.ReadInt32(); - byte binaryType = reader.ReadByte(); - - return new BValue(reader.ReadBytes(length)); - } - if( elementType == 0x08 ) - { - // Boolean - name = DecodeCString(); - return new BValue(reader.ReadBoolean()); - } - if( elementType == 0x09 ) - { - // DateTime - name = DecodeCString(); - Int64 time = reader.ReadInt64(); - return new BValue(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + new TimeSpan(time * 10000)); - } - if( elementType == 0x0A ) - { - // None - name = DecodeCString(); - return new BValue(); - } - if( elementType == 0x10 ) - { - // Int32 - name = DecodeCString(); - return new BValue(reader.ReadInt32()); - } - if( elementType == 0x12 ) - { - // Int64 - name = DecodeCString(); - return new BValue(reader.ReadInt64()); - } - - throw new Exception($"Don't know elementType={elementType}"); + // String + name = DecodeCString(); + return new BValue(DecodeString()); } - - private BObject DecodeDocument() + if( elementType == 0x03 ) { - int length = reader.ReadInt32() - 4; - - BObject obj = new BObject(); - - int i = (int)reader.BaseStream.Position; - while( reader.BaseStream.Position < i + length - 1 ) - { - BValue value = DecodeElement(out var name); - obj.Add(name, value); - } - - reader.ReadByte(); // zero - return obj; + // Document + name = DecodeCString(); + return DecodeDocument(); } - - private BArray DecodeArray() + if( elementType == 0x04 ) { - BObject obj = DecodeDocument(); - - int i = 0; - BArray array = new BArray(); - while( obj.ContainsKey(Convert.ToString(i)) ) - { - array.Add(obj[Convert.ToString(i)]); - - i += 1; - } - - return array; + // Array + name = DecodeCString(); + return DecodeArray(); } - - private string DecodeString() + if( elementType == 0x05 ) { + // Binary + name = DecodeCString(); int length = reader.ReadInt32(); - byte[] buf = reader.ReadBytes(length); + byte binaryType = reader.ReadByte(); - return Encoding.UTF8.GetString(buf); + return new BValue(reader.ReadBytes(length)); } - - private string DecodeCString() + if( elementType == 0x08 ) { - using var ms = new MemoryStream(); - while( true ) - { - byte buf = reader.ReadByte(); - if( buf == 0 ) - break; - ms.WriteByte(buf); - } - return Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Position); + // Boolean + name = DecodeCString(); + return new BValue(reader.ReadBoolean()); } - - - #region ENCODING - - private Bson() + if( elementType == 0x09 ) { - stream = new MemoryStream(); + // DateTime + name = DecodeCString(); + Int64 time = reader.ReadInt64(); + return new BValue(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) + new TimeSpan(time * 10000)); } - - public static byte[] Serialize(BObject obj) + if( elementType == 0x0A ) + { + // None + name = DecodeCString(); + return new BValue(); + } + if( elementType == 0x10 ) { - var bson = new Bson(); - var ms = new MemoryStream(); + // Int32 + name = DecodeCString(); + return new BValue(reader.ReadInt32()); + } + if( elementType == 0x12 ) + { + // Int64 + name = DecodeCString(); + return new BValue(reader.ReadInt64()); + } - bson.EncodeDocument(ms, obj); + throw new Exception($"Don't know elementType={elementType}"); + } - byte[] buf = new byte[ms.Position]; - ms.Seek(0, SeekOrigin.Begin); - ms.Read(buf, 0, buf.Length); + private BObject DecodeDocument() + { + int length = reader.ReadInt32() - 4; - return buf; - } + BObject obj = new BObject(); - private void EncodeElement(MemoryStream ms, string name, BValue v) + int i = (int)reader.BaseStream.Position; + while( reader.BaseStream.Position < i + length - 1 ) { - switch( v.ValueType ) - { - case BValueType.Double: - ms.WriteByte(0x01); - EncodeCString(ms, name); - EncodeDouble(ms, v.DoubleValue); - return; - case BValueType.String: - ms.WriteByte(0x02); - EncodeCString(ms, name); - EncodeString(ms, v.StringValue); - return; - case BValueType.Object: - ms.WriteByte(0x03); - EncodeCString(ms, name); - EncodeDocument(ms, v as BObject); - return; - case BValueType.Array: - ms.WriteByte(0x04); - EncodeCString(ms, name); - EncodeArray(ms, v as BArray); - return; - case BValueType.Binary: - ms.WriteByte(0x05); - EncodeCString(ms, name); - EncodeBinary(ms, v.BinaryValue); - return; - case BValueType.Boolean: - ms.WriteByte(0x08); - EncodeCString(ms, name); - EncodeBool(ms, v.BoolValue); - return; - case BValueType.UTCDateTime: - ms.WriteByte(0x09); - EncodeCString(ms, name); - EncodeUTCDateTime(ms, v.DateTimeValue); - return; - case BValueType.None: - ms.WriteByte(0x0A); - EncodeCString(ms, name); - return; - case BValueType.Int32: - ms.WriteByte(0x10); - EncodeCString(ms, name); - EncodeInt32(ms, v.Int32Value); - return; - case BValueType.Int64: - ms.WriteByte(0x12); - EncodeCString(ms, name); - EncodeInt64(ms, v.Int64Value); - return; - } + BValue value = DecodeElement(out var name); + obj.Add(name, value); } - private void EncodeDocument(MemoryStream ms, BObject obj) - { - var dms = new MemoryStream(); - foreach( string str in obj.Keys ) - { - EncodeElement(dms, str, obj[str]); - } - - var bw = new BinaryWriter(ms); - bw.Write((Int32)(dms.Position + 4 + 1)); - bw.Write(dms.ToArray(), 0, (int)dms.Position); - bw.Write((byte)0); - } + reader.ReadByte(); // zero + return obj; + } + + private BArray DecodeArray() + { + BObject obj = DecodeDocument(); - private void EncodeArray(MemoryStream ms, BArray lst) + int i = 0; + BArray array = new BArray(); + while( obj.ContainsKey(Convert.ToString(i)) ) { - var obj = new BObject(); - for( int i = 0; i < lst.Count; ++i ) - { - obj.Add(Convert.ToString(i), lst[i]); - } + array.Add(obj[Convert.ToString(i)]); - EncodeDocument(ms, obj); + i += 1; } - private void EncodeBinary(MemoryStream ms, byte[] buf) - { - byte[] aBuf = BitConverter.GetBytes(buf.Length); - ms.Write(aBuf, 0, aBuf.Length); - ms.WriteByte(0); - ms.Write(buf, 0, buf.Length); - } + return array; + } - private void EncodeCString(MemoryStream ms, string v) + private string DecodeString() + { + int length = reader.ReadInt32(); + byte[] buf = reader.ReadBytes(length); + + return Encoding.UTF8.GetString(buf); + } + + private string DecodeCString() + { + using var ms = new MemoryStream(); + while( true ) { - byte[] buf = new UTF8Encoding().GetBytes(v); - ms.Write(buf, 0, buf.Length); - ms.WriteByte(0); + byte buf = reader.ReadByte(); + if( buf == 0 ) + break; + ms.WriteByte(buf); } + return Encoding.UTF8.GetString(ms.ToArray(), 0, (int)ms.Position); + } - private void EncodeString(MemoryStream ms, string v) - { - byte[] strBuf = new UTF8Encoding().GetBytes(v); - byte[] buf = BitConverter.GetBytes(strBuf.Length + 1); - ms.Write(buf, 0, buf.Length); - ms.Write(strBuf, 0, strBuf.Length); - ms.WriteByte(0); - } + #region ENCODING + + private Bson() + { + stream = new MemoryStream(); + } + + public static byte[] Serialize(BObject obj) + { + var bson = new Bson(); + var ms = new MemoryStream(); + + bson.EncodeDocument(ms, obj); + + byte[] buf = new byte[ms.Position]; + ms.Seek(0, SeekOrigin.Begin); + ms.Read(buf, 0, buf.Length); + + return buf; + } - private void EncodeDouble(MemoryStream ms, double v) + private void EncodeElement(MemoryStream ms, string name, BValue v) + { + switch( v.ValueType ) { - byte[] buf = BitConverter.GetBytes(v); - ms.Write(buf, 0, buf.Length); + case BValueType.Double: + ms.WriteByte(0x01); + EncodeCString(ms, name); + EncodeDouble(ms, v.DoubleValue); + return; + case BValueType.String: + ms.WriteByte(0x02); + EncodeCString(ms, name); + EncodeString(ms, v.StringValue); + return; + case BValueType.Object: + ms.WriteByte(0x03); + EncodeCString(ms, name); + EncodeDocument(ms, v as BObject); + return; + case BValueType.Array: + ms.WriteByte(0x04); + EncodeCString(ms, name); + EncodeArray(ms, v as BArray); + return; + case BValueType.Binary: + ms.WriteByte(0x05); + EncodeCString(ms, name); + EncodeBinary(ms, v.BinaryValue); + return; + case BValueType.Boolean: + ms.WriteByte(0x08); + EncodeCString(ms, name); + EncodeBool(ms, v.BoolValue); + return; + case BValueType.UTCDateTime: + ms.WriteByte(0x09); + EncodeCString(ms, name); + EncodeUTCDateTime(ms, v.DateTimeValue); + return; + case BValueType.None: + ms.WriteByte(0x0A); + EncodeCString(ms, name); + return; + case BValueType.Int32: + ms.WriteByte(0x10); + EncodeCString(ms, name); + EncodeInt32(ms, v.Int32Value); + return; + case BValueType.Int64: + ms.WriteByte(0x12); + EncodeCString(ms, name); + EncodeInt64(ms, v.Int64Value); + return; } + } - private void EncodeBool(MemoryStream ms, bool v) + private void EncodeDocument(MemoryStream ms, BObject obj) + { + var dms = new MemoryStream(); + foreach( string str in obj.Keys ) { - byte[] buf = BitConverter.GetBytes(v); - ms.Write(buf, 0, buf.Length); + EncodeElement(dms, str, obj[str]); } - private void EncodeInt32(MemoryStream ms, Int32 v) + var bw = new BinaryWriter(ms); + bw.Write((Int32)(dms.Position + 4 + 1)); + bw.Write(dms.ToArray(), 0, (int)dms.Position); + bw.Write((byte)0); + } + + private void EncodeArray(MemoryStream ms, BArray lst) + { + var obj = new BObject(); + for( int i = 0; i < lst.Count; ++i ) { - byte[] buf = BitConverter.GetBytes(v); - ms.Write(buf, 0, buf.Length); + obj.Add(Convert.ToString(i), lst[i]); } - private void EncodeInt64(MemoryStream ms, Int64 v) + EncodeDocument(ms, obj); + } + + private void EncodeBinary(MemoryStream ms, byte[] buf) + { + byte[] aBuf = BitConverter.GetBytes(buf.Length); + ms.Write(aBuf, 0, aBuf.Length); + ms.WriteByte(0); + ms.Write(buf, 0, buf.Length); + } + + private void EncodeCString(MemoryStream ms, string v) + { + byte[] buf = new UTF8Encoding().GetBytes(v); + ms.Write(buf, 0, buf.Length); + ms.WriteByte(0); + } + + private void EncodeString(MemoryStream ms, string v) + { + byte[] strBuf = new UTF8Encoding().GetBytes(v); + byte[] buf = BitConverter.GetBytes(strBuf.Length + 1); + + ms.Write(buf, 0, buf.Length); + ms.Write(strBuf, 0, strBuf.Length); + ms.WriteByte(0); + } + + private void EncodeDouble(MemoryStream ms, double v) + { + byte[] buf = BitConverter.GetBytes(v); + ms.Write(buf, 0, buf.Length); + } + + private void EncodeBool(MemoryStream ms, bool v) + { + byte[] buf = BitConverter.GetBytes(v); + ms.Write(buf, 0, buf.Length); + } + + private void EncodeInt32(MemoryStream ms, Int32 v) + { + byte[] buf = BitConverter.GetBytes(v); + ms.Write(buf, 0, buf.Length); + } + + private void EncodeInt64(MemoryStream ms, Int64 v) + { + byte[] buf = BitConverter.GetBytes(v); + ms.Write(buf, 0, buf.Length); + } + + private void EncodeUTCDateTime(MemoryStream ms, DateTime dt) + { + TimeSpan span; + if( dt.Kind == DateTimeKind.Local ) { - byte[] buf = BitConverter.GetBytes(v); - ms.Write(buf, 0, buf.Length); + span = (dt - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()); } - - private void EncodeUTCDateTime(MemoryStream ms, DateTime dt) + else { - TimeSpan span; - if( dt.Kind == DateTimeKind.Local ) - { - span = (dt - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()); - } - else - { - span = dt - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - } - byte[] buf = BitConverter.GetBytes((Int64)(span.TotalSeconds * 1000)); - ms.Write(buf, 0, buf.Length); + span = dt - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); } - #endregion + byte[] buf = BitConverter.GetBytes((Int64)(span.TotalSeconds * 1000)); + ms.Write(buf, 0, buf.Length); } + #endregion } diff --git a/Source/Bogus/Chars.cs b/Source/Bogus/Chars.cs index 1b847c2b..374cce63 100644 --- a/Source/Bogus/Chars.cs +++ b/Source/Bogus/Chars.cs @@ -1,423 +1,422 @@ using System.ComponentModel; -namespace Bogus +namespace Bogus; + +/// +/// Static class for holding character string constants. +/// +public static class Chars { /// - /// Static class for holding character string constants. + /// Lower case, a-z. /// - public static class Chars - { - /// - /// Lower case, a-z. - /// - public const string LowerCase = "abcdefghijklmnopqrstuvwxyz"; + public const string LowerCase = "abcdefghijklmnopqrstuvwxyz"; - /// - /// Upper case, A-Z. - /// - public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /// + /// Upper case, A-Z. + /// + public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - /// - /// Numbers, 0-9. - /// - public const string Numbers = "0123456789"; + /// + /// Numbers, 0-9. + /// + public const string Numbers = "0123456789"; - /// - /// Hexadecimal, 0-9 and a-f. - /// - public const string HexLowerCase = Numbers + "abcdef"; + /// + /// Hexadecimal, 0-9 and a-f. + /// + public const string HexLowerCase = Numbers + "abcdef"; - /// - /// Hexadecimal, 0-9 and A-F. - /// - public const string HexUpperCase = Numbers + "ABCDEF"; + /// + /// Hexadecimal, 0-9 and A-F. + /// + public const string HexUpperCase = Numbers + "ABCDEF"; - /// - /// Alphanumeric upper case 0-9 and A-Z. - /// - public const string AlphaNumericUpperCase = Numbers + UpperCase; - - /// - /// Alphanumeric lower case 0-9 and a-z. - /// - public const string AlphaNumericLowerCase = Numbers + LowerCase; - } + /// + /// Alphanumeric upper case 0-9 and A-Z. + /// + public const string AlphaNumericUpperCase = Numbers + UpperCase; + + /// + /// Alphanumeric lower case 0-9 and a-z. + /// + public const string AlphaNumericLowerCase = Numbers + LowerCase; +} +/// +/// Contains information about contiguous blocks of unicode character ranges. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class SafeUnicodeRanges +{ /// - /// Contains information about contiguous blocks of unicode character ranges. + /// Contiguous blocks of unicode scalars that are not surrogates and where + /// char.IsLetterOrDigit is true. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public static class SafeUnicodeRanges - { - /// - /// Contiguous blocks of unicode scalars that are not surrogates and where - /// char.IsLetterOrDigit is true. - /// - public static string[] Basic = { - "\u0030-\u0039", - "\u0041-\u005A", - "\u0061-\u007A", - "\u00C0-\u00D6", - "\u00D8-\u00F6", - "\u00F8-\u02C1", - "\u02C6-\u02D1", - "\u02E0-\u02E4", - "\u037A-\u037D", - "\u0388-\u038A", - "\u038E-\u03A1", - "\u03A3-\u03CE", - "\u03D0-\u03F5", - "\u03F7-\u0481", - "\u048A-\u0513", - "\u0531-\u0556", - "\u0561-\u0587", - "\u05D0-\u05EA", - "\u05F0-\u05F2", - "\u0621-\u063A", - "\u0640-\u064A", - "\u0660-\u0669", - "\u066E-\u066F", - "\u0671-\u06D3", - "\u06E5-\u06E6", - "\u06EE-\u06FC", - "\u0712-\u072F", - "\u074D-\u076D", - "\u0780-\u07A5", - "\u07C0-\u07EA", - "\u07F4-\u07F5", - "\u0904-\u0939", - "\u0958-\u0961", - "\u0966-\u096F", - "\u097B-\u097F", - "\u0985-\u098C", - "\u098F-\u0990", - "\u0993-\u09A8", - "\u09AA-\u09B0", - "\u09B6-\u09B9", - "\u09DC-\u09DD", - "\u09DF-\u09E1", - "\u09E6-\u09F1", - "\u0A05-\u0A0A", - "\u0A0F-\u0A10", - "\u0A13-\u0A28", - "\u0A2A-\u0A30", - "\u0A32-\u0A33", - "\u0A35-\u0A36", - "\u0A38-\u0A39", - "\u0A59-\u0A5C", - "\u0A66-\u0A6F", - "\u0A72-\u0A74", - "\u0A85-\u0A8D", - "\u0A8F-\u0A91", - "\u0A93-\u0AA8", - "\u0AAA-\u0AB0", - "\u0AB2-\u0AB3", - "\u0AB5-\u0AB9", - "\u0AE0-\u0AE1", - "\u0AE6-\u0AEF", - "\u0B05-\u0B0C", - "\u0B0F-\u0B10", - "\u0B13-\u0B28", - "\u0B2A-\u0B30", - "\u0B32-\u0B33", - "\u0B35-\u0B39", - "\u0B5C-\u0B5D", - "\u0B5F-\u0B61", - "\u0B66-\u0B6F", - "\u0B85-\u0B8A", - "\u0B8E-\u0B90", - "\u0B92-\u0B95", - "\u0B99-\u0B9A", - "\u0B9E-\u0B9F", - "\u0BA3-\u0BA4", - "\u0BA8-\u0BAA", - "\u0BAE-\u0BB9", - "\u0BE6-\u0BEF", - "\u0C05-\u0C0C", - "\u0C0E-\u0C10", - "\u0C12-\u0C28", - "\u0C2A-\u0C33", - "\u0C35-\u0C39", - "\u0C60-\u0C61", - "\u0C66-\u0C6F", - "\u0C85-\u0C8C", - "\u0C8E-\u0C90", - "\u0C92-\u0CA8", - "\u0CAA-\u0CB3", - "\u0CB5-\u0CB9", - "\u0CE0-\u0CE1", - "\u0CE6-\u0CEF", - "\u0CF1-\u0CF2", - "\u0D05-\u0D0C", - "\u0D0E-\u0D10", - "\u0D12-\u0D28", - "\u0D2A-\u0D39", - "\u0D60-\u0D61", - "\u0D66-\u0D6F", - "\u0D85-\u0D96", - "\u0D9A-\u0DB1", - "\u0DB3-\u0DBB", - "\u0DC0-\u0DC6", - "\u0E01-\u0E30", - "\u0E32-\u0E33", - "\u0E40-\u0E46", - "\u0E50-\u0E59", - "\u0E81-\u0E82", - "\u0E87-\u0E88", - "\u0E94-\u0E97", - "\u0E99-\u0E9F", - "\u0EA1-\u0EA3", - "\u0EAA-\u0EAB", - "\u0EAD-\u0EB0", - "\u0EB2-\u0EB3", - "\u0EC0-\u0EC4", - "\u0ED0-\u0ED9", - "\u0EDC-\u0EDD", - "\u0F20-\u0F29", - "\u0F40-\u0F47", - "\u0F49-\u0F6A", - "\u0F88-\u0F8B", - "\u1000-\u1021", - "\u1023-\u1027", - "\u1029-\u102A", - "\u1040-\u1049", - "\u1050-\u1055", - "\u10A0-\u10C5", - "\u10D0-\u10FA", - "\u1100-\u1159", - "\u115F-\u11A2", - "\u11A8-\u11F9", - "\u1200-\u1248", - "\u124A-\u124D", - "\u1250-\u1256", - "\u125A-\u125D", - "\u1260-\u1288", - "\u128A-\u128D", - "\u1290-\u12B0", - "\u12B2-\u12B5", - "\u12B8-\u12BE", - "\u12C2-\u12C5", - "\u12C8-\u12D6", - "\u12D8-\u1310", - "\u1312-\u1315", - "\u1318-\u135A", - "\u1380-\u138F", - "\u13A0-\u13F4", - "\u1401-\u166C", - "\u166F-\u1676", - "\u1681-\u169A", - "\u16A0-\u16EA", - "\u1700-\u170C", - "\u170E-\u1711", - "\u1720-\u1731", - "\u1740-\u1751", - "\u1760-\u176C", - "\u176E-\u1770", - "\u1780-\u17B3", - "\u17E0-\u17E9", - "\u1810-\u1819", - "\u1820-\u1877", - "\u1880-\u18A8", - "\u1900-\u191C", - "\u1946-\u196D", - "\u1970-\u1974", - "\u1980-\u19A9", - "\u19B0-\u19C9", - "\u19D0-\u19D9", - "\u1A00-\u1A16", - "\u1B05-\u1B33", - "\u1B45-\u1B4B", - "\u1B50-\u1B59", - "\u1D00-\u1DBF", - "\u1E00-\u1E9B", - "\u1EA0-\u1EF9", - "\u1F00-\u1F15", - "\u1F18-\u1F1D", - "\u1F20-\u1F45", - "\u1F48-\u1F4D", - "\u1F50-\u1F57", - "\u1F5F-\u1F7D", - "\u1F80-\u1FB4", - "\u1FB6-\u1FBC", - "\u1FC2-\u1FC4", - "\u1FC6-\u1FCC", - "\u1FD0-\u1FD3", - "\u1FD6-\u1FDB", - "\u1FE0-\u1FEC", - "\u1FF2-\u1FF4", - "\u1FF6-\u1FFC", - "\u2090-\u2094", - "\u210A-\u2113", - "\u2119-\u211D", - "\u212A-\u212D", - "\u212F-\u2139", - "\u213C-\u213F", - "\u2145-\u2149", - "\u2183-\u2184", - "\u2C00-\u2C2E", - "\u2C30-\u2C5E", - "\u2C60-\u2C6C", - "\u2C74-\u2C77", - "\u2C80-\u2CE4", - "\u2D00-\u2D25", - "\u2D30-\u2D65", - "\u2D80-\u2D96", - "\u2DA0-\u2DA6", - "\u2DA8-\u2DAE", - "\u2DB0-\u2DB6", - "\u2DB8-\u2DBE", - "\u2DC0-\u2DC6", - "\u2DC8-\u2DCE", - "\u2DD0-\u2DD6", - "\u2DD8-\u2DDE", - "\u3005-\u3006", - "\u3031-\u3035", - "\u303B-\u303C", - "\u3041-\u3096", - "\u309D-\u309F", - "\u30A1-\u30FA", - "\u30FC-\u30FF", - "\u3105-\u312C", - "\u3131-\u318E", - "\u31A0-\u31B7", - "\u31F0-\u31FF", - "\u3400-\u4DB5", - "\u4E00-\u9FBB", - "\uA000-\uA48C", - "\uA717-\uA71A", - "\uA800-\uA801", - "\uA803-\uA805", - "\uA807-\uA80A", - "\uA80C-\uA822", - "\uA840-\uA873", - "\uAC00-\uD7A3", - "\uF900-\uFA2D", - "\uFA30-\uFA6A", - "\uFA70-\uFAD9", - "\uFB00-\uFB06", - "\uFB13-\uFB17", - "\uFB1F-\uFB28", - "\uFB2A-\uFB36", - "\uFB38-\uFB3C", - "\uFB40-\uFB41", - "\uFB43-\uFB44", - "\uFB46-\uFBB1", - "\uFBD3-\uFD3D", - "\uFD50-\uFD8F", - "\uFD92-\uFDC7", - "\uFDF0-\uFDFB", - "\uFE70-\uFE74", - "\uFE76-\uFEFC", - "\uFF10-\uFF19", - "\uFF21-\uFF3A", - "\uFF41-\uFF5A", - "\uFF66-\uFFBE", - "\uFFC2-\uFFC7", - "\uFFCA-\uFFCF", - "\uFFD2-\uFFD7", - "\uFFDA-\uFFDC", - }; + public static string[] Basic = { + "\u0030-\u0039", + "\u0041-\u005A", + "\u0061-\u007A", + "\u00C0-\u00D6", + "\u00D8-\u00F6", + "\u00F8-\u02C1", + "\u02C6-\u02D1", + "\u02E0-\u02E4", + "\u037A-\u037D", + "\u0388-\u038A", + "\u038E-\u03A1", + "\u03A3-\u03CE", + "\u03D0-\u03F5", + "\u03F7-\u0481", + "\u048A-\u0513", + "\u0531-\u0556", + "\u0561-\u0587", + "\u05D0-\u05EA", + "\u05F0-\u05F2", + "\u0621-\u063A", + "\u0640-\u064A", + "\u0660-\u0669", + "\u066E-\u066F", + "\u0671-\u06D3", + "\u06E5-\u06E6", + "\u06EE-\u06FC", + "\u0712-\u072F", + "\u074D-\u076D", + "\u0780-\u07A5", + "\u07C0-\u07EA", + "\u07F4-\u07F5", + "\u0904-\u0939", + "\u0958-\u0961", + "\u0966-\u096F", + "\u097B-\u097F", + "\u0985-\u098C", + "\u098F-\u0990", + "\u0993-\u09A8", + "\u09AA-\u09B0", + "\u09B6-\u09B9", + "\u09DC-\u09DD", + "\u09DF-\u09E1", + "\u09E6-\u09F1", + "\u0A05-\u0A0A", + "\u0A0F-\u0A10", + "\u0A13-\u0A28", + "\u0A2A-\u0A30", + "\u0A32-\u0A33", + "\u0A35-\u0A36", + "\u0A38-\u0A39", + "\u0A59-\u0A5C", + "\u0A66-\u0A6F", + "\u0A72-\u0A74", + "\u0A85-\u0A8D", + "\u0A8F-\u0A91", + "\u0A93-\u0AA8", + "\u0AAA-\u0AB0", + "\u0AB2-\u0AB3", + "\u0AB5-\u0AB9", + "\u0AE0-\u0AE1", + "\u0AE6-\u0AEF", + "\u0B05-\u0B0C", + "\u0B0F-\u0B10", + "\u0B13-\u0B28", + "\u0B2A-\u0B30", + "\u0B32-\u0B33", + "\u0B35-\u0B39", + "\u0B5C-\u0B5D", + "\u0B5F-\u0B61", + "\u0B66-\u0B6F", + "\u0B85-\u0B8A", + "\u0B8E-\u0B90", + "\u0B92-\u0B95", + "\u0B99-\u0B9A", + "\u0B9E-\u0B9F", + "\u0BA3-\u0BA4", + "\u0BA8-\u0BAA", + "\u0BAE-\u0BB9", + "\u0BE6-\u0BEF", + "\u0C05-\u0C0C", + "\u0C0E-\u0C10", + "\u0C12-\u0C28", + "\u0C2A-\u0C33", + "\u0C35-\u0C39", + "\u0C60-\u0C61", + "\u0C66-\u0C6F", + "\u0C85-\u0C8C", + "\u0C8E-\u0C90", + "\u0C92-\u0CA8", + "\u0CAA-\u0CB3", + "\u0CB5-\u0CB9", + "\u0CE0-\u0CE1", + "\u0CE6-\u0CEF", + "\u0CF1-\u0CF2", + "\u0D05-\u0D0C", + "\u0D0E-\u0D10", + "\u0D12-\u0D28", + "\u0D2A-\u0D39", + "\u0D60-\u0D61", + "\u0D66-\u0D6F", + "\u0D85-\u0D96", + "\u0D9A-\u0DB1", + "\u0DB3-\u0DBB", + "\u0DC0-\u0DC6", + "\u0E01-\u0E30", + "\u0E32-\u0E33", + "\u0E40-\u0E46", + "\u0E50-\u0E59", + "\u0E81-\u0E82", + "\u0E87-\u0E88", + "\u0E94-\u0E97", + "\u0E99-\u0E9F", + "\u0EA1-\u0EA3", + "\u0EAA-\u0EAB", + "\u0EAD-\u0EB0", + "\u0EB2-\u0EB3", + "\u0EC0-\u0EC4", + "\u0ED0-\u0ED9", + "\u0EDC-\u0EDD", + "\u0F20-\u0F29", + "\u0F40-\u0F47", + "\u0F49-\u0F6A", + "\u0F88-\u0F8B", + "\u1000-\u1021", + "\u1023-\u1027", + "\u1029-\u102A", + "\u1040-\u1049", + "\u1050-\u1055", + "\u10A0-\u10C5", + "\u10D0-\u10FA", + "\u1100-\u1159", + "\u115F-\u11A2", + "\u11A8-\u11F9", + "\u1200-\u1248", + "\u124A-\u124D", + "\u1250-\u1256", + "\u125A-\u125D", + "\u1260-\u1288", + "\u128A-\u128D", + "\u1290-\u12B0", + "\u12B2-\u12B5", + "\u12B8-\u12BE", + "\u12C2-\u12C5", + "\u12C8-\u12D6", + "\u12D8-\u1310", + "\u1312-\u1315", + "\u1318-\u135A", + "\u1380-\u138F", + "\u13A0-\u13F4", + "\u1401-\u166C", + "\u166F-\u1676", + "\u1681-\u169A", + "\u16A0-\u16EA", + "\u1700-\u170C", + "\u170E-\u1711", + "\u1720-\u1731", + "\u1740-\u1751", + "\u1760-\u176C", + "\u176E-\u1770", + "\u1780-\u17B3", + "\u17E0-\u17E9", + "\u1810-\u1819", + "\u1820-\u1877", + "\u1880-\u18A8", + "\u1900-\u191C", + "\u1946-\u196D", + "\u1970-\u1974", + "\u1980-\u19A9", + "\u19B0-\u19C9", + "\u19D0-\u19D9", + "\u1A00-\u1A16", + "\u1B05-\u1B33", + "\u1B45-\u1B4B", + "\u1B50-\u1B59", + "\u1D00-\u1DBF", + "\u1E00-\u1E9B", + "\u1EA0-\u1EF9", + "\u1F00-\u1F15", + "\u1F18-\u1F1D", + "\u1F20-\u1F45", + "\u1F48-\u1F4D", + "\u1F50-\u1F57", + "\u1F5F-\u1F7D", + "\u1F80-\u1FB4", + "\u1FB6-\u1FBC", + "\u1FC2-\u1FC4", + "\u1FC6-\u1FCC", + "\u1FD0-\u1FD3", + "\u1FD6-\u1FDB", + "\u1FE0-\u1FEC", + "\u1FF2-\u1FF4", + "\u1FF6-\u1FFC", + "\u2090-\u2094", + "\u210A-\u2113", + "\u2119-\u211D", + "\u212A-\u212D", + "\u212F-\u2139", + "\u213C-\u213F", + "\u2145-\u2149", + "\u2183-\u2184", + "\u2C00-\u2C2E", + "\u2C30-\u2C5E", + "\u2C60-\u2C6C", + "\u2C74-\u2C77", + "\u2C80-\u2CE4", + "\u2D00-\u2D25", + "\u2D30-\u2D65", + "\u2D80-\u2D96", + "\u2DA0-\u2DA6", + "\u2DA8-\u2DAE", + "\u2DB0-\u2DB6", + "\u2DB8-\u2DBE", + "\u2DC0-\u2DC6", + "\u2DC8-\u2DCE", + "\u2DD0-\u2DD6", + "\u2DD8-\u2DDE", + "\u3005-\u3006", + "\u3031-\u3035", + "\u303B-\u303C", + "\u3041-\u3096", + "\u309D-\u309F", + "\u30A1-\u30FA", + "\u30FC-\u30FF", + "\u3105-\u312C", + "\u3131-\u318E", + "\u31A0-\u31B7", + "\u31F0-\u31FF", + "\u3400-\u4DB5", + "\u4E00-\u9FBB", + "\uA000-\uA48C", + "\uA717-\uA71A", + "\uA800-\uA801", + "\uA803-\uA805", + "\uA807-\uA80A", + "\uA80C-\uA822", + "\uA840-\uA873", + "\uAC00-\uD7A3", + "\uF900-\uFA2D", + "\uFA30-\uFA6A", + "\uFA70-\uFAD9", + "\uFB00-\uFB06", + "\uFB13-\uFB17", + "\uFB1F-\uFB28", + "\uFB2A-\uFB36", + "\uFB38-\uFB3C", + "\uFB40-\uFB41", + "\uFB43-\uFB44", + "\uFB46-\uFBB1", + "\uFBD3-\uFD3D", + "\uFD50-\uFD8F", + "\uFD92-\uFDC7", + "\uFDF0-\uFDFB", + "\uFE70-\uFE74", + "\uFE76-\uFEFC", + "\uFF10-\uFF19", + "\uFF21-\uFF3A", + "\uFF41-\uFF5A", + "\uFF66-\uFFBE", + "\uFFC2-\uFFC7", + "\uFFCA-\uFFCF", + "\uFFD2-\uFFD7", + "\uFFDA-\uFFDC", + }; - /// - /// Contiguous blocks of unicode surrogate pairs and where - /// char.IsLetterOrDigit is true. - /// - public static string[] SurrogatePairs = - { - "\uD800\uDC00-\uD800\uDC0B", - "\uD800\uDC0D-\uD800\uDC26", - "\uD800\uDC28-\uD800\uDC3A", - "\uD800\uDC3C-\uD800\uDC3D", - "\uD800\uDC3F-\uD800\uDC4D", - "\uD800\uDC50-\uD800\uDC5D", - "\uD800\uDC80-\uD800\uDCFA", - "\uD800\uDF00-\uD800\uDF1E", - "\uD800\uDF30-\uD800\uDF40", - "\uD800\uDF42-\uD800\uDF49", - "\uD800\uDF80-\uD800\uDF9D", - "\uD800\uDFA0-\uD800\uDFC3", - "\uD800\uDFC8-\uD800\uDFCF", - "\uD801\uDC00-\uD801\uDC9D", - "\uD801\uDCA0-\uD801\uDCA9", - "\uD802\uDC00-\uD802\uDC05", - "\uD802\uDC0A-\uD802\uDC35", - "\uD802\uDC37-\uD802\uDC38", - "\uD802\uDD00-\uD802\uDD15", - "\uD802\uDE10-\uD802\uDE13", - "\uD802\uDE15-\uD802\uDE17", - "\uD802\uDE19-\uD802\uDE33", - "\uD808\uDC00-\uD808\uDF6E", - "\uD835\uDC00-\uD835\uDC54", - "\uD835\uDC56-\uD835\uDC9C", - "\uD835\uDC9E-\uD835\uDC9F", - "\uD835\uDCA5-\uD835\uDCA6", - "\uD835\uDCA9-\uD835\uDCAC", - "\uD835\uDCAE-\uD835\uDCB9", - "\uD835\uDCBD-\uD835\uDCC3", - "\uD835\uDCC5-\uD835\uDD05", - "\uD835\uDD07-\uD835\uDD0A", - "\uD835\uDD0D-\uD835\uDD14", - "\uD835\uDD16-\uD835\uDD1C", - "\uD835\uDD1E-\uD835\uDD39", - "\uD835\uDD3B-\uD835\uDD3E", - "\uD835\uDD40-\uD835\uDD44", - "\uD835\uDD4A-\uD835\uDD50", - "\uD835\uDD52-\uD835\uDEA5", - "\uD835\uDEA8-\uD835\uDEC0", - "\uD835\uDEC2-\uD835\uDEDA", - "\uD835\uDEDC-\uD835\uDEFA", - "\uD835\uDEFC-\uD835\uDF14", - "\uD835\uDF16-\uD835\uDF34", - "\uD835\uDF36-\uD835\uDF4E", - "\uD835\uDF50-\uD835\uDF6E", - "\uD835\uDF70-\uD835\uDF88", - "\uD835\uDF8A-\uD835\uDFA8", - "\uD835\uDFAA-\uD835\uDFC2", - "\uD835\uDFC4-\uD835\uDFCB", - "\uD835\uDFCE-\uD835\uDFFF", - "\uD840\uDC00-\uD840\uDFFF", - "\uD841\uDC00-\uD841\uDFFF", - "\uD842\uDC00-\uD842\uDFFF", - "\uD843\uDC00-\uD843\uDFFF", - "\uD844\uDC00-\uD844\uDFFF", - "\uD845\uDC00-\uD845\uDFFF", - "\uD846\uDC00-\uD846\uDFFF", - "\uD847\uDC00-\uD847\uDFFF", - "\uD848\uDC00-\uD848\uDFFF", - "\uD849\uDC00-\uD849\uDFFF", - "\uD84A\uDC00-\uD84A\uDFFF", - "\uD84B\uDC00-\uD84B\uDFFF", - "\uD84C\uDC00-\uD84C\uDFFF", - "\uD84D\uDC00-\uD84D\uDFFF", - "\uD84E\uDC00-\uD84E\uDFFF", - "\uD84F\uDC00-\uD84F\uDFFF", - "\uD850\uDC00-\uD850\uDFFF", - "\uD851\uDC00-\uD851\uDFFF", - "\uD852\uDC00-\uD852\uDFFF", - "\uD853\uDC00-\uD853\uDFFF", - "\uD854\uDC00-\uD854\uDFFF", - "\uD855\uDC00-\uD855\uDFFF", - "\uD856\uDC00-\uD856\uDFFF", - "\uD857\uDC00-\uD857\uDFFF", - "\uD858\uDC00-\uD858\uDFFF", - "\uD859\uDC00-\uD859\uDFFF", - "\uD85A\uDC00-\uD85A\uDFFF", - "\uD85B\uDC00-\uD85B\uDFFF", - "\uD85C\uDC00-\uD85C\uDFFF", - "\uD85D\uDC00-\uD85D\uDFFF", - "\uD85E\uDC00-\uD85E\uDFFF", - "\uD85F\uDC00-\uD85F\uDFFF", - "\uD860\uDC00-\uD860\uDFFF", - "\uD861\uDC00-\uD861\uDFFF", - "\uD862\uDC00-\uD862\uDFFF", - "\uD863\uDC00-\uD863\uDFFF", - "\uD864\uDC00-\uD864\uDFFF", - "\uD865\uDC00-\uD865\uDFFF", - "\uD866\uDC00-\uD866\uDFFF", - "\uD867\uDC00-\uD867\uDFFF", - "\uD868\uDC00-\uD868\uDFFF", - "\uD869\uDC00-\uD869\uDED6" - }; - } + /// + /// Contiguous blocks of unicode surrogate pairs and where + /// char.IsLetterOrDigit is true. + /// + public static string[] SurrogatePairs = + { + "\uD800\uDC00-\uD800\uDC0B", + "\uD800\uDC0D-\uD800\uDC26", + "\uD800\uDC28-\uD800\uDC3A", + "\uD800\uDC3C-\uD800\uDC3D", + "\uD800\uDC3F-\uD800\uDC4D", + "\uD800\uDC50-\uD800\uDC5D", + "\uD800\uDC80-\uD800\uDCFA", + "\uD800\uDF00-\uD800\uDF1E", + "\uD800\uDF30-\uD800\uDF40", + "\uD800\uDF42-\uD800\uDF49", + "\uD800\uDF80-\uD800\uDF9D", + "\uD800\uDFA0-\uD800\uDFC3", + "\uD800\uDFC8-\uD800\uDFCF", + "\uD801\uDC00-\uD801\uDC9D", + "\uD801\uDCA0-\uD801\uDCA9", + "\uD802\uDC00-\uD802\uDC05", + "\uD802\uDC0A-\uD802\uDC35", + "\uD802\uDC37-\uD802\uDC38", + "\uD802\uDD00-\uD802\uDD15", + "\uD802\uDE10-\uD802\uDE13", + "\uD802\uDE15-\uD802\uDE17", + "\uD802\uDE19-\uD802\uDE33", + "\uD808\uDC00-\uD808\uDF6E", + "\uD835\uDC00-\uD835\uDC54", + "\uD835\uDC56-\uD835\uDC9C", + "\uD835\uDC9E-\uD835\uDC9F", + "\uD835\uDCA5-\uD835\uDCA6", + "\uD835\uDCA9-\uD835\uDCAC", + "\uD835\uDCAE-\uD835\uDCB9", + "\uD835\uDCBD-\uD835\uDCC3", + "\uD835\uDCC5-\uD835\uDD05", + "\uD835\uDD07-\uD835\uDD0A", + "\uD835\uDD0D-\uD835\uDD14", + "\uD835\uDD16-\uD835\uDD1C", + "\uD835\uDD1E-\uD835\uDD39", + "\uD835\uDD3B-\uD835\uDD3E", + "\uD835\uDD40-\uD835\uDD44", + "\uD835\uDD4A-\uD835\uDD50", + "\uD835\uDD52-\uD835\uDEA5", + "\uD835\uDEA8-\uD835\uDEC0", + "\uD835\uDEC2-\uD835\uDEDA", + "\uD835\uDEDC-\uD835\uDEFA", + "\uD835\uDEFC-\uD835\uDF14", + "\uD835\uDF16-\uD835\uDF34", + "\uD835\uDF36-\uD835\uDF4E", + "\uD835\uDF50-\uD835\uDF6E", + "\uD835\uDF70-\uD835\uDF88", + "\uD835\uDF8A-\uD835\uDFA8", + "\uD835\uDFAA-\uD835\uDFC2", + "\uD835\uDFC4-\uD835\uDFCB", + "\uD835\uDFCE-\uD835\uDFFF", + "\uD840\uDC00-\uD840\uDFFF", + "\uD841\uDC00-\uD841\uDFFF", + "\uD842\uDC00-\uD842\uDFFF", + "\uD843\uDC00-\uD843\uDFFF", + "\uD844\uDC00-\uD844\uDFFF", + "\uD845\uDC00-\uD845\uDFFF", + "\uD846\uDC00-\uD846\uDFFF", + "\uD847\uDC00-\uD847\uDFFF", + "\uD848\uDC00-\uD848\uDFFF", + "\uD849\uDC00-\uD849\uDFFF", + "\uD84A\uDC00-\uD84A\uDFFF", + "\uD84B\uDC00-\uD84B\uDFFF", + "\uD84C\uDC00-\uD84C\uDFFF", + "\uD84D\uDC00-\uD84D\uDFFF", + "\uD84E\uDC00-\uD84E\uDFFF", + "\uD84F\uDC00-\uD84F\uDFFF", + "\uD850\uDC00-\uD850\uDFFF", + "\uD851\uDC00-\uD851\uDFFF", + "\uD852\uDC00-\uD852\uDFFF", + "\uD853\uDC00-\uD853\uDFFF", + "\uD854\uDC00-\uD854\uDFFF", + "\uD855\uDC00-\uD855\uDFFF", + "\uD856\uDC00-\uD856\uDFFF", + "\uD857\uDC00-\uD857\uDFFF", + "\uD858\uDC00-\uD858\uDFFF", + "\uD859\uDC00-\uD859\uDFFF", + "\uD85A\uDC00-\uD85A\uDFFF", + "\uD85B\uDC00-\uD85B\uDFFF", + "\uD85C\uDC00-\uD85C\uDFFF", + "\uD85D\uDC00-\uD85D\uDFFF", + "\uD85E\uDC00-\uD85E\uDFFF", + "\uD85F\uDC00-\uD85F\uDFFF", + "\uD860\uDC00-\uD860\uDFFF", + "\uD861\uDC00-\uD861\uDFFF", + "\uD862\uDC00-\uD862\uDFFF", + "\uD863\uDC00-\uD863\uDFFF", + "\uD864\uDC00-\uD864\uDFFF", + "\uD865\uDC00-\uD865\uDFFF", + "\uD866\uDC00-\uD866\uDFFF", + "\uD867\uDC00-\uD867\uDFFF", + "\uD868\uDC00-\uD868\uDFFF", + "\uD869\uDC00-\uD869\uDED6" + }; } \ No newline at end of file diff --git a/Source/Bogus/DataCategoryAttribute.cs b/Source/Bogus/DataCategoryAttribute.cs index 2ec1582a..6b7929e6 100644 --- a/Source/Bogus/DataCategoryAttribute.cs +++ b/Source/Bogus/DataCategoryAttribute.cs @@ -1,28 +1,27 @@ using System; -namespace Bogus +namespace Bogus; + +/// +/// DataCategory is used when resolving the final category name inside the locale. +/// For example, a 'phone_numbers' is the data set name in a locale, but the +/// C# style DataSet is PhoneNumbers; When a dataset is marked with DataCategory, +/// you can specify that category name manually. If no data category is specified, +/// then the C# class name is used. +/// +[AttributeUsage(AttributeTargets.Class)] +public class DataCategoryAttribute : Attribute { /// - /// DataCategory is used when resolving the final category name inside the locale. - /// For example, a 'phone_numbers' is the data set name in a locale, but the - /// C# style DataSet is PhoneNumbers; When a dataset is marked with DataCategory, - /// you can specify that category name manually. If no data category is specified, - /// then the C# class name is used. + /// The category name. /// - [AttributeUsage(AttributeTargets.Class)] - public class DataCategoryAttribute : Attribute - { - /// - /// The category name. - /// - public string Name { get; set; } + public string Name { get; set; } - /// - /// Creates a data category attribute with a specified category name. - /// - public DataCategoryAttribute(string name) - { - this.Name = name; - } + /// + /// Creates a data category attribute with a specified category name. + /// + public DataCategoryAttribute(string name) + { + this.Name = name; } } \ No newline at end of file diff --git a/Source/Bogus/DataSet.cs b/Source/Bogus/DataSet.cs index 4294b85e..6bc0f406 100644 --- a/Source/Bogus/DataSet.cs +++ b/Source/Bogus/DataSet.cs @@ -3,223 +3,222 @@ using System; using System.Text.RegularExpressions; -namespace Bogus +namespace Bogus; + +/// +/// Data set methods that access the BSON database of locales. +/// +public class DataSet : ILocaleAware, IHasRandomizer { /// - /// Data set methods that access the BSON database of locales. + /// Initializes a new instance of the class. /// - public class DataSet : ILocaleAware, IHasRandomizer + /// The locale wanting to be set. Default is "en" for English. + /// + /// When the given isn't found. + /// + public DataSet(string locale = "en") { - /// - /// Initializes a new instance of the class. - /// - /// The locale wanting to be set. Default is "en" for English. - /// - /// When the given isn't found. - /// - public DataSet(string locale = "en") + if (!Database.LocaleResourceExists(locale)) { - if (!Database.LocaleResourceExists(locale)) - { - throw new BogusException( - $"The locale '{locale}' does not exist. To see all available locales visit {AssemblyVersionInformation.AssemblyDescription}."); - } + throw new BogusException( + $"The locale '{locale}' does not exist. To see all available locales visit {AssemblyVersionInformation.AssemblyDescription}."); + } - this.Locale = locale; + this.Locale = locale; - this.Category = ResolveCategory(this.GetType()); - } + this.Category = ResolveCategory(this.GetType()); + } - /// - /// Gets or sets the category name inside the locale. - /// - protected string Category { get; set; } + /// + /// Gets or sets the category name inside the locale. + /// + protected string Category { get; set; } - /// - /// Gets or sets the current locale of the data set. - /// - public string Locale { get; set; } + /// + /// Gets or sets the current locale of the data set. + /// + public string Locale { get; set; } - /// - /// See . - /// - protected SeedNotifier Notifier = new SeedNotifier(); + /// + /// See . + /// + protected SeedNotifier Notifier = new SeedNotifier(); - private Randomizer randomizer; + private Randomizer randomizer; - /// - /// Gets or sets the used to generate values. - /// - public Randomizer Random + /// + /// Gets or sets the used to generate values. + /// + public Randomizer Random + { + get => this.randomizer ?? (this.Random = new Randomizer()); + set { - get => this.randomizer ?? (this.Random = new Randomizer()); - set - { - this.randomizer = value; - this.Notifier.Notify(value); - } + this.randomizer = value; + this.Notifier.Notify(value); } + } - SeedNotifier IHasRandomizer.GetNotifier() - { - return this.Notifier; - } + SeedNotifier IHasRandomizer.GetNotifier() + { + return this.Notifier; + } - /// - /// Resolves the 'category' type of a dataset type; respects the 'DataCategory' attribute. - /// - /// The type wanting to get the category from. - /// The name of the category. - public static string ResolveCategory(Type type) - { - var categoryAttribute = type.GetCustomAttributeX(); - return categoryAttribute != null ? categoryAttribute.Name : type.Name.ToLowerInvariant(); - } + /// + /// Resolves the 'category' type of a dataset type; respects the 'DataCategory' attribute. + /// + /// The type wanting to get the category from. + /// The name of the category. + public static string ResolveCategory(Type type) + { + var categoryAttribute = type.GetCustomAttributeX(); + return categoryAttribute != null ? categoryAttribute.Name : type.Name.ToLowerInvariant(); + } - /// - /// Returns a BSON value given a JSON path into the data set. Only simple "." dotted JSON paths are supported. - /// - /// path/key in the category - /// A BSON value for the given JSON path. - protected internal virtual BValue Get(string path) - { - return Database.Get(this.Category, path, this.Locale); - } + /// + /// Returns a BSON value given a JSON path into the data set. Only simple "." dotted JSON paths are supported. + /// + /// path/key in the category + /// A BSON value for the given JSON path. + protected internal virtual BValue Get(string path) + { + return Database.Get(this.Category, path, this.Locale); + } - /// - /// Returns a BSON value given a JSON path into the data set. Only simple "." dotted JSON paths are supported. - /// - /// Overrides the category name on the dataset. - /// path/key in the category. - /// A BSON value for the given JSON path. - protected internal virtual BValue Get(string category, string path) + /// + /// Returns a BSON value given a JSON path into the data set. Only simple "." dotted JSON paths are supported. + /// + /// Overrides the category name on the dataset. + /// path/key in the category. + /// A BSON value for the given JSON path. + protected internal virtual BValue Get(string category, string path) + { + return Database.Get(category, path, this.Locale); + } + + /// + /// Determines if a key exists in the locale. + /// + /// A boolean to indicate if the locale exists. + protected internal virtual bool HasKey(string path, bool includeFallback = true) + { + if (includeFallback) { - return Database.Get(category, path, this.Locale); + return Database.HasKey(this.Category, path, this.Locale); } - /// - /// Determines if a key exists in the locale. - /// - /// A boolean to indicate if the locale exists. - protected internal virtual bool HasKey(string path, bool includeFallback = true) - { - if (includeFallback) - { - return Database.HasKey(this.Category, path, this.Locale); - } + return Database.HasKey(this.Category, path, this.Locale, null); + } - return Database.HasKey(this.Category, path, this.Locale, null); - } + /// + /// Returns a BSON array given a JSON path into the data set. Only simple "." dotted JSON paths are supported. + /// + /// key in the category. + /// A BSON value for the given JSON path. + protected internal virtual BArray GetArray(string path) + { + return (BArray)Get(path); + } - /// - /// Returns a BSON array given a JSON path into the data set. Only simple "." dotted JSON paths are supported. - /// - /// key in the category. - /// A BSON value for the given JSON path. - protected internal virtual BArray GetArray(string path) - { - return (BArray)Get(path); - } + protected internal virtual BArray GetArray(string category, string path) + { + return (BArray)Get(category, path); + } - protected internal virtual BArray GetArray(string category, string path) - { - return (BArray)Get(category, path); - } + /// + /// Returns a BSON object given a JSON path into the data set. Only simple "." dotted JSON paths are supported. + /// + /// path/key in the category + /// A BSON value for the given JSON path. + protected internal virtual BObject GetObject(string path) + { + return (BObject)Get(path); + } + + /// + /// Picks a random string inside a BSON array. Only simple "." dotted JSON paths are supported. + /// + /// key in the category + /// The minimum value. + /// The maximum value. + /// A random item from the BSON array. + protected internal virtual string GetRandomArrayItem(string path, int? min = null, int? max = null) + { + return this.GetRandomArrayItem(this.Category, path, min, max); + } - /// - /// Returns a BSON object given a JSON path into the data set. Only simple "." dotted JSON paths are supported. - /// - /// path/key in the category - /// A BSON value for the given JSON path. - protected internal virtual BObject GetObject(string path) + protected internal virtual string GetRandomArrayItem(string category, string path, int? min = null, int? max = null) + { + var arr = GetArray(category, path); + if (!arr.HasValues) { - return (BObject)Get(path); + return string.Empty; } - /// - /// Picks a random string inside a BSON array. Only simple "." dotted JSON paths are supported. - /// - /// key in the category - /// The minimum value. - /// The maximum value. - /// A random item from the BSON array. - protected internal virtual string GetRandomArrayItem(string path, int? min = null, int? max = null) + return Random.ArrayElement(arr, min, max); + } + + /// + /// Picks a random BObject inside an array. Only simple "." dotted JSON paths are supported. + /// + /// key in the category + /// A random BObject based on the given path. + protected internal virtual BObject GetRandomBObject(string path) + { + var arr = GetArray(path); + if (!arr.HasValues) { - return this.GetRandomArrayItem(this.Category, path, min, max); + return null; } - protected internal virtual string GetRandomArrayItem(string category, string path, int? min = null, int? max = null) - { - var arr = GetArray(category, path); - if (!arr.HasValues) - { - return string.Empty; - } + return Random.ArrayElement(arr) as BObject; + } - return Random.ArrayElement(arr, min, max); - } + /// + /// Picks a random string inside a BSON array, then formats it. Only simple "." dotted JSON paths are supported. + /// + /// key in the category + /// A random formatted value. + protected internal virtual string GetFormattedValue(string path) + { + var value = GetRandomArrayItem(path); - /// - /// Picks a random BObject inside an array. Only simple "." dotted JSON paths are supported. - /// - /// key in the category - /// A random BObject based on the given path. - protected internal virtual BObject GetRandomBObject(string path) - { - var arr = GetArray(path); - if (!arr.HasValues) - { - return null; - } + var tokenResult = ParseTokens(value); - return Random.ArrayElement(arr) as BObject; - } + return Random.Replace(tokenResult); + } - /// - /// Picks a random string inside a BSON array, then formats it. Only simple "." dotted JSON paths are supported. - /// - /// key in the category - /// A random formatted value. - protected internal virtual string GetFormattedValue(string path) - { - var value = GetRandomArrayItem(path); + private static readonly Regex parseTokensRegex = new Regex("\\#{(.*?)\\}", RegexOptions.Compiled); - var tokenResult = ParseTokens(value); + /// + /// Recursive parse the tokens in the string. + /// + /// The value. + /// The parsed token. + private string ParseTokens(string value) + { + var parseResult = parseTokensRegex.Replace( + value, + x => + { + BArray result; + var groupValue = x.Groups[1].Value.ToLowerInvariant().Split('.'); + if (groupValue.Length == 1) + { + result = (BArray)Database.Get(this.Category, groupValue[0], this.Locale); + } + else + { + result = (BArray)Database.Get(groupValue[0], groupValue[1], this.Locale); + } - return Random.Replace(tokenResult); - } + var randomElement = this.Random.ArrayElement(result); - private static readonly Regex parseTokensRegex = new Regex("\\#{(.*?)\\}", RegexOptions.Compiled); + // replace values + return ParseTokens(randomElement); + }); - /// - /// Recursive parse the tokens in the string. - /// - /// The value. - /// The parsed token. - private string ParseTokens(string value) - { - var parseResult = parseTokensRegex.Replace( - value, - x => - { - BArray result; - var groupValue = x.Groups[1].Value.ToLowerInvariant().Split('.'); - if (groupValue.Length == 1) - { - result = (BArray)Database.Get(this.Category, groupValue[0], this.Locale); - } - else - { - result = (BArray)Database.Get(groupValue[0], groupValue[1], this.Locale); - } - - var randomElement = this.Random.ArrayElement(result); - - // replace values - return ParseTokens(randomElement); - }); - - return parseResult; - } + return parseResult; } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Address.cs b/Source/Bogus/DataSets/Address.cs index b2195563..7a15c732 100644 --- a/Source/Bogus/DataSets/Address.cs +++ b/Source/Bogus/DataSets/Address.cs @@ -1,264 +1,263 @@ using System; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Methods for generating an address. +/// +public class Address : DataSet { /// - /// Methods for generating an address. + /// The source to pull names from. /// - public class Address : DataSet + protected Name Name; + + /// + /// Initializes a new instance of the class. + /// + /// The locale used to generate the data. + public Address(string locale = "en") : base(locale) { - /// - /// The source to pull names from. - /// - protected Name Name; - - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate the data. - public Address(string locale = "en") : base(locale) - { - this.Name = this.Notifier.Flow(new Name(locale)); - } + this.Name = this.Notifier.Flow(new Name(locale)); + } - /// - /// Get a zipcode. - /// - /// - /// If a format is provided it will fill the format with letters and numbers. - /// Example "???? ##" can become "QYTE 78". - /// - /// A random zipcode. - public string ZipCode(string format = null) - { - return format == null ? GetFormattedValue("postcode") : Random.Replace(format); - } + /// + /// Get a zipcode. + /// + /// + /// If a format is provided it will fill the format with letters and numbers. + /// Example "???? ##" can become "QYTE 78". + /// + /// A random zipcode. + public string ZipCode(string format = null) + { + return format == null ? GetFormattedValue("postcode") : Random.Replace(format); + } - /// - /// Get a city name. - /// - /// A random city name. - public string City() - { - return GetFormattedValue("city"); - } + /// + /// Get a city name. + /// + /// A random city name. + public string City() + { + return GetFormattedValue("city"); + } - /// - /// Get a street address. - /// - /// If true, the returned value will also include a . - /// A random street address. - public string StreetAddress(bool useFullAddress = false) - { - var streetAddress = GetFormattedValue("street_address"); - return useFullAddress ? $"{streetAddress} {SecondaryAddress()}" : streetAddress; - } + /// + /// Get a street address. + /// + /// If true, the returned value will also include a . + /// A random street address. + public string StreetAddress(bool useFullAddress = false) + { + var streetAddress = GetFormattedValue("street_address"); + return useFullAddress ? $"{streetAddress} {SecondaryAddress()}" : streetAddress; + } - /// - /// Get a city prefix. - /// - /// A random city prefix. - public string CityPrefix() - { - return GetRandomArrayItem("city_prefix"); - } + /// + /// Get a city prefix. + /// + /// A random city prefix. + public string CityPrefix() + { + return GetRandomArrayItem("city_prefix"); + } - /// - /// Get a city suffix. - /// - /// A random city suffix. - public string CitySuffix() - { - return GetRandomArrayItem("city_suffix"); - } + /// + /// Get a city suffix. + /// + /// A random city suffix. + public string CitySuffix() + { + return GetRandomArrayItem("city_suffix"); + } - /// - /// Get a street name. - /// - /// A random street name. - public string StreetName() - { - return GetFormattedValue("street_name"); - } + /// + /// Get a street name. + /// + /// A random street name. + public string StreetName() + { + return GetFormattedValue("street_name"); + } - /// - /// Get a building number. - /// - /// A random building number. - public string BuildingNumber() - { - return GetFormattedValue("building_number"); - } + /// + /// Get a building number. + /// + /// A random building number. + public string BuildingNumber() + { + return GetFormattedValue("building_number"); + } - /// - /// Get a street suffix. - /// - /// A random street suffix. - public string StreetSuffix() - { - return GetRandomArrayItem("street_suffix"); - } + /// + /// Get a street suffix. + /// + /// A random street suffix. + public string StreetSuffix() + { + return GetRandomArrayItem("street_suffix"); + } - /// - /// Get a secondary address like 'Apt. 2' or 'Suite 321'. - /// - /// A random secondary address. - public string SecondaryAddress() - { - return GetFormattedValue("secondary_address"); - } + /// + /// Get a secondary address like 'Apt. 2' or 'Suite 321'. + /// + /// A random secondary address. + public string SecondaryAddress() + { + return GetFormattedValue("secondary_address"); + } - /// - /// Get a county. - /// - /// A random county. - public string County() - { - return GetRandomArrayItem("county"); - } + /// + /// Get a county. + /// + /// A random county. + public string County() + { + return GetRandomArrayItem("county"); + } + + /// + /// Get a country. + /// + /// A random country. + public string Country() + { + return GetRandomArrayItem("country"); + } + + /// + /// Get a full address like Street, City, Country. + /// + /// A random full address. + public string FullAddress() + { + var street = StreetAddress(); + var city = City(); + var country = Country(); + return $"{street}, {city}, {country}"; + } - /// - /// Get a country. - /// - /// A random country. - public string Country() + + /// + /// Get a random ISO 3166-1 country code. + /// + /// The format the country code should be in. + /// A random country code. + public string CountryCode(Iso3166Format format = Iso3166Format.Alpha2) + { + if( format == Iso3166Format.Alpha2 ) { - return GetRandomArrayItem("country"); + return GetRandomArrayItem("country_code"); } - /// - /// Get a full address like Street, City, Country. - /// - /// A random full address. - public string FullAddress() + if( format == Iso3166Format.Alpha3 ) { - var street = StreetAddress(); - var city = City(); - var country = Country(); - return $"{street}, {city}, {country}"; + return GetRandomArrayItem("country_code_alpha_3"); } + throw new ArgumentException("Invalid country code", nameof(format)); + } - /// - /// Get a random ISO 3166-1 country code. - /// - /// The format the country code should be in. - /// A random country code. - public string CountryCode(Iso3166Format format = Iso3166Format.Alpha2) - { - if( format == Iso3166Format.Alpha2 ) - { - return GetRandomArrayItem("country_code"); - } + /// + /// Get a random state state. + /// + /// A random state. + public string State() + { + return GetRandomArrayItem("state"); + } - if( format == Iso3166Format.Alpha3 ) - { - return GetRandomArrayItem("country_code_alpha_3"); - } + /// + /// Get a state abbreviation. + /// + /// An abbreviation for a random state. + public string StateAbbr() + { + return GetRandomArrayItem("state_abbr"); + } - throw new ArgumentException("Invalid country code", nameof(format)); - } + /// + /// Get a Latitude. + /// + /// The minimum value. + /// The maximum value. + /// A random latitude value. + public double Latitude(double min = -90, double max = 90) + { + return Math.Round(Random.Double(min, max), 4); + } - /// - /// Get a random state state. - /// - /// A random state. - public string State() - { - return GetRandomArrayItem("state"); - } + /// + /// Get a Longitude. + /// + /// The minimum value. + /// The maximum value. + /// A random longitude value. + public double Longitude(double min = -180, double max = 180) + { + return Math.Round(Random.Double(min, max), 4); + } - /// - /// Get a state abbreviation. - /// - /// An abbreviation for a random state. - public string StateAbbr() + /// + /// Generates a cardinal or ordinal direction. IE: Northwest, South, SW, E. + /// + /// When true, directions such as Northwest turn into NW. + /// A random cardinal or ordinal direction. + public string Direction(bool useAbbreviation = false) + { + if( useAbbreviation ) { - return GetRandomArrayItem("state_abbr"); + return GetRandomArrayItem("direction_abbr"); } - /// - /// Get a Latitude. - /// - /// The minimum value. - /// The maximum value. - /// A random latitude value. - public double Latitude(double min = -90, double max = 90) - { - return Math.Round(Random.Double(min, max), 4); - } + return GetRandomArrayItem("direction"); + } - /// - /// Get a Longitude. - /// - /// The minimum value. - /// The maximum value. - /// A random longitude value. - public double Longitude(double min = -180, double max = 180) + /// + /// Generates a cardinal direction. IE: North, South, E, W. + /// + /// When true, directions such as West turn into W. + /// A random cardinal direction + public string CardinalDirection(bool useAbbreviation = false) + { + if( useAbbreviation ) { - return Math.Round(Random.Double(min, max), 4); + return GetRandomArrayItem("direction_abbr", min: 0, max: 4); } - /// - /// Generates a cardinal or ordinal direction. IE: Northwest, South, SW, E. - /// - /// When true, directions such as Northwest turn into NW. - /// A random cardinal or ordinal direction. - public string Direction(bool useAbbreviation = false) - { - if( useAbbreviation ) - { - return GetRandomArrayItem("direction_abbr"); - } - - return GetRandomArrayItem("direction"); - } + return GetRandomArrayItem("direction", min: 0, max: 4); + } - /// - /// Generates a cardinal direction. IE: North, South, E, W. - /// - /// When true, directions such as West turn into W. - /// A random cardinal direction - public string CardinalDirection(bool useAbbreviation = false) + /// + /// Generates an ordinal direction. IE: Northwest, Southeast, SW, NE. + /// + /// When true, directions such as Northwest turn into NW. + /// A random ordinal direction. + public string OrdinalDirection(bool useAbbreviation = false) + { + if( useAbbreviation ) { - if( useAbbreviation ) - { - return GetRandomArrayItem("direction_abbr", min: 0, max: 4); - } - - return GetRandomArrayItem("direction", min: 0, max: 4); + return GetRandomArrayItem("direction_abbr", min: 4, max: 8); } - /// - /// Generates an ordinal direction. IE: Northwest, Southeast, SW, NE. - /// - /// When true, directions such as Northwest turn into NW. - /// A random ordinal direction. - public string OrdinalDirection(bool useAbbreviation = false) - { - if( useAbbreviation ) - { - return GetRandomArrayItem("direction_abbr", min: 4, max: 8); - } - - return GetRandomArrayItem("direction", min: 4, max: 8); - } + return GetRandomArrayItem("direction", min: 4, max: 8); } +} +/// +/// Defines format for ISO 3166-1 country codes. +/// +public enum Iso3166Format +{ /// - /// Defines format for ISO 3166-1 country codes. + /// Two character ISO 3166-1 format. /// - public enum Iso3166Format - { - /// - /// Two character ISO 3166-1 format. - /// - Alpha2 = 0x2, - - /// - /// Three character ISO 3166-1 format. - /// - Alpha3 - } + Alpha2 = 0x2, + + /// + /// Three character ISO 3166-1 format. + /// + Alpha3 } diff --git a/Source/Bogus/DataSets/ColorFormat.cs b/Source/Bogus/DataSets/ColorFormat.cs index 344b88b0..6c86a0d5 100644 --- a/Source/Bogus/DataSets/ColorFormat.cs +++ b/Source/Bogus/DataSets/ColorFormat.cs @@ -1,21 +1,20 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Type of color format +/// +public enum ColorFormat { /// - /// Type of color format + /// Hexadecimal format: #4d0e68 /// - public enum ColorFormat - { - /// - /// Hexadecimal format: #4d0e68 - /// - Hex = 0x1, - /// - /// CSS format: rgb(77,14,104) - /// - Rgb, - /// - /// Delimited R,G,B: 77,14,104 - /// - Delimited - } + Hex = 0x1, + /// + /// CSS format: rgb(77,14,104) + /// + Rgb, + /// + /// Delimited R,G,B: 77,14,104 + /// + Delimited } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Commerce.cs b/Source/Bogus/DataSets/Commerce.cs index 27461783..a094315a 100644 --- a/Source/Bogus/DataSets/Commerce.cs +++ b/Source/Bogus/DataSets/Commerce.cs @@ -1,177 +1,176 @@ using System; using System.Linq; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Methods relating to commerce. +/// +public class Commerce : DataSet { /// - /// Methods relating to commerce. + /// Initializes a new instance of the class. /// - public class Commerce : DataSet + /// The locale used to generate the values. + public Commerce(string locale = "en") : base(locale) { - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate the values. - public Commerce(string locale = "en") : base(locale) - { - } + } + + /// + /// Get a random commerce department. + /// + /// The maximum amount of departments + /// If true the method returns the max amount of values, otherwise the number of categories returned is between 1 and max. + /// A random commerce department. + public string Department(int max = 3, bool returnMax = false) + { + var num = max; - /// - /// Get a random commerce department. - /// - /// The maximum amount of departments - /// If true the method returns the max amount of values, otherwise the number of categories returned is between 1 and max. - /// A random commerce department. - public string Department(int max = 3, bool returnMax = false) + if( !returnMax ) { - var num = max; - - if( !returnMax ) - { - num = this.Random.Number(1, max); - } - - var cats = Categories(num); - if( num > 1 ) - { - var catJoin = string.Join(", ", cats.Take(cats.Length - 1)); - var catLast = cats.Last(); - return $"{catJoin} & {catLast}"; - } - - return cats[0]; + num = this.Random.Number(1, max); } - // there is an easier way to do this. - // check finance.amount - /// - /// Get a random product price. - /// - /// The minimum price. - /// The maximum price. - /// How many decimals the number may include. - /// The symbol in front of the price. - /// A randomly generated price. - public string Price(decimal min = 1, decimal max = 1000, int decimals = 2, string symbol = "") + var cats = Categories(num); + if( num > 1 ) { - var amount = max - min; - var part = (decimal)Random.Double() * amount; - return symbol + Math.Round(min + part, decimals); + var catJoin = string.Join(", ", cats.Take(cats.Length - 1)); + var catLast = cats.Last(); + return $"{catJoin} & {catLast}"; } - /// - /// Get random product categories. - /// - /// The amount of categories to be generated. - /// A collection of random product categories. - public string[] Categories(int num) - { - var result = new string[num]; + return cats[0]; + } - for( var i = 0; i < num; i++ ) - { - result[i] = GetRandomArrayItem("department"); - } + // there is an easier way to do this. + // check finance.amount + /// + /// Get a random product price. + /// + /// The minimum price. + /// The maximum price. + /// How many decimals the number may include. + /// The symbol in front of the price. + /// A randomly generated price. + public string Price(decimal min = 1, decimal max = 1000, int decimals = 2, string symbol = "") + { + var amount = max - min; + var part = (decimal)Random.Double() * amount; + return symbol + Math.Round(min + part, decimals); + } - return result; - } + /// + /// Get random product categories. + /// + /// The amount of categories to be generated. + /// A collection of random product categories. + public string[] Categories(int num) + { + var result = new string[num]; - /// - /// Get a random product name. - /// - /// A random product name. - public string ProductName() + for( var i = 0; i < num; i++ ) { - return $"{ProductAdjective()} {ProductMaterial()} {Product()}"; + result[i] = GetRandomArrayItem("department"); } - /// - /// Get a random color. - /// - /// A random color. - public string Color() - { - return GetRandomArrayItem("color"); - } + return result; + } - /// - /// Get a random product. - /// - /// A random product. - public string Product() - { - return GetRandomArrayItem("product_name.product"); - } + /// + /// Get a random product name. + /// + /// A random product name. + public string ProductName() + { + return $"{ProductAdjective()} {ProductMaterial()} {Product()}"; + } - /// - /// Random product adjective. - /// - /// A random product adjective. - public string ProductAdjective() - { - return GetRandomArrayItem("product_name.adjective"); - } + /// + /// Get a random color. + /// + /// A random color. + public string Color() + { + return GetRandomArrayItem("color"); + } - /// - /// Random product material. - /// - /// A random product material. - public string ProductMaterial() - { - return GetRandomArrayItem("product_name.material"); - } + /// + /// Get a random product. + /// + /// A random product. + public string Product() + { + return GetRandomArrayItem("product_name.product"); + } - /// - /// Random product description. - /// - /// A random product description. - public string ProductDescription() - { - return GetRandomArrayItem("product_description"); - } + /// + /// Random product adjective. + /// + /// A random product adjective. + public string ProductAdjective() + { + return GetRandomArrayItem("product_name.adjective"); + } - /// - /// EAN-8 checksum weights. - /// - protected static int[] Ean8Weights = {3, 1, 3, 1, 3, 1, 3}; + /// + /// Random product material. + /// + /// A random product material. + public string ProductMaterial() + { + return GetRandomArrayItem("product_name.material"); + } - /// - /// Get a random EAN-8 barcode number. - /// - /// A random EAN-8 barcode number. - public string Ean8() - { - // [3, 1, 3, 1, 3, 1, 3] - return this.Ean(8, Ean8Weights); - } + /// + /// Random product description. + /// + /// A random product description. + public string ProductDescription() + { + return GetRandomArrayItem("product_description"); + } - /// - /// EAN-18 checksum weights. - /// - protected static int[] Ean13Weights = { 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 }; + /// + /// EAN-8 checksum weights. + /// + protected static int[] Ean8Weights = {3, 1, 3, 1, 3, 1, 3}; - /// - /// Get a random EAN-13 barcode number. - /// - /// A random EAN-13 barcode number. - public string Ean13() - { - // [1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3] - return this.Ean(13, Ean13Weights); - } + /// + /// Get a random EAN-8 barcode number. + /// + /// A random EAN-8 barcode number. + public string Ean8() + { + // [3, 1, 3, 1, 3, 1, 3] + return this.Ean(8, Ean8Weights); + } - private string Ean(int length, int[] weights) - { - var digits = this.Random.Digits(length - 1); + /// + /// EAN-18 checksum weights. + /// + protected static int[] Ean13Weights = { 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3 }; - var weightedSum = - digits.Zip(weights, - (d, w) => d * w) - .Sum(); + /// + /// Get a random EAN-13 barcode number. + /// + /// A random EAN-13 barcode number. + public string Ean13() + { + // [1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3] + return this.Ean(13, Ean13Weights); + } - var checkDigit = (10 - weightedSum % 10) % 10; + private string Ean(int length, int[] weights) + { + var digits = this.Random.Digits(length - 1); - return string.Join("", digits) + checkDigit; - } + var weightedSum = + digits.Zip(weights, + (d, w) => d * w) + .Sum(); + + var checkDigit = (10 - weightedSum % 10) % 10; + + return string.Join("", digits) + checkDigit; } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Company.cs b/Source/Bogus/DataSets/Company.cs index d3d3d942..dff3d15f 100644 --- a/Source/Bogus/DataSets/Company.cs +++ b/Source/Bogus/DataSets/Company.cs @@ -1,119 +1,118 @@ using System.Linq; using Bogus.Bson; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates a random company name and phrases +/// +public class Company : DataSet { /// - /// Generates a random company name and phrases + /// The source to pull names from. + /// + protected Name Name = null; + + /// + /// Initializes a new instance of the class. + /// + /// The locale used to generate the values. + public Company(string locale = "en") : base(locale) + { + this.Name = this.Notifier.Flow(new Name(locale)); + } + + /// + /// Get a company suffix. "Inc" and "LLC" etc. /// - public class Company : DataSet + /// A random company suffix. + public string CompanySuffix() { - /// - /// The source to pull names from. - /// - protected Name Name = null; - - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate the values. - public Company(string locale = "en") : base(locale) - { - this.Name = this.Notifier.Flow(new Name(locale)); - } - - /// - /// Get a company suffix. "Inc" and "LLC" etc. - /// - /// A random company suffix. - public string CompanySuffix() - { - return Random.ArrayElement(Suffixes()); - } - - /// - /// Get a company name. - /// - /// 0: name + suffix, 1: name-name, 2: name, name and name." - /// A random company name. - public string CompanyName(int? formatIndex = null) - { - var formats = new[] - { - "{{name.lastName}} {{company.companySuffix}}", - "{{name.lastName}} - {{name.lastName}}", - "{{name.lastName}}, {{name.lastName}} and {{name.lastName}}" - }; - - var index = formatIndex ?? Random.Number(formats.Length - 1); - return CompanyName(formats[index]); - } - - /// - /// Get a company name. The format can use any name.* and company.* methods. - /// - /// Example: "{{name.lastName}} {{company.companySuffix}}" - /// A random company name in the given format. - public string CompanyName(string format) - { - return Tokenizer.Parse(format, this, this.Name); - } - - - /// - /// Get a company catch phrase. - /// - /// A random company catch phrase. - public string CatchPhrase() - { - return $"{CatchPhraseAdjective()} {CatchPhraseDescriptor()} {CatchPhraseNoun()}"; - } - - /// - /// Get a company BS phrase. - /// - /// A random company BS phrase. - public string Bs() - { - return $"{BsBuzz()} {BsAdjective()} {BsNoun()}"; - } + return Random.ArrayElement(Suffixes()); + } + + /// + /// Get a company name. + /// + /// 0: name + suffix, 1: name-name, 2: name, name and name." + /// A random company name. + public string CompanyName(int? formatIndex = null) + { + var formats = new[] + { + "{{name.lastName}} {{company.companySuffix}}", + "{{name.lastName}} - {{name.lastName}}", + "{{name.lastName}}, {{name.lastName}} and {{name.lastName}}" + }; + + var index = formatIndex ?? Random.Number(formats.Length - 1); + return CompanyName(formats[index]); + } + + /// + /// Get a company name. The format can use any name.* and company.* methods. + /// + /// Example: "{{name.lastName}} {{company.companySuffix}}" + /// A random company name in the given format. + public string CompanyName(string format) + { + return Tokenizer.Parse(format, this, this.Name); + } + + + /// + /// Get a company catch phrase. + /// + /// A random company catch phrase. + public string CatchPhrase() + { + return $"{CatchPhraseAdjective()} {CatchPhraseDescriptor()} {CatchPhraseNoun()}"; + } + + /// + /// Get a company BS phrase. + /// + /// A random company BS phrase. + public string Bs() + { + return $"{BsBuzz()} {BsAdjective()} {BsNoun()}"; + } #pragma warning disable 1591 - internal protected virtual string[] Suffixes() - { - return GetArray("suffix").OfType().Select(s => s.StringValue).ToArray(); - } - - internal protected virtual string CatchPhraseAdjective() - { - return GetRandomArrayItem("adjective"); - } - - - internal protected virtual string CatchPhraseDescriptor() - { - return GetRandomArrayItem("descriptor"); - } - - internal protected virtual string CatchPhraseNoun() - { - return GetRandomArrayItem("noun"); - } - - internal protected virtual string BsAdjective() - { - return GetRandomArrayItem("bs_adjective"); - } - - internal protected virtual string BsBuzz() - { - return GetRandomArrayItem("bs_verb"); - } - - internal protected virtual string BsNoun() - { - return GetRandomArrayItem("bs_noun"); - } -#pragma warning restore 1591 + internal protected virtual string[] Suffixes() + { + return GetArray("suffix").OfType().Select(s => s.StringValue).ToArray(); + } + + internal protected virtual string CatchPhraseAdjective() + { + return GetRandomArrayItem("adjective"); + } + + + internal protected virtual string CatchPhraseDescriptor() + { + return GetRandomArrayItem("descriptor"); } + + internal protected virtual string CatchPhraseNoun() + { + return GetRandomArrayItem("noun"); + } + + internal protected virtual string BsAdjective() + { + return GetRandomArrayItem("bs_adjective"); + } + + internal protected virtual string BsBuzz() + { + return GetRandomArrayItem("bs_verb"); + } + + internal protected virtual string BsNoun() + { + return GetRandomArrayItem("bs_noun"); + } +#pragma warning restore 1591 } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Database.cs b/Source/Bogus/DataSets/Database.cs index d9120ced..8b0259ef 100644 --- a/Source/Bogus/DataSets/Database.cs +++ b/Source/Bogus/DataSets/Database.cs @@ -1,44 +1,43 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates some random database stuff. +/// +public class Database : DataSet { /// - /// Generates some random database stuff. + /// Generates a column name. /// - public class Database : DataSet + /// A random column name. + public string Column() { - /// - /// Generates a column name. - /// - /// A random column name. - public string Column() - { - return this.GetRandomArrayItem("column"); - } + return this.GetRandomArrayItem("column"); + } - /// - /// Generates a column type. - /// - /// A random column type. - public string Type() - { - return this.GetRandomArrayItem("type"); - } + /// + /// Generates a column type. + /// + /// A random column type. + public string Type() + { + return this.GetRandomArrayItem("type"); + } - /// - /// Generates a collation. - /// - /// A random collation. - public string Collation() - { - return this.GetRandomArrayItem("collation"); - } + /// + /// Generates a collation. + /// + /// A random collation. + public string Collation() + { + return this.GetRandomArrayItem("collation"); + } - /// - /// Generates a storage engine. - /// - /// A random storage engine. - public string Engine() - { - return this.GetRandomArrayItem("engine"); - } + /// + /// Generates a storage engine. + /// + /// A random storage engine. + public string Engine() + { + return this.GetRandomArrayItem("engine"); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Date.cs b/Source/Bogus/DataSets/Date.cs index 4bb1bb25..758d9235 100644 --- a/Source/Bogus/DataSets/Date.cs +++ b/Source/Bogus/DataSets/Date.cs @@ -1,267 +1,266 @@ using System; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Methods for generating dates +/// +public partial class Date : DataSet { + private bool hasMonthWideContext; + private bool hasMonthAbbrContext; + private bool hasWeekdayWideContext; + private bool hasWeekdayAbbrContext; + /// - /// Methods for generating dates + /// Sets the system clock time Bogus uses for date calculations. + /// This value is normally . If deterministic times are desired, + /// set the to a single instance in time. + /// IE: () => new DateTime(2018, 4, 23); + /// Setting this static value will effect and apply to all Faker[T], Faker, + /// and new Date() datasets instances. /// - public partial class Date : DataSet + public static Func SystemClock = () => DateTime.Now; + + /// + /// Create a Date dataset + /// + public Date(string locale = "en") : base(locale) { - private bool hasMonthWideContext; - private bool hasMonthAbbrContext; - private bool hasWeekdayWideContext; - private bool hasWeekdayAbbrContext; - - /// - /// Sets the system clock time Bogus uses for date calculations. - /// This value is normally . If deterministic times are desired, - /// set the to a single instance in time. - /// IE: () => new DateTime(2018, 4, 23); - /// Setting this static value will effect and apply to all Faker[T], Faker, - /// and new Date() datasets instances. - /// - public static Func SystemClock = () => DateTime.Now; - - /// - /// Create a Date dataset - /// - public Date(string locale = "en") : base(locale) - { - this.hasMonthWideContext = HasKey("month.wide_context", false); - this.hasMonthAbbrContext = HasKey("month.abbr_context", false); - this.hasWeekdayWideContext = HasKey("weekday.wide_context", false); - this.hasWeekdayAbbrContext = HasKey("weekday.abbr_context", false); - } + this.hasMonthWideContext = HasKey("month.wide_context", false); + this.hasMonthAbbrContext = HasKey("month.abbr_context", false); + this.hasWeekdayWideContext = HasKey("weekday.wide_context", false); + this.hasWeekdayAbbrContext = HasKey("weekday.abbr_context", false); + } - /// - /// Get a in the past between and . - /// - /// Years to go back from . Default is 1 year. - /// The date to start calculations. Default is . - public DateTime Past(int yearsToGoBack = 1, DateTime? refDate = null) - { - var maxDate = refDate ?? SystemClock(); + /// + /// Get a in the past between and . + /// + /// Years to go back from . Default is 1 year. + /// The date to start calculations. Default is . + public DateTime Past(int yearsToGoBack = 1, DateTime? refDate = null) + { + var maxDate = refDate ?? SystemClock(); - var minDate = maxDate.AddYears(-yearsToGoBack); + var minDate = maxDate.AddYears(-yearsToGoBack); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return maxDate - partTimeSpan; - } + return maxDate - partTimeSpan; + } - /// - /// Get a in the past between and . - /// - /// Years to go back from . Default is 1 year. - /// The date to start calculations. Default is . - public DateTimeOffset PastOffset(int yearsToGoBack = 1, DateTimeOffset? refDate = null) - { - var maxDate = refDate ?? SystemClock(); + /// + /// Get a in the past between and . + /// + /// Years to go back from . Default is 1 year. + /// The date to start calculations. Default is . + public DateTimeOffset PastOffset(int yearsToGoBack = 1, DateTimeOffset? refDate = null) + { + var maxDate = refDate ?? SystemClock(); - var minDate = maxDate.AddYears(-yearsToGoBack); + var minDate = maxDate.AddYears(-yearsToGoBack); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return maxDate - partTimeSpan; - } + return maxDate - partTimeSpan; + } - /// - /// Gets an random timespan from ticks. - /// - protected internal TimeSpan RandomTimeSpanFromTicks(long totalTimeSpanTicks) - { - //find % of the timespan - var partTimeSpanTicks = Random.Double() * totalTimeSpanTicks; - return TimeSpan.FromTicks(Convert.ToInt64(partTimeSpanTicks)); - } + /// + /// Gets an random timespan from ticks. + /// + protected internal TimeSpan RandomTimeSpanFromTicks(long totalTimeSpanTicks) + { + //find % of the timespan + var partTimeSpanTicks = Random.Double() * totalTimeSpanTicks; + return TimeSpan.FromTicks(Convert.ToInt64(partTimeSpanTicks)); + } - /// - /// Get a that will happen soon. - /// - /// A date no more than ahead. - /// The date to start calculations. Default is . - public DateTime Soon(int days = 1, DateTime? refDate = null) - { - var dt = refDate ?? SystemClock(); - return Between(dt, dt.AddDays(days)); - } + /// + /// Get a that will happen soon. + /// + /// A date no more than ahead. + /// The date to start calculations. Default is . + public DateTime Soon(int days = 1, DateTime? refDate = null) + { + var dt = refDate ?? SystemClock(); + return Between(dt, dt.AddDays(days)); + } - /// - /// Get a that will happen soon. - /// - /// A date no more than ahead. - /// The date to start calculations. Default is . - public DateTimeOffset SoonOffset(int days = 1, DateTimeOffset? refDate = null) - { - var dt = refDate ?? SystemClock(); - return BetweenOffset(dt, dt.AddDays(days)); - } + /// + /// Get a that will happen soon. + /// + /// A date no more than ahead. + /// The date to start calculations. Default is . + public DateTimeOffset SoonOffset(int days = 1, DateTimeOffset? refDate = null) + { + var dt = refDate ?? SystemClock(); + return BetweenOffset(dt, dt.AddDays(days)); + } - /// - /// Get a in the future between and . - /// - /// Years to go forward from . Default is 1 year. - /// The date to start calculations. Default is . - public DateTime Future(int yearsToGoForward = 1, DateTime? refDate = null) - { - var minDate = refDate ?? SystemClock(); + /// + /// Get a in the future between and . + /// + /// Years to go forward from . Default is 1 year. + /// The date to start calculations. Default is . + public DateTime Future(int yearsToGoForward = 1, DateTime? refDate = null) + { + var minDate = refDate ?? SystemClock(); - var maxDate = minDate.AddYears(yearsToGoForward); + var maxDate = minDate.AddYears(yearsToGoForward); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return minDate + partTimeSpan; - } + return minDate + partTimeSpan; + } - /// - /// Get a in the future between and . - /// - /// Years to go forward from . Default is 1 year. - /// The date to start calculations. Default is . - public DateTimeOffset FutureOffset(int yearsToGoForward = 1, DateTimeOffset? refDate = null) - { - var minDate = refDate ?? SystemClock(); + /// + /// Get a in the future between and . + /// + /// Years to go forward from . Default is 1 year. + /// The date to start calculations. Default is . + public DateTimeOffset FutureOffset(int yearsToGoForward = 1, DateTimeOffset? refDate = null) + { + var minDate = refDate ?? SystemClock(); - var maxDate = minDate.AddYears(yearsToGoForward); + var maxDate = minDate.AddYears(yearsToGoForward); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return minDate + partTimeSpan; - } + return minDate + partTimeSpan; + } - /// - /// Get a random between and . - /// - /// Start time - The returned is used from this parameter. - /// End time - public DateTime Between(DateTime start, DateTime end) - { - var minTicks = Math.Min(start.Ticks, end.Ticks); - var maxTicks = Math.Max(start.Ticks, end.Ticks); + /// + /// Get a random between and . + /// + /// Start time - The returned is used from this parameter. + /// End time + public DateTime Between(DateTime start, DateTime end) + { + var minTicks = Math.Min(start.Ticks, end.Ticks); + var maxTicks = Math.Max(start.Ticks, end.Ticks); - var totalTimeSpanTicks = maxTicks - minTicks; + var totalTimeSpanTicks = maxTicks - minTicks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return new DateTime(minTicks, start.Kind) + partTimeSpan; - } + return new DateTime(minTicks, start.Kind) + partTimeSpan; + } - /// - /// Get a random between and . - /// - /// Start time - The returned offset value is used from this parameter - /// End time - public DateTimeOffset BetweenOffset(DateTimeOffset start, DateTimeOffset end) - { - var minTicks = Math.Min(start.Ticks, end.Ticks); - var maxTicks = Math.Max(start.Ticks, end.Ticks); + /// + /// Get a random between and . + /// + /// Start time - The returned offset value is used from this parameter + /// End time + public DateTimeOffset BetweenOffset(DateTimeOffset start, DateTimeOffset end) + { + var minTicks = Math.Min(start.Ticks, end.Ticks); + var maxTicks = Math.Max(start.Ticks, end.Ticks); - var totalTimeSpanTicks = maxTicks - minTicks; + var totalTimeSpanTicks = maxTicks - minTicks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return new DateTimeOffset(minTicks, start.Offset) + partTimeSpan; - } + return new DateTimeOffset(minTicks, start.Offset) + partTimeSpan; + } - /// - /// Get a random within the last few days. - /// - /// Number of days to go back. - /// The date to start calculations. Default is . - public DateTime Recent(int days = 1, DateTime? refDate = null) - { - var maxDate = refDate ?? SystemClock(); + /// + /// Get a random within the last few days. + /// + /// Number of days to go back. + /// The date to start calculations. Default is . + public DateTime Recent(int days = 1, DateTime? refDate = null) + { + var maxDate = refDate ?? SystemClock(); - var minDate = days == 0 ? SystemClock().Date : maxDate.AddDays(-days); + var minDate = days == 0 ? SystemClock().Date : maxDate.AddDays(-days); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return maxDate - partTimeSpan; - } + return maxDate - partTimeSpan; + } - /// - /// Get a random within the last few days. - /// - /// Number of days to go back. - /// The date to start calculations. Default is . - public DateTimeOffset RecentOffset(int days = 1, DateTimeOffset? refDate = null) - { - var maxDate = refDate ?? SystemClock(); + /// + /// Get a random within the last few days. + /// + /// Number of days to go back. + /// The date to start calculations. Default is . + public DateTimeOffset RecentOffset(int days = 1, DateTimeOffset? refDate = null) + { + var maxDate = refDate ?? SystemClock(); - var minDate = days == 0 ? SystemClock().Date : maxDate.AddDays(-days); + var minDate = days == 0 ? SystemClock().Date : maxDate.AddDays(-days); - var totalTimeSpanTicks = (maxDate - minDate).Ticks; + var totalTimeSpanTicks = (maxDate - minDate).Ticks; - var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); + var partTimeSpan = RandomTimeSpanFromTicks(totalTimeSpanTicks); - return maxDate - partTimeSpan; - } + return maxDate - partTimeSpan; + } - /// - /// Get a random . - /// - /// Maximum of time to span. Default 1 week/7 days. - public TimeSpan Timespan(TimeSpan? maxSpan = null) - { - var span = maxSpan ?? TimeSpan.FromDays(7); + /// + /// Get a random . + /// + /// Maximum of time to span. Default 1 week/7 days. + public TimeSpan Timespan(TimeSpan? maxSpan = null) + { + var span = maxSpan ?? TimeSpan.FromDays(7); - return RandomTimeSpanFromTicks(span.Ticks); - } + return RandomTimeSpanFromTicks(span.Ticks); + } - /// - /// Get a random month. - /// - public string Month(bool abbreviation = false, bool useContext = false) - { - var type = "wide"; - if( abbreviation ) - type = "abbr"; - - if( useContext && - (type == "wide" && hasMonthWideContext) || - (type == "abbr" && hasMonthAbbrContext) ) - { - type += "_context"; - } - - return GetRandomArrayItem("month." + type); - } + /// + /// Get a random month. + /// + public string Month(bool abbreviation = false, bool useContext = false) + { + var type = "wide"; + if( abbreviation ) + type = "abbr"; - /// - /// Get a random weekday. - /// - public string Weekday(bool abbreviation = false, bool useContext = false) + if( useContext && + (type == "wide" && hasMonthWideContext) || + (type == "abbr" && hasMonthAbbrContext) ) { - var type = "wide"; - if( abbreviation ) - type = "abbr"; - - if( useContext && - (type == "wide" && hasWeekdayWideContext) || - (type == "abbr" && hasWeekdayAbbrContext) ) - { - type += "_context"; - } - - return GetRandomArrayItem("weekday." + type); + type += "_context"; } - /// - /// Get a timezone string. Eg: America/Los_Angeles - /// - public string TimeZoneString() + return GetRandomArrayItem("month." + type); + } + + /// + /// Get a random weekday. + /// + public string Weekday(bool abbreviation = false, bool useContext = false) + { + var type = "wide"; + if( abbreviation ) + type = "abbr"; + + if( useContext && + (type == "wide" && hasWeekdayWideContext) || + (type == "abbr" && hasWeekdayAbbrContext) ) { - return GetRandomArrayItem("address", "time_zone"); + type += "_context"; } + + return GetRandomArrayItem("weekday." + type); + } + + /// + /// Get a timezone string. Eg: America/Los_Angeles + /// + public string TimeZoneString() + { + return GetRandomArrayItem("address", "time_zone"); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Date.net60.cs b/Source/Bogus/DataSets/Date.net60.cs index ac64f5ef..5e1793ca 100644 --- a/Source/Bogus/DataSets/Date.net60.cs +++ b/Source/Bogus/DataSets/Date.net60.cs @@ -3,119 +3,118 @@ using System.Linq; using System.Text; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +public partial class Date { - public partial class Date +#if NET6_0_OR_GREATER + /// + /// Get a random between and . + /// + /// Start date + /// End date + public DateOnly BetweenDateOnly(DateOnly start, DateOnly end) { -#if NET6_0 - /// - /// Get a random between and . - /// - /// Start date - /// End date - public DateOnly BetweenDateOnly(DateOnly start, DateOnly end) - { - var maxDay = Math.Max(start.DayNumber, end.DayNumber); - var minDay = Math.Min(start.DayNumber, end.DayNumber); - - var someDayNumber = this.Random.Number(minDay, maxDay); - - var dateBetween = DateOnly.FromDayNumber(someDayNumber); - return dateBetween; - } - - /// - /// Get a in the past between and . - /// - /// Years to go back from . Default is 1 year. - /// The date to start calculations. Default is from . - public DateOnly PastDateOnly(int yearsToGoBack = 1, DateOnly? refDate = null) - { - var start = refDate ?? DateOnly.FromDateTime(SystemClock()); - var maxBehind = start.AddYears(-yearsToGoBack); - - return BetweenDateOnly(maxBehind, start); - } - - /// - /// Get a that will happen soon. - /// - /// A date no more than ahead. - /// The date to start calculations. Default is from . - public DateOnly SoonDateOnly(int days = 1, DateOnly? refDate = null) - { - var start = refDate ?? DateOnly.FromDateTime(SystemClock()); - var maxForward = start.AddDays(days); - - return BetweenDateOnly(start, maxForward); - } - - /// - /// Get a in the future between and . - /// - /// Years to go forward from . Default is 1 year. - /// The date to start calculations. Default is from . - public DateOnly FutureDateOnly(int yearsToGoForward = 1, DateOnly? refDate = null) - { - var start = refDate ?? DateOnly.FromDateTime(SystemClock()); - var maxForward = start.AddYears(yearsToGoForward); - - return BetweenDateOnly(start, maxForward); - } - - /// - /// Get a random within the last few days. - /// - /// Number of days to go back. - /// The date to start calculations. Default is from . - public DateOnly RecentDateOnly(int days = 1, DateOnly? refDate = null) - { - var start = refDate ?? DateOnly.FromDateTime(SystemClock()); - var maxBehind = start.AddDays(-days); - - return BetweenDateOnly(maxBehind, start); - } - - /// - /// Get a random between and . - /// - /// Start time - /// End time - public TimeOnly BetweenTimeOnly(TimeOnly start, TimeOnly end) - { - var diff = end - start; - var diffTicks = diff.Ticks; - - var part = RandomTimeSpanFromTicks(diffTicks); - - return start.Add(part); - } - - /// - /// Get a that will happen soon. - /// - /// Minutes no more than ahead. - /// The time to start calculations. Default is time from . - public TimeOnly SoonTimeOnly(int mins = 60, TimeOnly? refTime = null) - { - var start = refTime ?? TimeOnly.FromDateTime(SystemClock()); - var maxForward = start.AddMinutes(mins); - - return BetweenTimeOnly(start, maxForward); - } - - /// - /// Get a random within the last few Minutes. - /// - /// Minutes of the day to go back. - /// The Time to start calculations. Default is time from . - public TimeOnly RecentTimeOnly(int mins = 60, TimeOnly? refTime = null) - { - var start = refTime ?? TimeOnly.FromDateTime(SystemClock()); - var maxBehind = start.AddMinutes(-mins); - - return BetweenTimeOnly(maxBehind, start); - } -#endif + var maxDay = Math.Max(start.DayNumber, end.DayNumber); + var minDay = Math.Min(start.DayNumber, end.DayNumber); + + var someDayNumber = this.Random.Number(minDay, maxDay); + + var dateBetween = DateOnly.FromDayNumber(someDayNumber); + return dateBetween; + } + + /// + /// Get a in the past between and . + /// + /// Years to go back from . Default is 1 year. + /// The date to start calculations. Default is from . + public DateOnly PastDateOnly(int yearsToGoBack = 1, DateOnly? refDate = null) + { + var start = refDate ?? DateOnly.FromDateTime(SystemClock()); + var maxBehind = start.AddYears(-yearsToGoBack); + + return BetweenDateOnly(maxBehind, start); + } + + /// + /// Get a that will happen soon. + /// + /// A date no more than ahead. + /// The date to start calculations. Default is from . + public DateOnly SoonDateOnly(int days = 1, DateOnly? refDate = null) + { + var start = refDate ?? DateOnly.FromDateTime(SystemClock()); + var maxForward = start.AddDays(days); + + return BetweenDateOnly(start, maxForward); + } + + /// + /// Get a in the future between and . + /// + /// Years to go forward from . Default is 1 year. + /// The date to start calculations. Default is from . + public DateOnly FutureDateOnly(int yearsToGoForward = 1, DateOnly? refDate = null) + { + var start = refDate ?? DateOnly.FromDateTime(SystemClock()); + var maxForward = start.AddYears(yearsToGoForward); + + return BetweenDateOnly(start, maxForward); + } + + /// + /// Get a random within the last few days. + /// + /// Number of days to go back. + /// The date to start calculations. Default is from . + public DateOnly RecentDateOnly(int days = 1, DateOnly? refDate = null) + { + var start = refDate ?? DateOnly.FromDateTime(SystemClock()); + var maxBehind = start.AddDays(-days); + + return BetweenDateOnly(maxBehind, start); } + + /// + /// Get a random between and . + /// + /// Start time + /// End time + public TimeOnly BetweenTimeOnly(TimeOnly start, TimeOnly end) + { + var diff = end - start; + var diffTicks = diff.Ticks; + + var part = RandomTimeSpanFromTicks(diffTicks); + + return start.Add(part); + } + + /// + /// Get a that will happen soon. + /// + /// Minutes no more than ahead. + /// The time to start calculations. Default is time from . + public TimeOnly SoonTimeOnly(int mins = 60, TimeOnly? refTime = null) + { + var start = refTime ?? TimeOnly.FromDateTime(SystemClock()); + var maxForward = start.AddMinutes(mins); + + return BetweenTimeOnly(start, maxForward); + } + + /// + /// Get a random within the last few Minutes. + /// + /// Minutes of the day to go back. + /// The Time to start calculations. Default is time from . + public TimeOnly RecentTimeOnly(int mins = 60, TimeOnly? refTime = null) + { + var start = refTime ?? TimeOnly.FromDateTime(SystemClock()); + var maxBehind = start.AddMinutes(-mins); + + return BetweenTimeOnly(maxBehind, start); + } +#endif } diff --git a/Source/Bogus/DataSets/Finance.cs b/Source/Bogus/DataSets/Finance.cs index 1d9ceb03..35c2fe9d 100644 --- a/Source/Bogus/DataSets/Finance.cs +++ b/Source/Bogus/DataSets/Finance.cs @@ -6,547 +6,546 @@ using Bogus.Bson; using Bogus.Extensions.Extras; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Represents a currency +/// +public class Currency { /// - /// Represents a currency + /// The long for description of the currency. IE: "US Dollar" + /// + public string Description { get; set; } + + /// + /// The currency code. IE: USD. + /// + public string Code { get; set; } + + /// + /// The currency symbol. IE: $ /// - public class Currency + public string Symbol { get; set; } + + public static Currency Default = new Currency { Description = "US Dollar", Code = "USD", Symbol = "$" }; +} + +/// +/// Represents an enumeration of all the supported card types. +/// +public class CardType +{ + internal string Value { get; } + + private CardType(string value) { - /// - /// The long for description of the currency. IE: "US Dollar" - /// - public string Description { get; set; } - - /// - /// The currency code. IE: USD. - /// - public string Code { get; set; } - - /// - /// The currency symbol. IE: $ - /// - public string Symbol { get; set; } - - public static Currency Default = new Currency { Description = "US Dollar", Code = "USD", Symbol = "$" }; + this.Value = value; + All.Add(this); } /// - /// Represents an enumeration of all the supported card types. + /// List of all card types. + /// + public static readonly List All = new List(); + + /// + /// Visa card number /// - public class CardType + public static readonly CardType Visa = new CardType("visa"); + + /// + /// Mastercard card number + /// + public static readonly CardType Mastercard = new CardType("mastercard"); + + /// + /// Discover card number + /// + public static readonly CardType Discover = new CardType("discover"); + + /// + /// American Express card number + /// + public static readonly CardType AmericanExpress = new CardType("american_express"); + + /// + /// Diners Club card number + /// + public static readonly CardType DinersClub = new CardType("diners_club"); + + /// + /// JCB card number + /// + public static readonly CardType Jcb = new CardType("jcb"); + + /// + /// Switch card number + /// + public static readonly CardType Switch = new CardType("switch"); + + /// + /// Solo card number + /// + public static readonly CardType Solo = new CardType("solo"); + + /// + /// Maestro card number + /// + public static readonly CardType Maestro = new CardType("maestro"); + + /// + /// Laser card number + /// + public static readonly CardType Laser = new CardType("laser"); + + /// + /// Instapayment card number + /// + public static readonly CardType Instapayment = new CardType("instapayment"); +} + + +/// +/// Provides financial randomness. +/// +public class Finance : DataSet +{ + /// + /// Get an account number. Default length is 8 digits. + /// + /// The length of the account number. + public string Account(int length = 8) { - internal string Value { get; } + var template = new string('#', length); + return Random.Replace(template); + } - private CardType(string value) - { - this.Value = value; - All.Add(this); - } + /// + /// Get an account name. Like "savings", "checking", "Home Loan" etc.. + /// + public string AccountName() + { + var type = GetRandomArrayItem("account_type"); + return $"{type} Account"; + } - /// - /// List of all card types. - /// - public static readonly List All = new List(); - - /// - /// Visa card number - /// - public static readonly CardType Visa = new CardType("visa"); - - /// - /// Mastercard card number - /// - public static readonly CardType Mastercard = new CardType("mastercard"); - - /// - /// Discover card number - /// - public static readonly CardType Discover = new CardType("discover"); - - /// - /// American Express card number - /// - public static readonly CardType AmericanExpress = new CardType("american_express"); - - /// - /// Diners Club card number - /// - public static readonly CardType DinersClub = new CardType("diners_club"); - - /// - /// JCB card number - /// - public static readonly CardType Jcb = new CardType("jcb"); - - /// - /// Switch card number - /// - public static readonly CardType Switch = new CardType("switch"); - - /// - /// Solo card number - /// - public static readonly CardType Solo = new CardType("solo"); - - /// - /// Maestro card number - /// - public static readonly CardType Maestro = new CardType("maestro"); - - /// - /// Laser card number - /// - public static readonly CardType Laser = new CardType("laser"); - - /// - /// Instapayment card number - /// - public static readonly CardType Instapayment = new CardType("instapayment"); + /// + /// Get a random amount. Default 0 - 1000. + /// + /// Min value. Default 0. + /// Max value. Default 1000. + /// Decimal places. Default 2. + public decimal Amount(decimal min = 0, decimal max = 1000, int decimals = 2) + { + var amount = (max - min); + var part = (decimal)Random.Double() * amount; + return Math.Round(min + part, decimals); } /// - /// Provides financial randomness. + /// Get a transaction type: "deposit", "withdrawal", "payment", or "invoice". /// - public class Finance : DataSet + public string TransactionType() { - /// - /// Get an account number. Default length is 8 digits. - /// - /// The length of the account number. - public string Account(int length = 8) - { - var template = new string('#', length); - return Random.Replace(template); - } + return GetRandomArrayItem("transaction_type"); + } - /// - /// Get an account name. Like "savings", "checking", "Home Loan" etc.. - /// - public string AccountName() - { - var type = GetRandomArrayItem("account_type"); - return $"{type} Account"; - } + /// + /// Get a random currency. + /// + public Currency Currency(bool includeFundCodes = false) + { + var obj = this.GetRandomBObject("currency"); - /// - /// Get a random amount. Default 0 - 1000. - /// - /// Min value. Default 0. - /// Max value. Default 1000. - /// Decimal places. Default 2. - public decimal Amount(decimal min = 0, decimal max = 1000, int decimals = 2) + var cur = new Currency { - var amount = (max - min); - var part = (decimal)Random.Double() * amount; - return Math.Round(min + part, decimals); - } + Description = obj["name"], + Code = obj["code"], + Symbol = obj["symbol"], + }; + // GitHub Issue #80: + // Make sure we exclude currency fund codes by default unless + // the user wants them. See: + //https://github.com/bchavez/Bogus/issues/80 - /// - /// Get a transaction type: "deposit", "withdrawal", "payment", or "invoice". - /// - public string TransactionType() + if (cur.Code.Contains(" ")) { - return GetRandomArrayItem("transaction_type"); + // We selected a currency fund code. Check if the user wants it. + if (includeFundCodes) + { + cur.Code = cur.Code.Split(' ')[1]; + return cur; + } + //If they don't want fund codes, send back a default USD. + //instead of selecting again (and possibly looping over and over). + return DataSets.Currency.Default; } - /// - /// Get a random currency. - /// - public Currency Currency(bool includeFundCodes = false) + return cur; + } + + /// + /// Generate a random credit card number with valid Luhn checksum. + /// + /// The type of credit card to generate (ie: American Express, Discover, etc.). Passing null, a random card provider will be chosen. + public string CreditCardNumber(CardType provider = null) + { + if (provider is null) { - var obj = this.GetRandomBObject("currency"); + provider = this.Random.ListItem(CardType.All); + } - var cur = new Currency - { - Description = obj["name"], - Code = obj["code"], - Symbol = obj["symbol"], - }; + var format = GetRandomArrayItem($"credit_card.{provider.Value}"); - // GitHub Issue #80: - // Make sure we exclude currency fund codes by default unless - // the user wants them. See: - //https://github.com/bchavez/Bogus/issues/80 + var symbol = '#'; + var expandedFormat = RegexStyleStringParse(format); // replace [4-9] with a random number in range etc... + var cardNumber = this.Random.ReplaceNumbers(expandedFormat, symbol); // replace ### with random numbers - if (cur.Code.Contains(" ")) - { - // We selected a currency fund code. Check if the user wants it. - if (includeFundCodes) - { - cur.Code = cur.Code.Split(' ')[1]; - return cur; - } - //If they don't want fund codes, send back a default USD. - //instead of selecting again (and possibly looping over and over). - return DataSets.Currency.Default; - } + var numberList = cardNumber.Where(char.IsDigit) + .Select(c => int.Parse(c.ToString())).ToList(); - return cur; - } + var checkNum = numberList.CheckDigit(); + return cardNumber.Replace("L", checkNum.ToString()); - /// - /// Generate a random credit card number with valid Luhn checksum. - /// - /// The type of credit card to generate (ie: American Express, Discover, etc.). Passing null, a random card provider will be chosen. - public string CreditCardNumber(CardType provider = null) + string RegexStyleStringParse(string str = "") { - if (provider is null) + // Deal with range repeat `{min,max}` + var RANGE_REP_REG = new Regex(@"(.)\{(\d+)\,(\d+)\}"); + var REP_REG = new Regex(@"(.)\{(\d+)\}"); + var RANGE_REG = new Regex(@"\[(\d+)\-(\d+)\]"); + int min, max, tmp, repetitions; + var token = RANGE_REP_REG.Match(str); + while (token.Success) { - provider = this.Random.ListItem(CardType.All); - } - - var format = GetRandomArrayItem($"credit_card.{provider.Value}"); + min = Int32.Parse(token.Groups[2].Value); + max = Int32.Parse(token.Groups[3].Value); - var symbol = '#'; - var expandedFormat = RegexStyleStringParse(format); // replace [4-9] with a random number in range etc... - var cardNumber = this.Random.ReplaceNumbers(expandedFormat, symbol); // replace ### with random numbers + if (min > max) + { + tmp = max; + max = min; + min = tmp; + } - var numberList = cardNumber.Where(char.IsDigit) - .Select(c => int.Parse(c.ToString())).ToList(); + repetitions = this.Random.Number(min, max); - var checkNum = numberList.CheckDigit(); - return cardNumber.Replace("L", checkNum.ToString()); + str = str.Substring(0, token.Index) + + new string(token.Groups[1].Value[0], repetitions) + + str.Substring(token.Index + token.Groups[0].Length); - string RegexStyleStringParse(string str = "") + token = RANGE_REP_REG.Match(str); + } + // Deal with repeat `{num}` + token = REP_REG.Match(str); + while (token.Success) { - // Deal with range repeat `{min,max}` - var RANGE_REP_REG = new Regex(@"(.)\{(\d+)\,(\d+)\}"); - var REP_REG = new Regex(@"(.)\{(\d+)\}"); - var RANGE_REG = new Regex(@"\[(\d+)\-(\d+)\]"); - int min, max, tmp, repetitions; - var token = RANGE_REP_REG.Match(str); - while (token.Success) - { - min = Int32.Parse(token.Groups[2].Value); - max = Int32.Parse(token.Groups[3].Value); - - if (min > max) - { - tmp = max; - max = min; - min = tmp; - } + repetitions = Int32.Parse(token.Groups[2].Value); - repetitions = this.Random.Number(min, max); + str = str.Substring(0, token.Index) + + new string(token.Groups[1].Value[0], repetitions) + + str.Substring(token.Index + token.Groups[0].Length); - str = str.Substring(0, token.Index) + - new string(token.Groups[1].Value[0], repetitions) + - str.Substring(token.Index + token.Groups[0].Length); - - token = RANGE_REP_REG.Match(str); - } - // Deal with repeat `{num}` token = REP_REG.Match(str); - while (token.Success) - { - repetitions = Int32.Parse(token.Groups[2].Value); - - str = str.Substring(0, token.Index) + - new string(token.Groups[1].Value[0], repetitions) + - str.Substring(token.Index + token.Groups[0].Length); - - token = REP_REG.Match(str); - } - // Deal with range `[min-max]` (only works with numbers for now) - //TODO: implement for letters e.g. [0-9a-zA-Z] etc. + } + // Deal with range `[min-max]` (only works with numbers for now) + //TODO: implement for letters e.g. [0-9a-zA-Z] etc. - token = RANGE_REG.Match(str); - while (token.Success) + token = RANGE_REG.Match(str); + while (token.Success) + { + min = Int32.Parse(token.Groups[1].Value); // This time we are not capturing the char before `[]` + max = Int32.Parse(token.Groups[2].Value); + // switch min and max + if (min > max) { - min = Int32.Parse(token.Groups[1].Value); // This time we are not capturing the char before `[]` - max = Int32.Parse(token.Groups[2].Value); - // switch min and max - if (min > max) - { - tmp = max; - max = min; - min = tmp; - } - str = str.Substring(0, token.Index) + - this.Random.Number(min, max) + - str.Substring(token.Index + token.Groups[0].Length); - token = RANGE_REG.Match(str); + tmp = max; + max = min; + min = tmp; } - return str; + str = str.Substring(0, token.Index) + + this.Random.Number(min, max) + + str.Substring(token.Index + token.Groups[0].Length); + token = RANGE_REG.Match(str); } + return str; } + } - /// - /// Generate a credit card CVV. - /// - public string CreditCardCvv() + /// + /// Generate a credit card CVV. + /// + public string CreditCardCvv() + { + return this.Random.Replace("###"); + } + + private static readonly char[] BtcCharset = { - return this.Random.Replace("###"); + '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + + /// + /// Generates a random Bitcoin address. + /// + public string BitcoinAddress() + { + var addressLength = this.Random.Number(25, 34); + var lastBits = new string(this.Random.ArrayElements(BtcCharset, addressLength)); + if (this.Random.Bool()) + { + return $"1{lastBits}"; } + return $"3{lastBits}"; + } - private static readonly char[] BtcCharset = - { - '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - }; + /// + /// Generate a random Ethereum address. + /// + public string EthereumAddress() + { + return Random.Hexadecimal(40); + } - /// - /// Generates a random Bitcoin address. - /// - public string BitcoinAddress() + /// + /// Generate a random Litecoin address. + /// + public string LitecoinAddress() + { + var addressLength = this.Random.Number(26, 33); + var lastBits = new string(this.Random.ArrayElements(BtcCharset, addressLength)); + var prefix = this.Random.Number(0, 2); + + if( prefix == 0 ) { - var addressLength = this.Random.Number(25, 34); - var lastBits = new string(this.Random.ArrayElements(BtcCharset, addressLength)); - if (this.Random.Bool()) - { - return $"1{lastBits}"; - } - return $"3{lastBits}"; + return $"L{lastBits}"; } - - /// - /// Generate a random Ethereum address. - /// - public string EthereumAddress() + if( prefix == 1 ) { - return Random.Hexadecimal(40); + return $"M{lastBits}"; } + return $"3{lastBits}"; + } + + /// + /// Generates an ABA routing number with valid check digit. + /// + public string RoutingNumber() + { + var digits = this.Random.Digits(8); - /// - /// Generate a random Litecoin address. - /// - public string LitecoinAddress() + var sum = 0; + for (var i = 0; i < digits.Length; i += 3) { - var addressLength = this.Random.Number(26, 33); - var lastBits = new string(this.Random.ArrayElements(BtcCharset, addressLength)); - var prefix = this.Random.Number(0, 2); - - if( prefix == 0 ) - { - return $"L{lastBits}"; - } - if( prefix == 1 ) - { - return $"M{lastBits}"; - } - return $"3{lastBits}"; + sum += 3 * digits.ElementAt(i); + sum += 7 * digits.ElementAt(i + 1); + sum += digits.ElementAtOrDefault(i + 2); } - /// - /// Generates an ABA routing number with valid check digit. - /// - public string RoutingNumber() - { - var digits = this.Random.Digits(8); + var checkDigit = Math.Ceiling(sum / 10d) * 10 - sum; - var sum = 0; - for (var i = 0; i < digits.Length; i += 3) - { - sum += 3 * digits.ElementAt(i); - sum += 7 * digits.ElementAt(i + 1); - sum += digits.ElementAtOrDefault(i + 2); - } + return digits.Aggregate("", (str, digit) => str + digit, str => str + checkDigit); + } - var checkDigit = Math.Ceiling(sum / 10d) * 10 - sum; + protected static readonly string[] BicVowels = { "A", "E", "I", "O", "U" }; - return digits.Aggregate("", (str, digit) => str + digit, str => str + checkDigit); - } + /// + /// Generates Bank Identifier Code (BIC) code. + /// + public string Bic() + { + var prob = this.Random.Number(100); + return this.Random.Replace("???") + + this.Random.ArrayElement(BicVowels) + + this.Random.ArrayElement(IbanIso3166) + + this.Random.Replace("?") + "1" + + (prob < 10 ? this.Random.Replace("?" + this.Random.ArrayElement(BicVowels) + "?") : prob < 40 ? this.Random.Replace("###") : ""); + } - protected static readonly string[] BicVowels = { "A", "E", "I", "O", "U" }; + /// + /// Generates an International Bank Account Number (IBAN). + /// + /// Formatted IBAN containing spaces. + /// A two letter ISO3166 country code. Throws an exception if the country code is not found or is an invalid length. + /// An exception is thrown if the ISO3166 country code is not found. + public string Iban(bool formatted = false, string countryCode = null) + { + var arr = this.GetArray("iban_formats"); - /// - /// Generates Bank Identifier Code (BIC) code. - /// - public string Bic() + IBanFormat ibanFormat; + if( countryCode is null ) { - var prob = this.Random.Number(100); - return this.Random.Replace("???") + - this.Random.ArrayElement(BicVowels) + - this.Random.ArrayElement(IbanIso3166) + - this.Random.Replace("?") + "1" + - (prob < 10 ? this.Random.Replace("?" + this.Random.ArrayElement(BicVowels) + "?") : prob < 40 ? this.Random.Replace("###") : ""); + var formatEntry = this.Random.ArrayElement(arr) as BObject; + ibanFormat = this.GetIbanFormat(formatEntry); } - - /// - /// Generates an International Bank Account Number (IBAN). - /// - /// Formatted IBAN containing spaces. - /// A two letter ISO3166 country code. Throws an exception if the country code is not found or is an invalid length. - /// An exception is thrown if the ISO3166 country code is not found. - public string Iban(bool formatted = false, string countryCode = null) + else { - var arr = this.GetArray("iban_formats"); - - IBanFormat ibanFormat; - if( countryCode is null ) + if( countryCode.Length != 2 ) { - var formatEntry = this.Random.ArrayElement(arr) as BObject; - ibanFormat = this.GetIbanFormat(formatEntry); + throw new ArgumentOutOfRangeException(nameof(countryCode), countryCode.Length, "The country code must be an ISO3166 two-letter country code."); } - else - { - if( countryCode.Length != 2 ) - { - throw new ArgumentOutOfRangeException(nameof(countryCode), countryCode.Length, "The country code must be an ISO3166 two-letter country code."); - } - var formatEntry = arr.OfType() - .Where(b => countryCode.Equals(b["country"].StringValue, StringComparison.OrdinalIgnoreCase)) - .FirstOrDefault(); - - if (formatEntry is null) - { - throw new KeyNotFoundException($"The ISO3166 IBAN country code '{countryCode}' was not found."); - } + var formatEntry = arr.OfType() + .Where(b => countryCode.Equals(b["country"].StringValue, StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); - ibanFormat = this.GetIbanFormat(formatEntry); + if (formatEntry is null) + { + throw new KeyNotFoundException($"The ISO3166 IBAN country code '{countryCode}' was not found."); } - return Iban(ibanFormat, formatted); + ibanFormat = this.GetIbanFormat(formatEntry); } - protected string Iban(IBanFormat ibanFormat, bool formatted) + return Iban(ibanFormat, formatted); + } + + protected string Iban(IBanFormat ibanFormat, bool formatted) + { + var stringBuilder = new StringBuilder(); + var count = 0; + for (var b = 0; b < ibanFormat.Bban.Length; b++) { - var stringBuilder = new StringBuilder(); - var count = 0; - for (var b = 0; b < ibanFormat.Bban.Length; b++) + var bban = ibanFormat.Bban[b]; + var c = bban.Count; + count += bban.Count; + while (c > 0) { - var bban = ibanFormat.Bban[b]; - var c = bban.Count; - count += bban.Count; - while (c > 0) + if (bban.Type == "a") { - if (bban.Type == "a") + stringBuilder.Append(this.Random.ArrayElement(IbanAlpha)); + } + else if (bban.Type == "c") + { + if (this.Random.Number(100) < 80) + { + stringBuilder.Append(this.Random.Number(9)); + } + else { stringBuilder.Append(this.Random.ArrayElement(IbanAlpha)); } - else if (bban.Type == "c") + } + else + { + if (c >= 3 && this.Random.Number(100) < 30) { - if (this.Random.Number(100) < 80) + if (this.Random.Bool()) { - stringBuilder.Append(this.Random.Number(9)); + stringBuilder.Append(this.Random.ArrayElement(IbanPattern100)); + c -= 2; } else { - stringBuilder.Append(this.Random.ArrayElement(IbanAlpha)); + stringBuilder.Append(this.Random.ArrayElement(IbanPattern10)); + c--; } } else { - if (c >= 3 && this.Random.Number(100) < 30) - { - if (this.Random.Bool()) - { - stringBuilder.Append(this.Random.ArrayElement(IbanPattern100)); - c -= 2; - } - else - { - stringBuilder.Append(this.Random.ArrayElement(IbanPattern10)); - c--; - } - } - else - { - stringBuilder.Append(this.Random.Number(9)); - } + stringBuilder.Append(this.Random.Number(9)); } - c--; } - - stringBuilder = stringBuilder.Remove(count, stringBuilder.Length - count); + c--; } - var checksum = 98 - IbanMod97(IbanToDigitString(stringBuilder + ibanFormat.Country + "00")); - var iban = ibanFormat.Country + checksum.ToString("00") + stringBuilder; - if (formatted) - { - var matches = Regex.Matches(iban, ".{1,4}"); - var array = matches.OfType() - .Select(m => m.Value) - .ToArray(); - return string.Join(" ", array); - } - return iban; + stringBuilder = stringBuilder.Remove(count, stringBuilder.Length - count); } + var checksum = 98 - IbanMod97(IbanToDigitString(stringBuilder + ibanFormat.Country + "00")); + var iban = ibanFormat.Country + checksum.ToString("00") + stringBuilder; - protected int IbanMod97(string digitStr) + if (formatted) { - var m = 0; - for (int i = 0; i < digitStr.Length; i++) - { - m = ((m * 10) + (digitStr[i] - '0')) % 97; - } - return m; - } - - protected string IbanToDigitString(string str) - { - return Regex.Replace(str, "[A-Z]", (m) => (Convert.ToChar(m.Value) - 55).ToString()); + var matches = Regex.Matches(iban, ".{1,4}"); + var array = matches.OfType() + .Select(m => m.Value) + .ToArray(); + return string.Join(" ", array); } + return iban; + } - protected class IBanFormat + protected int IbanMod97(string digitStr) + { + var m = 0; + for (int i = 0; i < digitStr.Length; i++) { - public class BbanItem - { - public string Type { get; set; } - public int Count { get; set; } - } - - public string Country { get; set; } - public int Total { get; set; } - public BbanItem[] Bban { get; set; } - public string Format { get; set; } + m = ((m * 10) + (digitStr[i] - '0')) % 97; } + return m; + } - protected IBanFormat GetIbanFormat(BObject obj) - { - var bbitems = GetBbanItems(obj); - - return new IBanFormat - { - Country = obj["country"].StringValue, - Total = obj["total"].Int32Value, - Format = obj["format"].StringValue, - Bban = bbitems - }; - } + protected string IbanToDigitString(string str) + { + return Regex.Replace(str, "[A-Z]", (m) => (Convert.ToChar(m.Value) - 55).ToString()); + } - protected IBanFormat.BbanItem[] GetBbanItems(BObject obj) + protected class IBanFormat + { + public class BbanItem { - var arr = obj["bban"] as BArray; - return arr.OfType() - .Select(o => new IBanFormat.BbanItem - { - Count = o["count"].Int32Value, - Type = o["type"].StringValue - }) - .ToArray(); + public string Type { get; set; } + public int Count { get; set; } } - protected static readonly string[] IbanAlpha = - {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; - - protected static readonly string[] IbanPattern10 = { "01", "02", "03", "04", "05", "06", "07", "08", "09" }; + public string Country { get; set; } + public int Total { get; set; } + public BbanItem[] Bban { get; set; } + public string Format { get; set; } + } - protected static readonly string[] IbanPattern100 = { "001", "002", "003", "004", "005", "006", "007", "008", "009" }; + protected IBanFormat GetIbanFormat(BObject obj) + { + var bbitems = GetBbanItems(obj); - protected static readonly string[] IbanIso3166 = + return new IBanFormat { - "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", - "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BU", - "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CP", - "CR", "CS", "CS", "CU", "CV", "CW", "CX", "CY", "CZ", "DD", "DE", "DG", "DJ", "DK", "DM", "DO", "DZ", "EA", - "EC", "EE", "EG", "EH", "ER", "ES", "ET", "EU", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", "GD", - "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", - "HN", "HR", "HT", "HU", "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", "JO", - "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", - "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", - "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", - "NP", "NR", "NT", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", - "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", - "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SU", "SV", "SX", "SY", "SZ", "TA", "TC", "TD", "TF", "TG", "TH", - "TJ", "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", - "VC", "VE", "VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "YU", "ZA", "ZM", "ZR", "ZW" + Country = obj["country"].StringValue, + Total = obj["total"].Int32Value, + Format = obj["format"].StringValue, + Bban = bbitems }; } + + protected IBanFormat.BbanItem[] GetBbanItems(BObject obj) + { + var arr = obj["bban"] as BArray; + return arr.OfType() + .Select(o => new IBanFormat.BbanItem + { + Count = o["count"].Int32Value, + Type = o["type"].StringValue + }) + .ToArray(); + } + + protected static readonly string[] IbanAlpha = + {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; + + protected static readonly string[] IbanPattern10 = { "01", "02", "03", "04", "05", "06", "07", "08", "09" }; + + protected static readonly string[] IbanPattern100 = { "001", "002", "003", "004", "005", "006", "007", "008", "009" }; + + protected static readonly string[] IbanIso3166 = + { + "AC", "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AN", "AO", "AQ", "AR", "AS", "AT", "AU", "AW", "AX", "AZ", + "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BU", + "BV", "BW", "BY", "BZ", "CA", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", "CO", "CP", + "CR", "CS", "CS", "CU", "CV", "CW", "CX", "CY", "CZ", "DD", "DE", "DG", "DJ", "DK", "DM", "DO", "DZ", "EA", + "EC", "EE", "EG", "EH", "ER", "ES", "ET", "EU", "FI", "FJ", "FK", "FM", "FO", "FR", "FX", "GA", "GB", "GD", + "GE", "GF", "GG", "GH", "GI", "GL", "GM", "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", + "HN", "HR", "HT", "HU", "IC", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", "IT", "JE", "JM", "JO", + "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", + "LS", "LT", "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "ML", "MM", "MN", "MO", "MP", + "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", + "NP", "NR", "NT", "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", "PR", "PS", "PT", + "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", + "SL", "SM", "SN", "SO", "SR", "SS", "ST", "SU", "SV", "SX", "SY", "SZ", "TA", "TC", "TD", "TF", "TG", "TH", + "TJ", "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", + "VC", "VE", "VG", "VI", "VN", "VU", "WF", "WS", "YE", "YT", "YU", "ZA", "ZM", "ZR", "ZW" + }; } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Hacker.cs b/Source/Bogus/DataSets/Hacker.cs index 5db00a76..8f2c9ec1 100644 --- a/Source/Bogus/DataSets/Hacker.cs +++ b/Source/Bogus/DataSets/Hacker.cs @@ -1,76 +1,75 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Hackerish words +/// +public class Hacker : DataSet { /// - /// Hackerish words + /// Initializes a new instance of the class. /// - public class Hacker : DataSet + /// The locale that will be used to generate values. + public Hacker(string locale = "en") : base(locale) { - /// - /// Initializes a new instance of the class. - /// - /// The locale that will be used to generate values. - public Hacker(string locale = "en") : base(locale) - { - } + } - /// - /// Returns an abbreviation. - /// - /// A random abbreviation. - public string Abbreviation() - { - return GetRandomArrayItem("abbreviation"); - } + /// + /// Returns an abbreviation. + /// + /// A random abbreviation. + public string Abbreviation() + { + return GetRandomArrayItem("abbreviation"); + } - /// - /// Returns a adjective. - /// - /// A random adjective. - public string Adjective() - { - return GetRandomArrayItem("adjective"); - } + /// + /// Returns a adjective. + /// + /// A random adjective. + public string Adjective() + { + return GetRandomArrayItem("adjective"); + } - /// - /// Returns a noun. - /// - /// A random noun. - public string Noun() - { - return GetRandomArrayItem("noun"); - } + /// + /// Returns a noun. + /// + /// A random noun. + public string Noun() + { + return GetRandomArrayItem("noun"); + } - /// - /// Returns a verb. - /// - /// A random verb. - public string Verb() - { - return GetRandomArrayItem("verb"); - } + /// + /// Returns a verb. + /// + /// A random verb. + public string Verb() + { + return GetRandomArrayItem("verb"); + } - /// - /// Returns a verb ending with -ing. - /// - /// A random -ing verb. - public string IngVerb() - { - return GetRandomArrayItem("ingverb"); - } + /// + /// Returns a verb ending with -ing. + /// + /// A random -ing verb. + public string IngVerb() + { + return GetRandomArrayItem("ingverb"); + } - /// - /// Returns a phrase. - /// - /// A random phrase. - public string Phrase() - { - var phrase = GetRandomArrayItem("phrase"); + /// + /// Returns a phrase. + /// + /// A random phrase. + public string Phrase() + { + var phrase = GetRandomArrayItem("phrase"); - return phrase.Replace("{{abbreviation}}", Abbreviation()) - .Replace("{{adjective}}", Adjective()) - .Replace("{{ingverb}}", IngVerb()) - .Replace("{{noun}}", Noun()) - .Replace("{{verb}}", Verb()); - } + return phrase.Replace("{{abbreviation}}", Abbreviation()) + .Replace("{{adjective}}", Adjective()) + .Replace("{{ingverb}}", IngVerb()) + .Replace("{{noun}}", Noun()) + .Replace("{{verb}}", Verb()); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Images.LoremPixel.cs b/Source/Bogus/DataSets/Images.LoremPixel.cs index bcd01d9e..369dc762 100644 --- a/Source/Bogus/DataSets/Images.LoremPixel.cs +++ b/Source/Bogus/DataSets/Images.LoremPixel.cs @@ -1,203 +1,202 @@ using System; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +public static class LoremPixelCategory +{ + public const string Abstract = "abstract"; + public const string Animals = "animals"; + public const string Business = "business"; + public const string Cats = "cats"; + public const string City = "city"; + public const string Food = "food"; + public const string Nightlife = "nightlife"; + public const string Fashion = "fashion"; + public const string People = "people"; + public const string Nature = "nature"; + public const string Sports = "sports"; + public const string Technics = "technics"; + public const string Transport = "transport"; + public const string Random = "random"; +} + +public partial class Images { - public static class LoremPixelCategory + /// + /// Gets a random LoremPixel.com image. + /// + [Obsolete("Please use Images.LoremPixelUrl(). Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Image(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl(LoremPixelCategory.Random, width, height, randomize, https); + } + + /// + /// Gets an abstract looking image. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Abstract. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Abstract(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("abstract", width, height, randomize, https); + } + + /// + /// Gets an image of an animal. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Animals. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Animals(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("animals", width, height, randomize, https); + } + + /// + /// Gets a business looking image. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Business. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Business(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("business", width, height, randomize, https); + } + + /// + /// Gets a picture of a cat. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Cats. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Cats(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("cats", width, height, randomize, https); + } + + /// + /// Gets a city looking image. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.City. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string City(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("city", width, height, randomize, https); + } + + /// + /// Gets an image of food. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Food. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Food(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("food", width, height, randomize, https); + } + + /// + /// Gets an image with city looking nightlife. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Nightlife. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Nightlife(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("nightlife", width, height, randomize, https); + } + + /// + /// Gets an image in the fashion category. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Fashion. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Fashion(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("fashion", width, height, randomize, https); + } + + /// + /// Gets an image of humans. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.People. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string People(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("people", width, height, randomize, https); + } + + /// + /// Gets an image of nature. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Nature. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Nature(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("nature", width, height, randomize, https); + } + + /// + /// Gets an image related to sports. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Sports. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Sports(int width = 640, int height = 480, bool randomize = false, bool https = false) + { + return LoremPixelUrl("sports", width, height, randomize, https); + } + + /// + /// Get a technology related image. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Technics. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Technics(int width = 640, int height = 480, bool randomize = false, bool https = false) { - public const string Abstract = "abstract"; - public const string Animals = "animals"; - public const string Business = "business"; - public const string Cats = "cats"; - public const string City = "city"; - public const string Food = "food"; - public const string Nightlife = "nightlife"; - public const string Fashion = "fashion"; - public const string People = "people"; - public const string Nature = "nature"; - public const string Sports = "sports"; - public const string Technics = "technics"; - public const string Transport = "transport"; - public const string Random = "random"; + return LoremPixelUrl("technics", width, height, randomize, https); } - public partial class Images + /// + /// Get a transportation related image. + /// + /// Width + /// Height + /// Adds a random cache busting number to the URL + /// Uses https:// protocol + [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Transport. Consider using PicsumUrl() method as a more reliable/faster service.")] + public string Transport(int width = 640, int height = 480, bool randomize = false, bool https = false) { - /// - /// Gets a random LoremPixel.com image. - /// - [Obsolete("Please use Images.LoremPixelUrl(). Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Image(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl(LoremPixelCategory.Random, width, height, randomize, https); - } - - /// - /// Gets an abstract looking image. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Abstract. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Abstract(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("abstract", width, height, randomize, https); - } - - /// - /// Gets an image of an animal. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Animals. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Animals(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("animals", width, height, randomize, https); - } - - /// - /// Gets a business looking image. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Business. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Business(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("business", width, height, randomize, https); - } - - /// - /// Gets a picture of a cat. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Cats. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Cats(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("cats", width, height, randomize, https); - } - - /// - /// Gets a city looking image. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.City. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string City(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("city", width, height, randomize, https); - } - - /// - /// Gets an image of food. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Food. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Food(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("food", width, height, randomize, https); - } - - /// - /// Gets an image with city looking nightlife. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Nightlife. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Nightlife(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("nightlife", width, height, randomize, https); - } - - /// - /// Gets an image in the fashion category. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Fashion. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Fashion(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("fashion", width, height, randomize, https); - } - - /// - /// Gets an image of humans. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.People. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string People(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("people", width, height, randomize, https); - } - - /// - /// Gets an image of nature. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Nature. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Nature(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("nature", width, height, randomize, https); - } - - /// - /// Gets an image related to sports. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Sports. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Sports(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("sports", width, height, randomize, https); - } - - /// - /// Get a technology related image. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Technics. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Technics(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("technics", width, height, randomize, https); - } - - /// - /// Get a transportation related image. - /// - /// Width - /// Height - /// Adds a random cache busting number to the URL - /// Uses https:// protocol - [Obsolete("Please use Images.LoremPixelUrl() method with category:LoremPixelCategory.Transport. Consider using PicsumUrl() method as a more reliable/faster service.")] - public string Transport(int width = 640, int height = 480, bool randomize = false, bool https = false) - { - return LoremPixelUrl("transport", width, height, randomize, https); - } + return LoremPixelUrl("transport", width, height, randomize, https); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Images.PlaceImg.cs b/Source/Bogus/DataSets/Images.PlaceImg.cs index ad79c871..79f2b3ab 100644 --- a/Source/Bogus/DataSets/Images.PlaceImg.cs +++ b/Source/Bogus/DataSets/Images.PlaceImg.cs @@ -1,18 +1,17 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +public enum PlaceImgFilter { - public enum PlaceImgFilter - { - Grayscale, - Sepia - } + Grayscale, + Sepia +} - public static class PlaceImgCategory - { - public const string Animals = "animals"; - public const string Architecture = "arch"; - public const string Nature = "nature"; - public const string People = "people"; - public const string Tech = "tech"; - public const string Any = "any"; - } +public static class PlaceImgCategory +{ + public const string Animals = "animals"; + public const string Architecture = "arch"; + public const string Nature = "nature"; + public const string People = "people"; + public const string Tech = "tech"; + public const string Any = "any"; } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Images.cs b/Source/Bogus/DataSets/Images.cs index 5196ec8a..8838c941 100644 --- a/Source/Bogus/DataSets/Images.cs +++ b/Source/Bogus/DataSets/Images.cs @@ -1,231 +1,230 @@ using System; using System.Text; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates images URLs. +/// +[DataCategory("image")] +public partial class Images : DataSet { /// - /// Generates images URLs. + /// Default constructor /// - [DataCategory("image")] - public partial class Images : DataSet + public Images(string locale = "en") : base(locale) { - /// - /// Default constructor - /// - public Images(string locale = "en") : base(locale) - { - } - - /// - /// Get a SVG data URI image with a specific width and height. - /// - /// Width of the image. - /// Height of the image. - /// An html color in named format 'grey', RGB format 'rgb(r,g,b)', or hex format '#888888'. - public string DataUri(int width, int height, string htmlColor = "grey") - { - var rawPrefix = "data:image/svg+xml;charset=UTF-8,"; - var svgString = - $@"{width}x{height}"; - - return rawPrefix + Uri.EscapeDataString(svgString); - } + } - /// - /// Get an image from the https://placeimg.com service. - /// - /// Width of the image. - /// Height of the image. - /// Category of the image. See for string categories. - /// The filter to apply to the image. See . - /// - public string PlaceImgUrl( - int width = 640, int height = 480, - string category = PlaceImgCategory.Any, - PlaceImgFilter? filter = null) - { - //https://placeimg.com/640/480/nature/grayscale - //https://placeimg.com/640/480/nature - //https://placeimg.com/640/480/any/sepia - //https://placeimg.com/640/480/any + /// + /// Get a SVG data URI image with a specific width and height. + /// + /// Width of the image. + /// Height of the image. + /// An html color in named format 'grey', RGB format 'rgb(r,g,b)', or hex format '#888888'. + public string DataUri(int width, int height, string htmlColor = "grey") + { + var rawPrefix = "data:image/svg+xml;charset=UTF-8,"; + var svgString = + $@"{width}x{height}"; - const string Url = "https://placeimg.com"; + return rawPrefix + Uri.EscapeDataString(svgString); + } - var sb = new StringBuilder(Url); - sb.Append($"/{width}/{height}/{category}"); + /// + /// Get an image from the https://placeimg.com service. + /// + /// Width of the image. + /// Height of the image. + /// Category of the image. See for string categories. + /// The filter to apply to the image. See . + /// + public string PlaceImgUrl( + int width = 640, int height = 480, + string category = PlaceImgCategory.Any, + PlaceImgFilter? filter = null) + { + //https://placeimg.com/640/480/nature/grayscale + //https://placeimg.com/640/480/nature + //https://placeimg.com/640/480/any/sepia + //https://placeimg.com/640/480/any - if (filter is PlaceImgFilter.Grayscale) - { - sb.Append("/grayscale"); - } - else if ( filter is PlaceImgFilter.Sepia ) - { - sb.Append("/sepia"); - } + const string Url = "https://placeimg.com"; - return sb.ToString(); + var sb = new StringBuilder(Url); + sb.Append($"/{width}/{height}/{category}"); + if (filter is PlaceImgFilter.Grayscale) + { + sb.Append("/grayscale"); } - - /// - /// Get an image from the https://picsum.photos service. - /// - /// Width of the image. - /// Height of the image. - /// Grayscale (no color) image. - /// Blurry image. - /// Optional Image ID found here https://picsum.photos/images - public string PicsumUrl(int width = 640, int height = 480, bool grayscale = false, bool blur = false, int? imageId = null ) + else if ( filter is PlaceImgFilter.Sepia ) { - const string Url = "https://picsum.photos"; + sb.Append("/sepia"); + } - var sb = new StringBuilder(Url); + return sb.ToString(); - if (grayscale) - { - sb.Append("/g"); - } + } - sb.Append($"/{width}/{height}"); - - var n = imageId ?? this.Random.Number(0, 1084); - sb.Append($"/?image={n}"); + /// + /// Get an image from the https://picsum.photos service. + /// + /// Width of the image. + /// Height of the image. + /// Grayscale (no color) image. + /// Blurry image. + /// Optional Image ID found here https://picsum.photos/images + public string PicsumUrl(int width = 640, int height = 480, bool grayscale = false, bool blur = false, int? imageId = null ) + { + const string Url = "https://picsum.photos"; - if (blur) - { - sb.Append("&blur"); - } + var sb = new StringBuilder(Url); - return sb.ToString(); + if (grayscale) + { + sb.Append("/g"); } - /// - /// Get an image from https://placeholder.com service. - /// - /// Width of the image. - /// Height of the image. - /// - /// Image format. Supported values: 'jpg', 'jpeg', 'png', 'gif', 'webp'. - /// HTML color code for the background color. - /// HTML color code for the foreground (text) color. - public string PlaceholderUrl(int width, int height, string text = null, string backColor = "cccccc", string textColor = "9c9c9c", string format = "png") + sb.Append($"/{width}/{height}"); + + var n = imageId ?? this.Random.Number(0, 1084); + sb.Append($"/?image={n}"); + + if (blur) { - const string Url = "https://via.placeholder.com/"; + sb.Append("&blur"); + } - var sb = new StringBuilder(Url); + return sb.ToString(); + } - sb.Append(width) - .Append("x") - .Append(height) - .Append("/") - .Append(backColor) - .Append("/") - .Append(textColor) - .Append(".") - .Append(format); + /// + /// Get an image from https://placeholder.com service. + /// + /// Width of the image. + /// Height of the image. + /// + /// Image format. Supported values: 'jpg', 'jpeg', 'png', 'gif', 'webp'. + /// HTML color code for the background color. + /// HTML color code for the foreground (text) color. + public string PlaceholderUrl(int width, int height, string text = null, string backColor = "cccccc", string textColor = "9c9c9c", string format = "png") + { + const string Url = "https://via.placeholder.com/"; - if( text != null ) - { - sb.Append("?text=") - .Append(Uri.EscapeUriString(text)); - } + var sb = new StringBuilder(Url); - return sb.ToString(); - } + sb.Append(width) + .Append("x") + .Append(height) + .Append("/") + .Append(backColor) + .Append("/") + .Append(textColor) + .Append(".") + .Append(format); - /// - /// Get an image from https://loremflickr.com service. - /// - /// Space or comma delimited list of keywords you want the picture to contain. IE: "cat, dog" for images with cats and dogs. - /// The image width. - /// The image height. - /// Grayscale the image. - /// True tries to match an image with all specified keywords. False tries to match an image with any specified keyword. - /// Deterministic image id. By default, this method generates URLs with image lock ids. - /// So, if a random seed is set, repeat runs of this method will generate the same lock id sequence - /// for images. If you want explicit control over the lock id, you can pass it as a parameter here. - /// Additionally, if you don't want any lock ids, pass -1 for this parameter this method will generate - /// a URL that will result in a new random image every time the HTTP URL is hit. - /// - public string LoremFlickrUrl( - int width = 320, int height = 240, - string keywords = null, - bool grascale = false, - bool matchAllKeywords = false, int? lockId = null) + if( text != null ) { - const string Url = "https://loremflickr.com"; + sb.Append("?text=") + .Append(Uri.EscapeUriString(text)); + } - var sb = new StringBuilder(); - if (grascale) - { - sb.Append("/g"); - } + return sb.ToString(); + } - sb.Append($"/{width}/{height}"); + /// + /// Get an image from https://loremflickr.com service. + /// + /// Space or comma delimited list of keywords you want the picture to contain. IE: "cat, dog" for images with cats and dogs. + /// The image width. + /// The image height. + /// Grayscale the image. + /// True tries to match an image with all specified keywords. False tries to match an image with any specified keyword. + /// Deterministic image id. By default, this method generates URLs with image lock ids. + /// So, if a random seed is set, repeat runs of this method will generate the same lock id sequence + /// for images. If you want explicit control over the lock id, you can pass it as a parameter here. + /// Additionally, if you don't want any lock ids, pass -1 for this parameter this method will generate + /// a URL that will result in a new random image every time the HTTP URL is hit. + /// + public string LoremFlickrUrl( + int width = 320, int height = 240, + string keywords = null, + bool grascale = false, + bool matchAllKeywords = false, int? lockId = null) + { + const string Url = "https://loremflickr.com"; - if (keywords != null) - { - var tags = keywords.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); - var cleanTags = string.Join(",", tags); - var match = matchAllKeywords ? "all" : "any"; - sb.Append($"/{cleanTags}/{match}"); - } + var sb = new StringBuilder(); + if (grascale) + { + sb.Append("/g"); + } - if( lockId is null ) - { - lockId = this.Random.Number(int.MaxValue); - } - if( lockId >= 0 ) - { - sb.Append($"?lock={lockId}"); - } + sb.Append($"/{width}/{height}"); - return Url + sb; + if (keywords != null) + { + var tags = keywords.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + var cleanTags = string.Join(",", tags); + var match = matchAllKeywords ? "all" : "any"; + sb.Append($"/{cleanTags}/{match}"); } - /// - /// Creates an image URL with http://lorempixel.com. Note: This service is slow. Consider using PicsumUrl() as a faster alternative. - /// - public string LoremPixelUrl(string category = LoremPixelCategory.Random, int width = 640, int height = 480, bool randomize = false, bool https = false) + if( lockId is null ) { - if( category == LoremPixelCategory.Random ) - { - var categories = new[] - { - LoremPixelCategory.Abstract, - LoremPixelCategory.Animals, - LoremPixelCategory.Business, - LoremPixelCategory.Cats, - LoremPixelCategory.City, - LoremPixelCategory.Food, - LoremPixelCategory.Nightlife, - LoremPixelCategory.Fashion, - LoremPixelCategory.People, - LoremPixelCategory.Nature, - LoremPixelCategory.Sports, - LoremPixelCategory.Technics, - LoremPixelCategory.Transport - }; - - category = this.Random.ArrayElement(categories); - } + lockId = this.Random.Number(int.MaxValue); + } + if( lockId >= 0 ) + { + sb.Append($"?lock={lockId}"); + } - var proto = "http://"; - if( https ) - { - proto = "https://"; - } - var url = $"{proto}lorempixel.com/{width}/{height}"; - if( !string.IsNullOrWhiteSpace(category) ) - { - url += $"/{category}"; - if( randomize ) + return Url + sb; + } + + /// + /// Creates an image URL with http://lorempixel.com. Note: This service is slow. Consider using PicsumUrl() as a faster alternative. + /// + public string LoremPixelUrl(string category = LoremPixelCategory.Random, int width = 640, int height = 480, bool randomize = false, bool https = false) + { + if( category == LoremPixelCategory.Random ) + { + var categories = new[] { - url += $"/{this.Random.Number(1, 10)}"; - } - } + LoremPixelCategory.Abstract, + LoremPixelCategory.Animals, + LoremPixelCategory.Business, + LoremPixelCategory.Cats, + LoremPixelCategory.City, + LoremPixelCategory.Food, + LoremPixelCategory.Nightlife, + LoremPixelCategory.Fashion, + LoremPixelCategory.People, + LoremPixelCategory.Nature, + LoremPixelCategory.Sports, + LoremPixelCategory.Technics, + LoremPixelCategory.Transport + }; + + category = this.Random.ArrayElement(categories); + } - return url; + var proto = "http://"; + if( https ) + { + proto = "https://"; } + var url = $"{proto}lorempixel.com/{width}/{height}"; + if( !string.IsNullOrWhiteSpace(category) ) + { + url += $"/{category}"; + if( randomize ) + { + url += $"/{this.Random.Number(1, 10)}"; + } + } + + return url; } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Internet.cs b/Source/Bogus/DataSets/Internet.cs index 4b688351..dc149800 100644 --- a/Source/Bogus/DataSets/Internet.cs +++ b/Source/Bogus/DataSets/Internet.cs @@ -6,386 +6,385 @@ using Bogus.Extensions; using Bogus.Vendor; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Random Internet things like email addresses +/// +public class Internet : DataSet { /// - /// Random Internet things like email addresses + /// The source to pull names from. /// - public class Internet : DataSet - { - /// - /// The source to pull names from. - /// - protected Name Name = null; - - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate values. - public Internet(string locale = "en") : base(locale) - { - this.Name = this.Notifier.Flow(new Name(locale)); - this.userAgentGenerator = new UserAgentGenerator(() => this.Random); - } - - /// - /// Generates a legit Internet URL avatar from twitter accounts. - /// - /// A string containing a URL avatar from twitter accounts. - public string Avatar() - { - var n = this.Random.Number(0, 1249); - return $"https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/{n}.jpg"; - } + protected Name Name = null; - /// - /// Generates an email address. - /// - /// Always use this first name. - /// Sometimes used depending on randomness. See 'UserName'. - /// Always use the provider. - /// This parameter is appended to - /// the email account just before the @ symbol. This is useful for situations - /// where you might have a unique email constraint in your database or application. - /// Passing var f = new Faker(); f.UniqueIndex is a good choice. Or you can supply - /// your own unique changing suffix too like Guid.NewGuid; just be sure to change the - /// value each time before calling this method - /// to ensure that email accounts that are generated are totally unique. - /// An email address - public string Email(string firstName = null, string lastName = null, string provider = null, string uniqueSuffix = null) - { - provider ??= GetRandomArrayItem("free_email"); + /// + /// Initializes a new instance of the class. + /// + /// The locale used to generate values. + public Internet(string locale = "en") : base(locale) + { + this.Name = this.Notifier.Flow(new Name(locale)); + this.userAgentGenerator = new UserAgentGenerator(() => this.Random); + } - return UserName(firstName, lastName) + uniqueSuffix + "@" + provider; - } + /// + /// Generates a legit Internet URL avatar from twitter accounts. + /// + /// A string containing a URL avatar from twitter accounts. + public string Avatar() + { + var n = this.Random.Number(0, 1249); + return $"https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/{n}.jpg"; + } - /// - /// Generates an example email with @example.com. - /// - /// Optional: first name of the user. - /// Optional: last name of the user. - /// An example email ending with @example.com. - public string ExampleEmail(string firstName = null, string lastName = null) - { - var provider = GetRandomArrayItem("example_email"); - return Email(firstName, lastName, provider); - } + /// + /// Generates an email address. + /// + /// Always use this first name. + /// Sometimes used depending on randomness. See 'UserName'. + /// Always use the provider. + /// This parameter is appended to + /// the email account just before the @ symbol. This is useful for situations + /// where you might have a unique email constraint in your database or application. + /// Passing var f = new Faker(); f.UniqueIndex is a good choice. Or you can supply + /// your own unique changing suffix too like Guid.NewGuid; just be sure to change the + /// value each time before calling this method + /// to ensure that email accounts that are generated are totally unique. + /// An email address + public string Email(string firstName = null, string lastName = null, string provider = null, string uniqueSuffix = null) + { + provider ??= GetRandomArrayItem("free_email"); - /// - /// Generates user names. - /// - /// First name is always part of the returned user name. - /// Last name may or may not be used. - /// A random user name. - public string UserName(string firstName = null, string lastName = null) - { - firstName ??= Name.FirstName(); - lastName ??= Name.LastName(); + return UserName(firstName, lastName) + uniqueSuffix + "@" + provider; + } - firstName = firstName.Transliterate(this.Locale); - lastName = lastName.Transliterate(this.Locale); + /// + /// Generates an example email with @example.com. + /// + /// Optional: first name of the user. + /// Optional: last name of the user. + /// An example email ending with @example.com. + public string ExampleEmail(string firstName = null, string lastName = null) + { + var provider = GetRandomArrayItem("example_email"); + return Email(firstName, lastName, provider); + } - return Utils.Slugify(UserNameUnicode(firstName, lastName)); - } + /// + /// Generates user names. + /// + /// First name is always part of the returned user name. + /// Last name may or may not be used. + /// A random user name. + public string UserName(string firstName = null, string lastName = null) + { + firstName ??= Name.FirstName(); + lastName ??= Name.LastName(); - /// - /// Generates a user name preserving Unicode characters. - /// - /// First name is always part of the returned user name. - /// Last name may or may not be used. - public string UserNameUnicode(string firstName = null, string lastName = null) - { - firstName ??= Name.FirstName(); - lastName ??= Name.LastName(); + firstName = firstName.Transliterate(this.Locale); + lastName = lastName.Transliterate(this.Locale); - var val = Random.Number(2); + return Utils.Slugify(UserNameUnicode(firstName, lastName)); + } - string result; + /// + /// Generates a user name preserving Unicode characters. + /// + /// First name is always part of the returned user name. + /// Last name may or may not be used. + public string UserNameUnicode(string firstName = null, string lastName = null) + { + firstName ??= Name.FirstName(); + lastName ??= Name.LastName(); - if (val == 0) - { - result = firstName + Random.Number(99); - } - else if (val == 1) - { - result = firstName + Random.ArrayElement(new[] { ".", "_" }) + lastName; - } - else - { - result = firstName + Random.ArrayElement(new[] { ".", "_" }) + lastName + Random.Number(99); - } + var val = Random.Number(2); - result = result.Replace(" ", string.Empty); - return result; - } + string result; - /// - /// Generates a random domain name. - /// - /// A random domain name. - public string DomainName() + if (val == 0) { - return DomainWord() + "." + DomainSuffix(); + result = firstName + Random.Number(99); } - - /// - /// Generates a domain word used for domain names. - /// - /// A random domain word. - public string DomainWord() + else if (val == 1) { - var domain = Name.FirstName().ToLower(); - - return Regex.Replace(domain, @"([\\ ~#&*{}/:<>?|\""'])", string.Empty); + result = firstName + Random.ArrayElement(new[] { ".", "_" }) + lastName; } - - /// - /// Generates a domain name suffix like .com, .net, .org - /// - /// A random domain suffix. - public string DomainSuffix() + else { - return GetRandomArrayItem("domain_suffix"); + result = firstName + Random.ArrayElement(new[] { ".", "_" }) + lastName + Random.Number(99); } - /// - /// Gets a random IPv4 address string. - /// - /// A random IPv4 address. - public string Ip() - { - return $"{Random.Number(1, 255)}.{Random.Number(255)}.{Random.Number(255)}.{Random.Number(255)}"; - } + result = result.Replace(" ", string.Empty); + return result; + } - /// - /// Generates a random port number. - /// - /// A random port number - public int Port() - { - return this.Random.Number(min: IPEndPoint.MinPort + 1, max: IPEndPoint.MaxPort); - } + /// + /// Generates a random domain name. + /// + /// A random domain name. + public string DomainName() + { + return DomainWord() + "." + DomainSuffix(); + } - /// - /// Gets a random IPv4 IPAddress type. - /// - public IPAddress IpAddress() - { - var bytes = this.Random.Bytes(4); - if( bytes[0] == 0 ) bytes[0]++; - var address = new IPAddress(bytes); - return address; - } + /// + /// Generates a domain word used for domain names. + /// + /// A random domain word. + public string DomainWord() + { + var domain = Name.FirstName().ToLower(); - /// - /// Gets a random IPv4 IPEndPoint. - /// - /// A random IPv4 IPEndPoint. - public IPEndPoint IpEndPoint() - { - var address = this.IpAddress(); - var port = this.Random.Int(IPEndPoint.MinPort + 1, IPEndPoint.MaxPort); - return new IPEndPoint(address, port); - } + return Regex.Replace(domain, @"([\\ ~#&*{}/:<>?|\""'])", string.Empty); + } - /// - /// Generates a random IPv6 address string. - /// - /// A random IPv6 address. - public string Ipv6() - { - var bytes = this.Random.Bytes(16); - return - $"{bytes[0]:x}{bytes[1]:x}:{bytes[2]:x}{bytes[3]:x}:{bytes[4]:x}{bytes[5]:x}:{bytes[6]:x}{bytes[7]:x}:{bytes[8]:x}{bytes[9]:x}:{bytes[10]:x}{bytes[11]:x}:{bytes[12]:x}{bytes[13]:x}:{bytes[14]:x}{bytes[15]:x}"; - } + /// + /// Generates a domain name suffix like .com, .net, .org + /// + /// A random domain suffix. + public string DomainSuffix() + { + return GetRandomArrayItem("domain_suffix"); + } - /// - /// Generate a random IPv6 IPAddress type. - /// - /// - public IPAddress Ipv6Address() - { - var address = new IPAddress(this.Random.Bytes(16)); - return address; - } + /// + /// Gets a random IPv4 address string. + /// + /// A random IPv4 address. + public string Ip() + { + return $"{Random.Number(1, 255)}.{Random.Number(255)}.{Random.Number(255)}.{Random.Number(255)}"; + } - /// - /// Gets a random IPv6 IPEndPoint. - /// - /// A random IPv6 IPEndPoint. - public IPEndPoint Ipv6EndPoint() - { - var address = this.Ipv6Address(); - var port = this.Random.Int(IPEndPoint.MinPort + 1, IPEndPoint.MaxPort); - return new IPEndPoint(address, port); - } + /// + /// Generates a random port number. + /// + /// A random port number + public int Port() + { + return this.Random.Number(min: IPEndPoint.MinPort + 1, max: IPEndPoint.MaxPort); + } - private UserAgentGenerator userAgentGenerator; + /// + /// Gets a random IPv4 IPAddress type. + /// + public IPAddress IpAddress() + { + var bytes = this.Random.Bytes(4); + if( bytes[0] == 0 ) bytes[0]++; + var address = new IPAddress(bytes); + return address; + } - /// - /// Generates a random user agent. - /// - /// A random user agent. - public string UserAgent() - { - return userAgentGenerator.Generate(); - } + /// + /// Gets a random IPv4 IPEndPoint. + /// + /// A random IPv4 IPEndPoint. + public IPEndPoint IpEndPoint() + { + var address = this.IpAddress(); + var port = this.Random.Int(IPEndPoint.MinPort + 1, IPEndPoint.MaxPort); + return new IPEndPoint(address, port); + } - /// - /// Gets a random mac address. - /// - /// The string the mac address should be separated with. - /// A random mac address. - public string Mac(string separator = ":") - { - var arr = Enumerable.Range(0, 6) - .Select(_ => this.Random.Number(0, 255).ToString("x2")); + /// + /// Generates a random IPv6 address string. + /// + /// A random IPv6 address. + public string Ipv6() + { + var bytes = this.Random.Bytes(16); + return + $"{bytes[0]:x}{bytes[1]:x}:{bytes[2]:x}{bytes[3]:x}:{bytes[4]:x}{bytes[5]:x}:{bytes[6]:x}{bytes[7]:x}:{bytes[8]:x}{bytes[9]:x}:{bytes[10]:x}{bytes[11]:x}:{bytes[12]:x}{bytes[13]:x}:{bytes[14]:x}{bytes[15]:x}"; + } - return string.Join(separator, arr); - } + /// + /// Generate a random IPv6 IPAddress type. + /// + /// + public IPAddress Ipv6Address() + { + var address = new IPAddress(this.Random.Bytes(16)); + return address; + } - /// - /// Generates a random password. - /// - /// Length of the password. - /// A memorable password (ie: all lower case). - /// Regex pattern that the password should follow. - /// Password prefix. - /// A random password. - public string Password(int length = 10, bool memorable = false, string regexPattern = "\\w", string prefix = "") - { - string consonant, vowel; - vowel = "[aeiouAEIOU]$"; - consonant = "[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]"; + /// + /// Gets a random IPv6 IPEndPoint. + /// + /// A random IPv6 IPEndPoint. + public IPEndPoint Ipv6EndPoint() + { + var address = this.Ipv6Address(); + var port = this.Random.Int(IPEndPoint.MinPort + 1, IPEndPoint.MaxPort); + return new IPEndPoint(address, port); + } - if( prefix.Length >= length ) - { - return prefix; - } + private UserAgentGenerator userAgentGenerator; - if( memorable ) - { - if( Regex.IsMatch(prefix, consonant) ) - { - regexPattern = vowel; - } - else - { - regexPattern = consonant; - } - } + /// + /// Generates a random user agent. + /// + /// A random user agent. + public string UserAgent() + { + return userAgentGenerator.Generate(); + } - var asciiNumber = this.Random.Number(32, 126); //ascii - var character = Convert.ToChar(asciiNumber).ToString(); - if( memorable ) - { - character = character.ToLowerInvariant(); - } + /// + /// Gets a random mac address. + /// + /// The string the mac address should be separated with. + /// A random mac address. + public string Mac(string separator = ":") + { + var arr = Enumerable.Range(0, 6) + .Select(_ => this.Random.Number(0, 255).ToString("x2")); - if( !Regex.IsMatch(character, regexPattern) ) - { - return Password(length, memorable, regexPattern, prefix); - } + return string.Join(separator, arr); + } - return Password(length, memorable, regexPattern, prefix + character); - } + /// + /// Generates a random password. + /// + /// Length of the password. + /// A memorable password (ie: all lower case). + /// Regex pattern that the password should follow. + /// Password prefix. + /// A random password. + public string Password(int length = 10, bool memorable = false, string regexPattern = "\\w", string prefix = "") + { + string consonant, vowel; + vowel = "[aeiouAEIOU]$"; + consonant = "[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]"; - /// - /// Gets a random aesthetically pleasing color near the base RGB. See [here](http://stackoverflow.com/questions/43044/algorithm-to-randomly-generate-an-aesthetically-pleasing-color-palette). - /// - /// Red base color - /// Green base color - /// Blue base color - /// Output a gray scale color - /// The color format - /// A random color. - public string Color(byte baseRed = 0, byte baseGreen = 0, byte baseBlue = 0, bool grayscale = false, ColorFormat format = ColorFormat.Hex) + if( prefix.Length >= length ) { - var red = Math.Floor((Random.Number(256) + (double)baseRed) / 2); - var green = Math.Floor((Random.Number(256) + (double)baseGreen) / 2); - var blue = Math.Floor((Random.Number(256) + (double)baseBlue) / 2); - - if( grayscale ) - { - green = red; - blue = red; - } - - var r = (byte)red; - var g = (byte)green; - var b = (byte)blue; - - if( format == ColorFormat.Hex ) - { - return $"#{r:x02}{g:x02}{b:x02}"; - } + return prefix; + } - if( format == ColorFormat.Delimited ) + if( memorable ) + { + if( Regex.IsMatch(prefix, consonant) ) { - return DelimitedRgb(); + regexPattern = vowel; } - - return $"rgb({DelimitedRgb()})"; - - string DelimitedRgb() + else { - return $"{r},{g},{b}"; + regexPattern = consonant; } } - /// - /// Returns a random protocol. HTTP or HTTPS. - /// - /// A random protocol. - public string Protocol() + var asciiNumber = this.Random.Number(32, 126); //ascii + var character = Convert.ToChar(asciiNumber).ToString(); + if( memorable ) { - var protocols = new[] {"http", "https"}; - - return Random.ArrayElement(protocols); + character = character.ToLowerInvariant(); } - /// - /// Generates a random URL. - /// - /// A random URL. - public string Url() + if( !Regex.IsMatch(character, regexPattern) ) { - return Url(null, null); + return Password(length, memorable, regexPattern, prefix); } - /// - /// Get an absolute URL with random path. - /// - /// Protocol part of the URL, random if null - /// Domain part of the URL, random if null - /// The file extension to use in the path, directory if null - /// An URL with a random path. - public string UrlWithPath(string protocol = null, string domain = null, string fileExt = null) + return Password(length, memorable, regexPattern, prefix + character); + } + + /// + /// Gets a random aesthetically pleasing color near the base RGB. See [here](http://stackoverflow.com/questions/43044/algorithm-to-randomly-generate-an-aesthetically-pleasing-color-palette). + /// + /// Red base color + /// Green base color + /// Blue base color + /// Output a gray scale color + /// The color format + /// A random color. + public string Color(byte baseRed = 0, byte baseGreen = 0, byte baseBlue = 0, bool grayscale = false, ColorFormat format = ColorFormat.Hex) + { + var red = Math.Floor((Random.Number(256) + (double)baseRed) / 2); + var green = Math.Floor((Random.Number(256) + (double)baseGreen) / 2); + var blue = Math.Floor((Random.Number(256) + (double)baseBlue) / 2); + + if( grayscale ) { - var path = UrlRootedPath(fileExt); - return $"{Url(protocol, domain)}{path}"; + green = red; + blue = red; } - /// - /// Get a rooted URL path like: /foo/bar. Optionally with file extension. - /// - /// Optional: The file extension to use. If is null, then a rooted URL directory is returned. - /// Returns a rooted URL path like: /foo/bar; optionally with a file extension. - public string UrlRootedPath(string fileExt = null) - { - var words = Random.WordsArray(1, 3) - .Select(Utils.Slugify) - .Select(s => s.ToLower()) - .ToArray(); + var r = (byte)red; + var g = (byte)green; + var b = (byte)blue; - var path = $"/{Utils.Slashify(words)}"; + if( format == ColorFormat.Hex ) + { + return $"#{r:x02}{g:x02}{b:x02}"; + } - return Path.ChangeExtension(path, fileExt); + if( format == ColorFormat.Delimited ) + { + return DelimitedRgb(); } - private string Url(string protocol, string domain) + return $"rgb({DelimitedRgb()})"; + + string DelimitedRgb() { - return $"{protocol ?? Protocol()}://{domain ?? DomainName()}"; + return $"{r},{g},{b}"; } } + + /// + /// Returns a random protocol. HTTP or HTTPS. + /// + /// A random protocol. + public string Protocol() + { + var protocols = new[] {"http", "https"}; + + return Random.ArrayElement(protocols); + } + + /// + /// Generates a random URL. + /// + /// A random URL. + public string Url() + { + return Url(null, null); + } + + /// + /// Get an absolute URL with random path. + /// + /// Protocol part of the URL, random if null + /// Domain part of the URL, random if null + /// The file extension to use in the path, directory if null + /// An URL with a random path. + public string UrlWithPath(string protocol = null, string domain = null, string fileExt = null) + { + var path = UrlRootedPath(fileExt); + return $"{Url(protocol, domain)}{path}"; + } + + /// + /// Get a rooted URL path like: /foo/bar. Optionally with file extension. + /// + /// Optional: The file extension to use. If is null, then a rooted URL directory is returned. + /// Returns a rooted URL path like: /foo/bar; optionally with a file extension. + public string UrlRootedPath(string fileExt = null) + { + var words = Random.WordsArray(1, 3) + .Select(Utils.Slugify) + .Select(s => s.ToLower()) + .ToArray(); + + var path = $"/{Utils.Slashify(words)}"; + + return Path.ChangeExtension(path, fileExt); + } + + private string Url(string protocol, string domain) + { + return $"{protocol ?? Protocol()}://{domain ?? DomainName()}"; + } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Lorem.cs b/Source/Bogus/DataSets/Lorem.cs index 8a2255f1..d638df4b 100644 --- a/Source/Bogus/DataSets/Lorem.cs +++ b/Source/Bogus/DataSets/Lorem.cs @@ -1,151 +1,150 @@ using System; using System.Linq; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates plain old boring text. +/// +public class Lorem : DataSet { + /// - /// Generates plain old boring text. + /// Initializes a new instance of the class. /// - public class Lorem : DataSet + /// The locale used to generate random values. + public Lorem(string locale = "en") : base(locale) { + } - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate random values. - public Lorem(string locale = "en") : base(locale) - { - } - - /// - /// Get a random lorem word. - /// - public string Word() - { - return this.GetRandomArrayItem("words"); - } + /// + /// Get a random lorem word. + /// + public string Word() + { + return this.GetRandomArrayItem("words"); + } - /// - /// Get an array of random lorem words. - /// - /// The number of random lorem words to return. - public string[] Words(int num = 3) - { - return Enumerable.Range(1, num).Select(_ => Word()).ToArray(); - } + /// + /// Get an array of random lorem words. + /// + /// The number of random lorem words to return. + public string[] Words(int num = 3) + { + return Enumerable.Range(1, num).Select(_ => Word()).ToArray(); + } - /// - /// Get a character letter. - /// - /// The number of characters to return. - public string Letter(int num = 1) - { - if( num <= 0 ) - return string.Empty; + /// + /// Get a character letter. + /// + /// The number of characters to return. + public string Letter(int num = 1) + { + if( num <= 0 ) + return string.Empty; - var words = Words(1)[0]; - var characters = Random.ArrayElement(words.ToArray()); - return characters + Letter(num - 1); - } + var words = Words(1)[0]; + var characters = Random.ArrayElement(words.ToArray()); + return characters + Letter(num - 1); + } - /// - /// Get a random sentence of specific number of words. - /// - /// Get a sentence with wordCount words. Defaults between 3 and 10. - /// Add anywhere between 0 to 'range' additional words to wordCount. Default is 0. - public string Sentence(int? wordCount = null, int? range = 0) + /// + /// Get a random sentence of specific number of words. + /// + /// Get a sentence with wordCount words. Defaults between 3 and 10. + /// Add anywhere between 0 to 'range' additional words to wordCount. Default is 0. + public string Sentence(int? wordCount = null, int? range = 0) + { + var wc = wordCount ?? this.Random.Number(3, 10); + if( range > 0 ) { - var wc = wordCount ?? this.Random.Number(3, 10); - if( range > 0 ) - { - wc += this.Random.Number(range.Value); - } - - var sentence = string.Join(" ", Words(wc)); - return sentence.Substring(0, 1).ToUpper() + sentence.Substring(1) + "."; + wc += this.Random.Number(range.Value); } - /// - /// Get some sentences. - /// - /// The number of sentences. - /// The string to separate sentences. - public string Sentences(int? sentenceCount = null, string separator = "\n") - { - var sc = sentenceCount ?? this.Random.Number(2, 6); - var sentences = Enumerable.Range(1, sc) - .Select(_ => Sentence()); + var sentence = string.Join(" ", Words(wc)); + return sentence.Substring(0, 1).ToUpper() + sentence.Substring(1) + "."; + } - return string.Join(separator, sentences); - } + /// + /// Get some sentences. + /// + /// The number of sentences. + /// The string to separate sentences. + public string Sentences(int? sentenceCount = null, string separator = "\n") + { + var sc = sentenceCount ?? this.Random.Number(2, 6); + var sentences = Enumerable.Range(1, sc) + .Select(_ => Sentence()); - /// - /// Get a paragraph. - /// - /// The minimum number of sentences in the paragraph. - /// The final number of sentences returned in the paragraph is bound between [min, min + 3], inclusive. - /// If you want an exact number of sentences, use the method. - public string Paragraph(int min = 3) - { - return Sentences(min + Random.Number(3), " "); - } + return string.Join(separator, sentences); + } - /// - /// Get a specified number of paragraphs. - /// - /// Number of paragraphs. - /// The string to separate paragraphs. - public string Paragraphs(int count = 3, string separator = "\n\n") - { - var paragraphs = Enumerable.Range(1, count) - .Select(_ => Paragraph()); + /// + /// Get a paragraph. + /// + /// The minimum number of sentences in the paragraph. + /// The final number of sentences returned in the paragraph is bound between [min, min + 3], inclusive. + /// If you want an exact number of sentences, use the method. + public string Paragraph(int min = 3) + { + return Sentences(min + Random.Number(3), " "); + } - return string.Join(separator, paragraphs); - } + /// + /// Get a specified number of paragraphs. + /// + /// Number of paragraphs. + /// The string to separate paragraphs. + public string Paragraphs(int count = 3, string separator = "\n\n") + { + var paragraphs = Enumerable.Range(1, count) + .Select(_ => Paragraph()); - /// - /// Get a random number of paragraphs between and . - /// - /// Minimum number of paragraphs. - /// Maximum number of paragraphs. - /// The string to separate the paragraphs. - public string Paragraphs(int min, int max, string separator = "\n\n") - { - var count = this.Random.Number(min, max); - return Paragraphs(count, separator); - } + return string.Join(separator, paragraphs); + } - /// - /// Get random text on a random lorem methods. - /// - public string Text() - { - var methods = new Func[] {() => Word(), () => Sentence(), () => Sentences(), () => Paragraph()}; + /// + /// Get a random number of paragraphs between and . + /// + /// Minimum number of paragraphs. + /// Maximum number of paragraphs. + /// The string to separate the paragraphs. + public string Paragraphs(int min, int max, string separator = "\n\n") + { + var count = this.Random.Number(min, max); + return Paragraphs(count, separator); + } - var randomLoremMethod = this.Random.ArrayElement(methods); - return randomLoremMethod(); - } + /// + /// Get random text on a random lorem methods. + /// + public string Text() + { + var methods = new Func[] {() => Word(), () => Sentence(), () => Sentences(), () => Paragraph()}; - /// - /// Get lines of lorem. - /// - /// The amount of lines to generate. Defaults between 1 and 5. - /// The string to separate the lines. - public string Lines(int? lineCount = null, string separator = "\n") - { - var lc = lineCount ?? this.Random.Number(1, 5); + var randomLoremMethod = this.Random.ArrayElement(methods); + return randomLoremMethod(); + } - return Sentences(lc, separator); - } + /// + /// Get lines of lorem. + /// + /// The amount of lines to generate. Defaults between 1 and 5. + /// The string to separate the lines. + public string Lines(int? lineCount = null, string separator = "\n") + { + var lc = lineCount ?? this.Random.Number(1, 5); - /// - /// Slugify lorem words. - /// - /// The amount of words to slugify. - public string Slug(int wordcount = 3) - { - var words = Words(wordcount); - return Utils.Slugify(string.Join(" ", words)); - } + return Sentences(lc, separator); + } + + /// + /// Slugify lorem words. + /// + /// The amount of words to slugify. + public string Slug(int wordcount = 3) + { + var words = Words(wordcount); + return Utils.Slugify(string.Join(" ", words)); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Music.cs b/Source/Bogus/DataSets/Music.cs index 5ce98f3d..a112d4ab 100644 --- a/Source/Bogus/DataSets/Music.cs +++ b/Source/Bogus/DataSets/Music.cs @@ -1,13 +1,12 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +public class Music : DataSet { - public class Music : DataSet + /// + /// Get a music genre + /// + public string Genre() { - /// - /// Get a music genre - /// - public string Genre() - { - return GetRandomArrayItem("genre"); - } + return GetRandomArrayItem("genre"); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Name.cs b/Source/Bogus/DataSets/Name.cs index 7d27931a..2e86029b 100644 --- a/Source/Bogus/DataSets/Name.cs +++ b/Source/Bogus/DataSets/Name.cs @@ -1,180 +1,179 @@ -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Methods for generating names +/// +public class Name : DataSet { + public enum Gender + { + Male, + Female + } + + public readonly bool SupportsGenderFirstNames = false; + public readonly bool SupportsGenderLastNames = false; + public readonly bool SupportsGenderPrefixes = false; + public readonly bool HasFirstNameList = false; + /// - /// Methods for generating names + /// Default constructor /// - public class Name : DataSet + public Name(string locale = "en") : base(locale) { - public enum Gender - { - Male, - Female - } + SupportsGenderFirstNames = HasKey("male_first_name", false) && HasKey("female_first_name", false); + SupportsGenderLastNames = HasKey("male_last_name", false) && HasKey("female_last_name", false); + SupportsGenderPrefixes = HasKey("male_prefix", false) && HasKey("female_prefix", false); + HasFirstNameList = HasKey("first_name", false); + } - public readonly bool SupportsGenderFirstNames = false; - public readonly bool SupportsGenderLastNames = false; - public readonly bool SupportsGenderPrefixes = false; - public readonly bool HasFirstNameList = false; + /// + /// Switches locale + /// + public Name this[string switchLocale] => new Name(switchLocale); - /// - /// Default constructor - /// - public Name(string locale = "en") : base(locale) - { - SupportsGenderFirstNames = HasKey("male_first_name", false) && HasKey("female_first_name", false); - SupportsGenderLastNames = HasKey("male_last_name", false) && HasKey("female_last_name", false); - SupportsGenderPrefixes = HasKey("male_prefix", false) && HasKey("female_prefix", false); - HasFirstNameList = HasKey("first_name", false); - } + /// + /// Get a first name. Getting a gender specific name is only supported on locales that support it. + /// + /// For locale's that support Gender naming. + public string FirstName(Gender? gender = null) + { + if ((gender is null && HasFirstNameList) || !SupportsGenderFirstNames) + return GetRandomArrayItem("first_name"); - /// - /// Switches locale - /// - public Name this[string switchLocale] => new Name(switchLocale); + if( gender is null ) + gender = this.Random.Enum(); - /// - /// Get a first name. Getting a gender specific name is only supported on locales that support it. - /// - /// For locale's that support Gender naming. - public string FirstName(Gender? gender = null) + if( gender == Gender.Male ) { - if ((gender is null && HasFirstNameList) || !SupportsGenderFirstNames) - return GetRandomArrayItem("first_name"); + return GetRandomArrayItem("male_first_name"); + } + return GetRandomArrayItem("female_first_name"); + } - if( gender is null ) - gender = this.Random.Enum(); + /// + /// Get a last name. Getting a gender specific name is only supported on locales that support it. + /// + /// For locale's that support Gender naming. + public string LastName(Gender? gender = null) + { + if( SupportsGenderLastNames ) + { + gender ??= this.Random.Enum(); if( gender == Gender.Male ) { - return GetRandomArrayItem("male_first_name"); + return GetRandomArrayItem("male_last_name"); } - return GetRandomArrayItem("female_first_name"); + return GetRandomArrayItem("female_last_name"); } - /// - /// Get a last name. Getting a gender specific name is only supported on locales that support it. - /// - /// For locale's that support Gender naming. - public string LastName(Gender? gender = null) - { - if( SupportsGenderLastNames ) - { - gender ??= this.Random.Enum(); - - if( gender == Gender.Male ) - { - return GetRandomArrayItem("male_last_name"); - } - return GetRandomArrayItem("female_last_name"); - } - - return GetRandomArrayItem("last_name"); - } + return GetRandomArrayItem("last_name"); + } - /// - /// Get a full name, concatenation of calling FirstName and LastName. - /// - /// Gender of the name if supported by the locale. - public string FullName(Gender? gender = null) - { - // PR#148 - 'ru' locale requires a gender to be - // specified for both first and last name. Gender is not - // picked when 'en' locale is specified because - // SupportsGenderLastNames = false when 'en' is used. - // SupportsGenderLastNames is false because 'en' doesn't have - // en: male_last_name and en: female_last_name JSON fields. - if ( SupportsGenderFirstNames && SupportsGenderLastNames ) - gender ??= this.Random.Enum(); - - return $"{FirstName(gender)} {LastName(gender)}"; - } + /// + /// Get a full name, concatenation of calling FirstName and LastName. + /// + /// Gender of the name if supported by the locale. + public string FullName(Gender? gender = null) + { + // PR#148 - 'ru' locale requires a gender to be + // specified for both first and last name. Gender is not + // picked when 'en' locale is specified because + // SupportsGenderLastNames = false when 'en' is used. + // SupportsGenderLastNames is false because 'en' doesn't have + // en: male_last_name and en: female_last_name JSON fields. + if ( SupportsGenderFirstNames && SupportsGenderLastNames ) + gender ??= this.Random.Enum(); + + return $"{FirstName(gender)} {LastName(gender)}"; + } - /// - /// Gets a random prefix for a name. - /// - public string Prefix(Gender? gender = null) + /// + /// Gets a random prefix for a name. + /// + public string Prefix(Gender? gender = null) + { + gender ??= this.Random.Enum(); + if( SupportsGenderPrefixes ) { - gender ??= this.Random.Enum(); - if( SupportsGenderPrefixes ) + if( gender == Gender.Male ) { - if( gender == Gender.Male ) - { - return GetRandomArrayItem("male_prefix"); - } - return GetRandomArrayItem("female_prefix"); + return GetRandomArrayItem("male_prefix"); } - return GetRandomArrayItem("prefix"); + return GetRandomArrayItem("female_prefix"); } + return GetRandomArrayItem("prefix"); + } - /// - /// Gets a random suffix for a name. - /// - public string Suffix() - { - return GetRandomArrayItem("suffix"); - } + /// + /// Gets a random suffix for a name. + /// + public string Suffix() + { + return GetRandomArrayItem("suffix"); + } - /// - /// Gets a full name. - /// - /// Use this first name. - /// use this last name. - /// Add a prefix? - /// Add a suffix? - public string FindName(string firstName = "", string lastName = "", bool? withPrefix = null, bool? withSuffix = null, Gender? gender = null) - { - gender ??= this.Random.Enum(); - if( string.IsNullOrWhiteSpace(firstName) ) - firstName = FirstName(gender); - if( string.IsNullOrWhiteSpace(lastName) ) - lastName = LastName(gender); + /// + /// Gets a full name. + /// + /// Use this first name. + /// use this last name. + /// Add a prefix? + /// Add a suffix? + public string FindName(string firstName = "", string lastName = "", bool? withPrefix = null, bool? withSuffix = null, Gender? gender = null) + { + gender ??= this.Random.Enum(); + if( string.IsNullOrWhiteSpace(firstName) ) + firstName = FirstName(gender); + if( string.IsNullOrWhiteSpace(lastName) ) + lastName = LastName(gender); - if( !withPrefix.HasValue && !withSuffix.HasValue ) - { - withPrefix = Random.Bool(); - withSuffix = !withPrefix; - } + if( !withPrefix.HasValue && !withSuffix.HasValue ) + { + withPrefix = Random.Bool(); + withSuffix = !withPrefix; + } - var prefix = withPrefix.GetValueOrDefault() ? Prefix(gender) : ""; - var suffix = withSuffix.GetValueOrDefault() ? Suffix() : ""; + var prefix = withPrefix.GetValueOrDefault() ? Prefix(gender) : ""; + var suffix = withSuffix.GetValueOrDefault() ? Suffix() : ""; - return $"{prefix} {firstName} {lastName} {suffix}".Trim(); - } + return $"{prefix} {firstName} {lastName} {suffix}".Trim(); + } - /// - /// Gets a random job title. - /// - public string JobTitle() - { - var descriptor = JobDescriptor(); - var level = JobArea(); - var job = JobType(); + /// + /// Gets a random job title. + /// + public string JobTitle() + { + var descriptor = JobDescriptor(); + var level = JobArea(); + var job = JobType(); - return $"{descriptor} {level} {job}"; - } + return $"{descriptor} {level} {job}"; + } - /// - /// Get a job description. - /// - public string JobDescriptor() - { - return GetRandomArrayItem("title.descriptor"); - } + /// + /// Get a job description. + /// + public string JobDescriptor() + { + return GetRandomArrayItem("title.descriptor"); + } - /// - /// Get a job area expertise. - /// - public string JobArea() - { - return GetRandomArrayItem("title.level"); - } + /// + /// Get a job area expertise. + /// + public string JobArea() + { + return GetRandomArrayItem("title.level"); + } - /// - /// Get a type of job. - /// - public string JobType() - { - return GetRandomArrayItem("title.job"); - } + /// + /// Get a type of job. + /// + public string JobType() + { + return GetRandomArrayItem("title.job"); } } diff --git a/Source/Bogus/DataSets/PhoneNumbers.cs b/Source/Bogus/DataSets/PhoneNumbers.cs index bf39b60d..cf4f92b9 100644 --- a/Source/Bogus/DataSets/PhoneNumbers.cs +++ b/Source/Bogus/DataSets/PhoneNumbers.cs @@ -1,68 +1,67 @@ using System; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates phone numbers +/// +[DataCategory("phone_number")] +public class PhoneNumbers : DataSet { /// - /// Generates phone numbers + /// Initializes a new instance of the class. /// - [DataCategory("phone_number")] - public class PhoneNumbers : DataSet + /// The locale used to generate values. + public PhoneNumbers(string locale = "en") : base(locale) { - /// - /// Initializes a new instance of the class. - /// - /// The locale used to generate values. - public PhoneNumbers(string locale = "en") : base(locale) - { - } + } - /// - /// Get a phone number. - /// - /// - /// Format of phone number in any format. - /// Replaces # characters with numbers. IE: '###-###-####' or '(###) ###-####'. - /// - /// A random phone number. - public string PhoneNumber(string format = null) + /// + /// Get a phone number. + /// + /// + /// Format of phone number in any format. + /// Replaces # characters with numbers. IE: '###-###-####' or '(###) ###-####'. + /// + /// A random phone number. + public string PhoneNumber(string format = null) + { + if (string.IsNullOrWhiteSpace(format)) { - if (string.IsNullOrWhiteSpace(format)) - { - format = PhoneFormat(); - } - - return Random.Replace(ReplaceExclamChar(format)); + format = PhoneFormat(); } - /// - /// Gets a phone number based on the locale's phone_number.formats[] array index. - /// - /// The array index as defined in the locale's phone_number.formats[] array. - /// A random phone number. - public string PhoneNumberFormat(int phoneFormatsArrayIndex = 0) - { - var formatArray = GetArray("formats"); - var format = (string)formatArray[phoneFormatsArrayIndex]; + return Random.Replace(ReplaceExclamChar(format)); + } - return PhoneNumber(format); - } + /// + /// Gets a phone number based on the locale's phone_number.formats[] array index. + /// + /// The array index as defined in the locale's phone_number.formats[] array. + /// A random phone number. + public string PhoneNumberFormat(int phoneFormatsArrayIndex = 0) + { + var formatArray = GetArray("formats"); + var format = (string)formatArray[phoneFormatsArrayIndex]; - /// - /// Gets the format of a phone number. - /// - /// A random phone number format. - protected virtual string PhoneFormat() - { - return GetRandomArrayItem("formats"); - } + return PhoneNumber(format); + } - /// - /// Replaces special ! characters in phone number formats. - /// - /// The newly formed string. - protected virtual string ReplaceExclamChar(string s) - { - return this.Random.ReplaceSymbols(s, '!', () => Convert.ToChar('0' + this.Random.Number(2, 9))); - } + /// + /// Gets the format of a phone number. + /// + /// A random phone number format. + protected virtual string PhoneFormat() + { + return GetRandomArrayItem("formats"); + } + + /// + /// Replaces special ! characters in phone number formats. + /// + /// The newly formed string. + protected virtual string ReplaceExclamChar(string s) + { + return this.Random.ReplaceSymbols(s, '!', () => Convert.ToChar('0' + this.Random.Number(2, 9))); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Rant.cs b/Source/Bogus/DataSets/Rant.cs index 58866959..d054493d 100644 --- a/Source/Bogus/DataSets/Rant.cs +++ b/Source/Bogus/DataSets/Rant.cs @@ -1,34 +1,33 @@ using System.Linq; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates random user content. +/// +public class Rant : DataSet { /// - /// Generates random user content. + /// Generates a random user review. /// - public class Rant : DataSet + /// The name of the product. + /// A user review as a string. + public string Review(string product = "product") { - /// - /// Generates a random user review. - /// - /// The name of the product. - /// A user review as a string. - public string Review(string product = "product") - { - return this.GetRandomArrayItem("review") - .Replace("$product", product); - } + return this.GetRandomArrayItem("review") + .Replace("$product", product); + } - /// - /// Generate an array of random reviews. - /// - /// The name of the product. - /// The number of reviews to be generated. - /// A string array of user reviews. - public string[] Reviews(string product = "product", int lines = 2) - { - return Enumerable.Range(1, lines) - .Select(_ => this.Review(product)) - .ToArray(); - } + /// + /// Generate an array of random reviews. + /// + /// The name of the product. + /// The number of reviews to be generated. + /// A string array of user reviews. + public string[] Reviews(string product = "product", int lines = 2) + { + return Enumerable.Range(1, lines) + .Select(_ => this.Review(product)) + .ToArray(); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/System.cs b/Source/Bogus/DataSets/System.cs index cf4835e2..46c8781c 100644 --- a/Source/Bogus/DataSets/System.cs +++ b/Source/Bogus/DataSets/System.cs @@ -4,400 +4,399 @@ using System.Linq; using Bogus.Bson; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Generates fake data for many computer systems properties +/// +public class System : DataSet { /// - /// Generates fake data for many computer systems properties + /// Initializes a new instance of the class. /// - public class System : DataSet + /// The locale that will be used to generate values. + public System(string locale = "en") : base(locale) { - /// - /// Initializes a new instance of the class. - /// - /// The locale that will be used to generate values. - public System(string locale = "en") : base(locale) - { - mimes = this.GetArray("mimeTypes"); - - lookup = mimes.OfType() - .ToDictionary(o => o["mime"].StringValue); + mimes = this.GetArray("mimeTypes"); + + lookup = mimes.OfType() + .ToDictionary(o => o["mime"].StringValue); + + mimeKeys = mimes + .OfType() + .Select(o => o["mime"].StringValue) + .Distinct() + .ToArray(); + + exts = mimes + .OfType() + .SelectMany(bObject => + { + if( bObject.ContainsKey("extensions") ) + { + var extensions = bObject["extensions"] as BArray; + return extensions.OfType().Select(s => s.StringValue); + } + return Enumerable.Empty(); + }) + .ToArray(); - mimeKeys = mimes - .OfType() - .Select(o => o["mime"].StringValue) - .Distinct() - .ToArray(); + types = mimeKeys.Select(k => k.Substring(0, k.IndexOf('/'))) + .Distinct() + .ToArray(); + } - exts = mimes - .OfType() - .SelectMany(bObject => - { - if( bObject.ContainsKey("extensions") ) - { - var extensions = bObject["extensions"] as BArray; - return extensions.OfType().Select(s => s.StringValue); - } - return Enumerable.Empty(); - }) - .ToArray(); - - types = mimeKeys.Select(k => k.Substring(0, k.IndexOf('/'))) - .Distinct() - .ToArray(); - } + protected Lorem Lorem = null; + private readonly Dictionary lookup; + private readonly BArray mimes; + private readonly string[] exts; + private readonly string[] types; + private readonly string[] mimeKeys; - protected Lorem Lorem = null; - private readonly Dictionary lookup; - private readonly BArray mimes; - private readonly string[] exts; - private readonly string[] types; - private readonly string[] mimeKeys; - - private static readonly string[] commonFileTypes = - { "video", "audio", "image", "text", "application" }; - - private static readonly string[] commonMimeTypes = - { - "application/pdf", - "audio/mpeg", - "audio/wav", - "image/png", - "image/jpeg", - "image/gif", - "video/mp4", - "video/mpeg", - "text/html" - }; - - /// - /// Get a random file name. - /// - /// - /// The extension the file name will have. - /// If null is provided, a random extension will be picked. - /// - /// - /// A random file name with the given - /// or a random extension - /// - public string FileName(string ext = null) - { - var filename = $"{this.Random.Words()}.{ext ?? FileExt()}"; - filename = filename.Replace(" ", "_"); - filename = filename.Replace(",", "_"); - filename = filename.Replace("-", "_"); - filename = filename.Replace(@"\", "_"); - filename = filename.Replace("/", "_"); - filename = filename.ToLower().Trim(); - return filename; - } + private static readonly string[] commonFileTypes = + { "video", "audio", "image", "text", "application" }; - /// - /// Get a random directory path (Unix). - /// - /// - /// A random Unix directory path. - /// - public string DirectoryPath() + private static readonly string[] commonMimeTypes = { - return GetRandomArrayItem("directoryPaths"); - } + "application/pdf", + "audio/mpeg", + "audio/wav", + "image/png", + "image/jpeg", + "image/gif", + "video/mp4", + "video/mpeg", + "text/html" + }; - /// - /// Get a random file path (Unix). - /// - /// - /// A random Unix file path. - /// - public string FilePath() - { - return $"{DirectoryPath()}/{FileName()}"; - } + /// + /// Get a random file name. + /// + /// + /// The extension the file name will have. + /// If null is provided, a random extension will be picked. + /// + /// + /// A random file name with the given + /// or a random extension + /// + public string FileName(string ext = null) + { + var filename = $"{this.Random.Words()}.{ext ?? FileExt()}"; + filename = filename.Replace(" ", "_"); + filename = filename.Replace(",", "_"); + filename = filename.Replace("-", "_"); + filename = filename.Replace(@"\", "_"); + filename = filename.Replace("/", "_"); + filename = filename.ToLower().Trim(); + return filename; + } - /// - /// Generates a random file name with a common file extension. - /// Extension can be overwritten with . - /// - /// - /// The extensions to be used for a file name. - /// - /// - /// A random file name with a common extension or . - /// - public string CommonFileName(string ext = null) - { - var filename = $"{this.Random.Words()}.{ext ?? CommonFileExt()}"; - filename = filename.Replace(" ", "_"); - filename = filename.Replace(",", "_"); - filename = filename.Replace("-", "_"); - filename = filename.Replace(@"\", "_"); - filename = filename.Replace("/", "_"); - filename = filename.ToLower().Trim(); - return filename; - } + /// + /// Get a random directory path (Unix). + /// + /// + /// A random Unix directory path. + /// + public string DirectoryPath() + { + return GetRandomArrayItem("directoryPaths"); + } + /// + /// Get a random file path (Unix). + /// + /// + /// A random Unix file path. + /// + public string FilePath() + { + return $"{DirectoryPath()}/{FileName()}"; + } - /// - /// Get a random mime type. - /// - /// - /// A random mime type. - /// - public string MimeType() - { - return this.Random.ArrayElement(this.mimeKeys); - } + /// + /// Generates a random file name with a common file extension. + /// Extension can be overwritten with . + /// + /// + /// The extensions to be used for a file name. + /// + /// + /// A random file name with a common extension or . + /// + public string CommonFileName(string ext = null) + { + var filename = $"{this.Random.Words()}.{ext ?? CommonFileExt()}"; + filename = filename.Replace(" ", "_"); + filename = filename.Replace(",", "_"); + filename = filename.Replace("-", "_"); + filename = filename.Replace(@"\", "_"); + filename = filename.Replace("/", "_"); + filename = filename.ToLower().Trim(); + return filename; + } - /// - /// Returns a commonly used file type. - /// - /// - /// A commonly used file type. - /// - public string CommonFileType() - { - return this.Random.ArrayElement(commonFileTypes); - } + /// + /// Get a random mime type. + /// + /// + /// A random mime type. + /// + public string MimeType() + { + return this.Random.ArrayElement(this.mimeKeys); + } - /// - /// Returns a commonly used file extension. - /// - /// - /// A commonly used file extension. - /// - public string CommonFileExt() - { - return FileExt(this.Random.ArrayElement(commonMimeTypes)); - } + /// + /// Returns a commonly used file type. + /// + /// + /// A commonly used file type. + /// + public string CommonFileType() + { + return this.Random.ArrayElement(commonFileTypes); + } - /// - /// Returns any file type available as mime-type. - /// - /// - /// Any file type available as mime-type. - /// - public string FileType() - { - return this.Random.ArrayElement(this.types); - } + /// + /// Returns a commonly used file extension. + /// + /// + /// A commonly used file extension. + /// + public string CommonFileExt() + { + return FileExt(this.Random.ArrayElement(commonMimeTypes)); + } - /// - /// Gets a random extension for the given mime type. - /// - /// - /// A random extension for the given mime type. - /// - public string FileExt(string mimeType = null) - { - if( mimeType != null && - lookup.TryGetValue(mimeType, out var mime) && - mime.ContainsKey("extensions") ) - { - return this.Random.ArrayElement(mime["extensions"] as BArray); - } - - return this.Random.ArrayElement(exts); - } + /// + /// Returns any file type available as mime-type. + /// + /// + /// Any file type available as mime-type. + /// + public string FileType() + { + return this.Random.ArrayElement(this.types); + } - /// - /// Get a random semver version string. - /// - /// - /// A random semver version string. - /// - public string Semver() - { - return $"{this.Random.Number(9)}.{this.Random.Number(9)}.{this.Random.Number(9)}"; - } - /// - /// Get a random `System.Version`. - /// - /// - /// A random `System.Version`. - /// - public Version Version() + /// + /// Gets a random extension for the given mime type. + /// + /// + /// A random extension for the given mime type. + /// + public string FileExt(string mimeType = null) + { + if( mimeType != null && + lookup.TryGetValue(mimeType, out var mime) && + mime.ContainsKey("extensions") ) { - return new Version(this.Random.Number(9), this.Random.Number(9), this.Random.Number(9), this.Random.Number(9)); + return this.Random.ArrayElement(mime["extensions"] as BArray); } + return this.Random.ArrayElement(exts); + } - /// - /// Get a random `Exception` with a fake stack trace. - /// - /// - /// A random `Exception` with a fake stack trace. - /// - public Exception Exception() - { - Exception exception = null; - switch( this.Random.Number(11) ) - { - case 0: - try - { - throw new ArgumentException(Random.Words(), Random.Word()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 1: - try - { - throw new ArgumentNullException(Random.Word(), Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 2: - try - { - throw new BadImageFormatException(Random.Words(), Random.Word()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 3: - try - { - throw new IndexOutOfRangeException(Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 4: - try - { - throw new ArithmeticException(Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 5: - try - { - throw new OutOfMemoryException(Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 6: - try - { - throw new FormatException(Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 7: - try - { - throw new DivideByZeroException(); - } - catch( Exception e ) - { - exception = e; - } - break; - case 8: - try - { - throw new EndOfStreamException(Random.Words()); - } - catch( Exception e ) - { - exception = e; - } - break; - case 9: - try - { - throw new FileNotFoundException("File not found...", Path.GetRandomFileName()); - } - catch( Exception e ) - { - exception = e; - } - break; + /// + /// Get a random semver version string. + /// + /// + /// A random semver version string. + /// + public string Semver() + { + return $"{this.Random.Number(9)}.{this.Random.Number(9)}.{this.Random.Number(9)}"; + } - case 10: - try - { - throw new NotImplementedException(); - } - catch( Exception e ) - { - exception = e; - } - break; + /// + /// Get a random `System.Version`. + /// + /// + /// A random `System.Version`. + /// + public Version Version() + { + return new Version(this.Random.Number(9), this.Random.Number(9), this.Random.Number(9), this.Random.Number(9)); + } - case 11: - try - { - throw new UnauthorizedAccessException(); - } - catch( Exception e ) - { - exception = e; - } - break; - } - return exception; + /// + /// Get a random `Exception` with a fake stack trace. + /// + /// + /// A random `Exception` with a fake stack trace. + /// + public Exception Exception() + { + Exception exception = null; + switch( this.Random.Number(11) ) + { + case 0: + try + { + throw new ArgumentException(Random.Words(), Random.Word()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 1: + try + { + throw new ArgumentNullException(Random.Word(), Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 2: + try + { + throw new BadImageFormatException(Random.Words(), Random.Word()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 3: + try + { + throw new IndexOutOfRangeException(Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 4: + try + { + throw new ArithmeticException(Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 5: + try + { + throw new OutOfMemoryException(Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 6: + try + { + throw new FormatException(Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 7: + try + { + throw new DivideByZeroException(); + } + catch( Exception e ) + { + exception = e; + } + break; + case 8: + try + { + throw new EndOfStreamException(Random.Words()); + } + catch( Exception e ) + { + exception = e; + } + break; + case 9: + try + { + throw new FileNotFoundException("File not found...", Path.GetRandomFileName()); + } + catch( Exception e ) + { + exception = e; + } + break; + + case 10: + try + { + throw new NotImplementedException(); + } + catch( Exception e ) + { + exception = e; + } + break; + + case 11: + try + { + throw new UnauthorizedAccessException(); + } + catch( Exception e ) + { + exception = e; + } + break; } - /// - /// Get a random GCM registration ID. - /// - /// - /// A random GCM registration ID. - /// - public string AndroidId() - { - const string androidIdCharacters = - "0123456789abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; + return exception; + } - return $"APA91{this.Random.String2(178, androidIdCharacters)}"; - } + /// + /// Get a random GCM registration ID. + /// + /// + /// A random GCM registration ID. + /// + public string AndroidId() + { + const string androidIdCharacters = + "0123456789abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; - /// - /// Get a random Apple Push Token. - /// - /// - /// A random Apple Push Token. - /// - public string ApplePushToken() - { - return this.Random.String2(64, Chars.HexLowerCase); - } + return $"APA91{this.Random.String2(178, androidIdCharacters)}"; + } - /// - /// Get a random BlackBerry Device PIN. - /// - /// - /// A random BlackBerry Device PIN. - /// - public string BlackBerryPin() - { - return this.Random.Hash(8); - } + /// + /// Get a random Apple Push Token. + /// + /// + /// A random Apple Push Token. + /// + public string ApplePushToken() + { + return this.Random.String2(64, Chars.HexLowerCase); + } + + /// + /// Get a random BlackBerry Device PIN. + /// + /// + /// A random BlackBerry Device PIN. + /// + public string BlackBerryPin() + { + return this.Random.Hash(8); } } \ No newline at end of file diff --git a/Source/Bogus/DataSets/Vehicle.cs b/Source/Bogus/DataSets/Vehicle.cs index 04a204d8..3e314e9a 100644 --- a/Source/Bogus/DataSets/Vehicle.cs +++ b/Source/Bogus/DataSets/Vehicle.cs @@ -1,57 +1,56 @@ using System.Text; -namespace Bogus.DataSets +namespace Bogus.DataSets; + +/// +/// Methods for generating vehicle information +/// +public class Vehicle : DataSet { /// - /// Methods for generating vehicle information + /// Generate a vehicle identification number (VIN). + /// + public string Vin() + { + var sb = new StringBuilder(); + + sb.Append(this.Random.String2(10, Chars.AlphaNumericUpperCase)); + sb.Append(this.Random.String2(1, Chars.UpperCase)); + sb.Append(this.Random.String2(1, Chars.AlphaNumericUpperCase)); + sb.Append(this.Random.Number(min: 10000, max: 100000)); + + return sb.ToString(); + } + + /// + /// Get a vehicle manufacture name. IE: Toyota, Ford, Porsche. + /// + public string Manufacturer() + { + return GetRandomArrayItem("manufacturer"); + } + + /// + /// Get a vehicle model. IE: Camry, Civic, Accord. /// - public class Vehicle : DataSet + public string Model() { - /// - /// Generate a vehicle identification number (VIN). - /// - public string Vin() - { - var sb = new StringBuilder(); - - sb.Append(this.Random.String2(10, Chars.AlphaNumericUpperCase)); - sb.Append(this.Random.String2(1, Chars.UpperCase)); - sb.Append(this.Random.String2(1, Chars.AlphaNumericUpperCase)); - sb.Append(this.Random.Number(min: 10000, max: 100000)); - - return sb.ToString(); - } - - /// - /// Get a vehicle manufacture name. IE: Toyota, Ford, Porsche. - /// - public string Manufacturer() - { - return GetRandomArrayItem("manufacturer"); - } - - /// - /// Get a vehicle model. IE: Camry, Civic, Accord. - /// - public string Model() - { - return GetRandomArrayItem("model"); - } - - /// - /// Get a vehicle type. IE: Minivan, SUV, Sedan. - /// - public string Type() - { - return GetRandomArrayItem("type"); - } - - /// - /// Get a vehicle fuel type. IE: Electric, Gasoline, Diesel. - /// - public string Fuel() - { - return GetRandomArrayItem("fuel"); - } + return GetRandomArrayItem("model"); } + + /// + /// Get a vehicle type. IE: Minivan, SUV, Sedan. + /// + public string Type() + { + return GetRandomArrayItem("type"); + } + + /// + /// Get a vehicle fuel type. IE: Electric, Gasoline, Diesel. + /// + public string Fuel() + { + return GetRandomArrayItem("fuel"); + } } \ No newline at end of file diff --git a/Source/Bogus/Database.cs b/Source/Bogus/Database.cs index 5d1f4dbc..91b03252 100644 --- a/Source/Bogus/Database.cs +++ b/Source/Bogus/Database.cs @@ -5,166 +5,165 @@ using Bogus.Bson; using Bogus.Platform; -namespace Bogus +namespace Bogus; + +/// +/// The main database object that can access locale data. +/// +public static class Database { /// - /// The main database object that can access locale data. + /// The root of all locales in a single BObject. /// - public static class Database - { - /// - /// The root of all locales in a single BObject. - /// - public static Lazy> Data = - new Lazy>(Initialize, LazyThreadSafetyMode.ExecutionAndPublication); - - /// - /// Returns all locales available inside Bogus' assembly manifest. - /// - public static string[] GetAllLocales() - { - var asm = typeof(Database).GetAssembly(); - - var parts = ResourceNameFormat.Split(new[] {"{0}"}, StringSplitOptions.None); + public static Lazy> Data = + new Lazy>(Initialize, LazyThreadSafetyMode.ExecutionAndPublication); - var prefix = parts[0]; - var suffix = parts[1]; + /// + /// Returns all locales available inside Bogus' assembly manifest. + /// + public static string[] GetAllLocales() + { + var asm = typeof(Database).GetAssembly(); - return asm.GetManifestResourceNames() - .Where(name => name.EndsWith(suffix)) - .Select(name => name.Replace(prefix, "").Replace(suffix, "")) - .ToArray(); - } + var parts = ResourceNameFormat.Split(new[] {"{0}"}, StringSplitOptions.None); - /// - /// Checks if a locale exists in Bogus. - /// - public static bool LocaleResourceExists(string locale) - { - var asm = typeof(Database).GetAssembly(); + var prefix = parts[0]; + var suffix = parts[1]; - var resourceName = GetLocaleResourceName(locale); + return asm.GetManifestResourceNames() + .Where(name => name.EndsWith(suffix)) + .Select(name => name.Replace(prefix, "").Replace(suffix, "")) + .ToArray(); + } - return ResourceHelper.ResourceExists(asm, resourceName); - } + /// + /// Checks if a locale exists in Bogus. + /// + public static bool LocaleResourceExists(string locale) + { + var asm = typeof(Database).GetAssembly(); - /// - /// Format of locale resource names. - /// - public const string ResourceNameFormat = "Bogus.data.{0}.locale.bson"; + var resourceName = GetLocaleResourceName(locale); - private static string GetLocaleResourceName(string locale) - { - return string.Format(ResourceNameFormat, locale); - } + return ResourceHelper.ResourceExists(asm, resourceName); + } - /// - /// Initializes the default locale database. - /// - private static ConcurrentDictionary Initialize() - { - //Just lazy load English only. - var d = new ConcurrentDictionary(); - d.TryAdd("en", DeserializeLocale("en")); - return d; - } + /// + /// Format of locale resource names. + /// + public const string ResourceNameFormat = "Bogus.data.{0}.locale.bson"; - internal static BObject DeserializeLocale(string locale) - { - var asm = typeof(Database).GetAssembly(); - var resourceName = GetLocaleResourceName(locale); + private static string GetLocaleResourceName(string locale) + { + return string.Format(ResourceNameFormat, locale); + } - return ResourceHelper.ReadBObjectResource(asm, resourceName); - } + /// + /// Initializes the default locale database. + /// + private static ConcurrentDictionary Initialize() + { + //Just lazy load English only. + var d = new ConcurrentDictionary(); + d.TryAdd("en", DeserializeLocale("en")); + return d; + } - /// - /// Gets a locale from the locale lookup cache, if the locale doesn't exist in the lookup cache, - /// the locale is read from the assembly manifest and added to the locale lookup cache. - /// - public static BObject GetLocale(string locale) - { - if( !Data.Value.TryGetValue(locale, out BObject l) ) - { - l = DeserializeLocale(locale); - Data.Value.TryAdd(locale, l); - } + internal static BObject DeserializeLocale(string locale) + { + var asm = typeof(Database).GetAssembly(); + var resourceName = GetLocaleResourceName(locale); - return l; - } + return ResourceHelper.ReadBObjectResource(asm, resourceName); + } - /// - /// Reset, reload, and reinitialize the locale from Bogus' assembly resource. - /// Any patches or modifications to the specified locale are destroyed. - /// - public static void ResetLocale(string locale) + /// + /// Gets a locale from the locale lookup cache, if the locale doesn't exist in the lookup cache, + /// the locale is read from the assembly manifest and added to the locale lookup cache. + /// + public static BObject GetLocale(string locale) + { + if( !Data.Value.TryGetValue(locale, out BObject l) ) { - Data.Value[locale] = DeserializeLocale(locale); + l = DeserializeLocale(locale); + Data.Value.TryAdd(locale, l); } - /// - /// Determines if a key exists in the locale. - /// - public static bool HasKey(string category, string path, string locale, string fallbackLocale = "en") - { - var l = GetLocale(locale); - var value = Select(category, path, l); - if( value != null ) - return true; + return l; + } - if( fallbackLocale == null ) return false; + /// + /// Reset, reload, and reinitialize the locale from Bogus' assembly resource. + /// Any patches or modifications to the specified locale are destroyed. + /// + public static void ResetLocale(string locale) + { + Data.Value[locale] = DeserializeLocale(locale); + } - l = GetLocale(fallbackLocale); - value = Select(category, path, l); + /// + /// Determines if a key exists in the locale. + /// + public static bool HasKey(string category, string path, string locale, string fallbackLocale = "en") + { + var l = GetLocale(locale); + var value = Select(category, path, l); + if( value != null ) + return true; - if( value != null ) - return true; + if( fallbackLocale == null ) return false; - return false; - } + l = GetLocale(fallbackLocale); + value = Select(category, path, l); - /// - /// Returns the JToken of the locale category path. If the key does not exist, then the locale fallback is used. - /// - public static BValue Get(string category, string path, string locale = "en", string localeFallback = "en") - { - var l = GetLocale(locale); + if( value != null ) + return true; - var val = Select(category, path, l); + return false; + } - if( val != null ) - { - return val; - } + /// + /// Returns the JToken of the locale category path. If the key does not exist, then the locale fallback is used. + /// + public static BValue Get(string category, string path, string locale = "en", string localeFallback = "en") + { + var l = GetLocale(locale); - //fall back path - var fallback = GetLocale(localeFallback); + var val = Select(category, path, l); - return Select(category, path, fallback); + if( val != null ) + { + return val; } - private static BValue Select(string category, string path, BValue localeRoot) + //fall back path + var fallback = GetLocale(localeFallback); + + return Select(category, path, fallback); + } + + private static BValue Select(string category, string path, BValue localeRoot) + { + var section = localeRoot[category]; + if( section is null ) return null; + + var current = 0; + while( true ) { - var section = localeRoot[category]; - if( section is null ) return null; + var len = path.IndexOf('.', current); - var current = 0; - while( true ) + string key; + + if( len < 0 ) { - var len = path.IndexOf('.', current); - - string key; - - if( len < 0 ) - { - //dot in path not found, final key - key = path.Substring(current); - return section[key]; - } - key = path.Substring(current, len); - section = section[key]; - if( section is null ) return null; - current = len + 1; + //dot in path not found, final key + key = path.Substring(current); + return section[key]; } + key = path.Substring(current, len); + section = section[key]; + if( section is null ) return null; + current = len + 1; } } } \ No newline at end of file diff --git a/Source/Bogus/Distributions/Gaussian/ExtensionsForRandomizer.cs b/Source/Bogus/Distributions/Gaussian/ExtensionsForRandomizer.cs index 69823ee5..b87c7c34 100644 --- a/Source/Bogus/Distributions/Gaussian/ExtensionsForRandomizer.cs +++ b/Source/Bogus/Distributions/Gaussian/ExtensionsForRandomizer.cs @@ -1,135 +1,134 @@ using System; -namespace Bogus.Distributions.Gaussian -{ +namespace Bogus.Distributions.Gaussian; - public static class ExtensionsForRandomizer - { - - // Coefficients used in Acklam's Inverse Normal Cumulative Distribution function. - private static readonly double[] AcklamsCoefficientA = - {-39.696830d, 220.946098d, -275.928510d, 138.357751d, -30.664798d, 2.506628d}; - private static readonly double[] AcklamsCoefficientB = - {-54.476098d, 161.585836d, -155.698979d, 66.801311d, -13.280681d}; +public static class ExtensionsForRandomizer + { - private static readonly double[] AcklamsCoefficientC = - {-0.007784894002d, -0.32239645d, -2.400758d, -2.549732d, 4.374664d, 2.938163d}; + // Coefficients used in Acklam's Inverse Normal Cumulative Distribution function. + private static readonly double[] AcklamsCoefficientA = + {-39.696830d, 220.946098d, -275.928510d, 138.357751d, -30.664798d, 2.506628d}; - private static readonly double[] AcklamsCoefficientD = { 0.007784695709d, 0.32246712d, 2.445134d, 3.754408d }; + private static readonly double[] AcklamsCoefficientB = + {-54.476098d, 161.585836d, -155.698979d, 66.801311d, -13.280681d}; - // Break-Points used in Acklam's Inverse Normal Cumulative Distribution function. - private const double AcklamsLowBreakPoint = 0.02425d; - private const double AcklamsHighBreakPoint = 1.0d - AcklamsLowBreakPoint; + private static readonly double[] AcklamsCoefficientC = + {-0.007784894002d, -0.32239645d, -2.400758d, -2.549732d, 4.374664d, 2.938163d}; + private static readonly double[] AcklamsCoefficientD = { 0.007784695709d, 0.32246712d, 2.445134d, 3.754408d }; - /// - /// This algorithm follows Peter J Acklam's Inverse Normal Cumulative Distribution function. - /// Reference: P.J. Acklam, "An algorithm for computing the inverse normal cumulative distribution function," 2010 - /// - /// - /// A double between 0.0 and 1.0 - /// - private static double InverseNCD(double probability) - { - // Rational approximation for lower region of distribution - if (probability < AcklamsLowBreakPoint) - { - double q = Math.Sqrt(-2 * Math.Log(probability)); - return (((((AcklamsCoefficientC[0] * q + AcklamsCoefficientC[1]) * q + AcklamsCoefficientC[2]) * q + AcklamsCoefficientC[3]) * q + - AcklamsCoefficientC[4]) * q + AcklamsCoefficientC[5]) / - ((((AcklamsCoefficientD[0] * q + AcklamsCoefficientD[1]) * q + AcklamsCoefficientD[2]) * q + AcklamsCoefficientD[3]) * q + 1); - } - - // Rational approximation for upper region of distribution - if (AcklamsHighBreakPoint < probability) - { - double q = Math.Sqrt(-2 * Math.Log(1 - probability)); - return -(((((AcklamsCoefficientC[0] * q + AcklamsCoefficientC[1]) * q + AcklamsCoefficientC[2]) * q + AcklamsCoefficientC[3]) * q + - AcklamsCoefficientC[4]) * q + AcklamsCoefficientC[5]) / - ((((AcklamsCoefficientD[0] * q + AcklamsCoefficientD[1]) * q + AcklamsCoefficientD[2]) * q + AcklamsCoefficientD[3]) * q + 1); - } - - // Rational approximation for central region of distribution - { - double q = probability - 0.5d; - double r = q * q; - return (((((AcklamsCoefficientA[0] * r + AcklamsCoefficientA[1]) * r + AcklamsCoefficientA[2]) * r + AcklamsCoefficientA[3]) * r + - AcklamsCoefficientA[4]) * r + AcklamsCoefficientA[5]) * q / - (((((AcklamsCoefficientB[0] * r + AcklamsCoefficientB[1]) * r + AcklamsCoefficientB[2]) * r + AcklamsCoefficientB[3]) * r + - AcklamsCoefficientB[4]) * r + 1); - } - } + // Break-Points used in Acklam's Inverse Normal Cumulative Distribution function. + private const double AcklamsLowBreakPoint = 0.02425d; + private const double AcklamsHighBreakPoint = 1.0d - AcklamsLowBreakPoint; - /// - /// Generate a random double, based on the specified normal distribution. - /// - /// To create random values around an average height of 69.1 - /// inches with a standard deviation of 2.9 inches away from the mean - /// - /// GaussianDouble(69.1, 2.9) - /// - /// - /// - /// Mean value of the normal distribution - /// Standard deviation of the normal distribution - public static double GaussianDouble(this Randomizer rnd, double mean, double standardDeviation) - { - double p = InverseNCD(rnd.Double(0D, 1D)); - return (p * standardDeviation) + mean; - } - - /// - /// Generate a random int, based on the specified normal distribution. - /// - /// To create random int values around an average age of 35 years, with - /// a standard deviation of 4 years away from the mean. - /// - /// - /// call GaussianInt(35, 4) - /// - /// - /// Mean average of the normal distribution - /// Standard deviation of the normal distribution - public static int GaussianInt(this Randomizer rnd, double mean, double standardDeviation) + /// + /// This algorithm follows Peter J Acklam's Inverse Normal Cumulative Distribution function. + /// Reference: P.J. Acklam, "An algorithm for computing the inverse normal cumulative distribution function," 2010 + /// + /// + /// A double between 0.0 and 1.0 + /// + private static double InverseNCD(double probability) + { + // Rational approximation for lower region of distribution + if (probability < AcklamsLowBreakPoint) { - return Convert.ToInt32(GaussianDouble(rnd, mean, standardDeviation)); + double q = Math.Sqrt(-2 * Math.Log(probability)); + return (((((AcklamsCoefficientC[0] * q + AcklamsCoefficientC[1]) * q + AcklamsCoefficientC[2]) * q + AcklamsCoefficientC[3]) * q + + AcklamsCoefficientC[4]) * q + AcklamsCoefficientC[5]) / + ((((AcklamsCoefficientD[0] * q + AcklamsCoefficientD[1]) * q + AcklamsCoefficientD[2]) * q + AcklamsCoefficientD[3]) * q + 1); } - /// - /// Generate a float decimal, based on the specified normal distribution. - /// - /// To create random float values around an average height of 69.1 - /// inches with a standard deviation of 2.9 inches away from the mean - /// - /// GaussianFloat(69.1, 2.9) - /// - /// - /// - /// Mean average of the normal distribution - /// Standard deviation of the normal distribution - public static float GaussianFloat(this Randomizer rnd, double mean, double standardDeviation) + // Rational approximation for upper region of distribution + if (AcklamsHighBreakPoint < probability) { - return Convert.ToSingle(GaussianDouble(rnd, mean, standardDeviation)); + double q = Math.Sqrt(-2 * Math.Log(1 - probability)); + return -(((((AcklamsCoefficientC[0] * q + AcklamsCoefficientC[1]) * q + AcklamsCoefficientC[2]) * q + AcklamsCoefficientC[3]) * q + + AcklamsCoefficientC[4]) * q + AcklamsCoefficientC[5]) / + ((((AcklamsCoefficientD[0] * q + AcklamsCoefficientD[1]) * q + AcklamsCoefficientD[2]) * q + AcklamsCoefficientD[3]) * q + 1); } - /// - /// Generate a random decimal, based on the specified normal distribution. - /// - /// To create random values around an average height of 69.1 - /// inches with a standard deviation of 2.9 inches away from the mean - /// - /// GaussianDecimal(69.1, 2.9) - /// - /// - /// - /// Mean average of the normal distribution - /// Standard deviation of the normal distribution - public static decimal GaussianDecimal(this Randomizer rnd, double mean, double standardDeviation) + // Rational approximation for central region of distribution { - return Convert.ToDecimal(GaussianDouble(rnd, mean, standardDeviation)); + double q = probability - 0.5d; + double r = q * q; + return (((((AcklamsCoefficientA[0] * r + AcklamsCoefficientA[1]) * r + AcklamsCoefficientA[2]) * r + AcklamsCoefficientA[3]) * r + + AcklamsCoefficientA[4]) * r + AcklamsCoefficientA[5]) * q / + (((((AcklamsCoefficientB[0] * r + AcklamsCoefficientB[1]) * r + AcklamsCoefficientB[2]) * r + AcklamsCoefficientB[3]) * r + + AcklamsCoefficientB[4]) * r + 1); } + } + + /// + /// Generate a random double, based on the specified normal distribution. + /// + /// To create random values around an average height of 69.1 + /// inches with a standard deviation of 2.9 inches away from the mean + /// + /// GaussianDouble(69.1, 2.9) + /// + /// + /// + /// Mean value of the normal distribution + /// Standard deviation of the normal distribution + public static double GaussianDouble(this Randomizer rnd, double mean, double standardDeviation) + { + double p = InverseNCD(rnd.Double(0D, 1D)); + return (p * standardDeviation) + mean; + } + + /// + /// Generate a random int, based on the specified normal distribution. + /// + /// To create random int values around an average age of 35 years, with + /// a standard deviation of 4 years away from the mean. + /// + /// + /// call GaussianInt(35, 4) + /// + /// + /// Mean average of the normal distribution + /// Standard deviation of the normal distribution + public static int GaussianInt(this Randomizer rnd, double mean, double standardDeviation) + { + return Convert.ToInt32(GaussianDouble(rnd, mean, standardDeviation)); + } + + /// + /// Generate a float decimal, based on the specified normal distribution. + /// + /// To create random float values around an average height of 69.1 + /// inches with a standard deviation of 2.9 inches away from the mean + /// + /// GaussianFloat(69.1, 2.9) + /// + /// + /// + /// Mean average of the normal distribution + /// Standard deviation of the normal distribution + public static float GaussianFloat(this Randomizer rnd, double mean, double standardDeviation) + { + return Convert.ToSingle(GaussianDouble(rnd, mean, standardDeviation)); } + + /// + /// Generate a random decimal, based on the specified normal distribution. + /// + /// To create random values around an average height of 69.1 + /// inches with a standard deviation of 2.9 inches away from the mean + /// + /// GaussianDecimal(69.1, 2.9) + /// + /// + /// + /// Mean average of the normal distribution + /// Standard deviation of the normal distribution + public static decimal GaussianDecimal(this Randomizer rnd, double mean, double standardDeviation) + { + return Convert.ToDecimal(GaussianDouble(rnd, mean, standardDeviation)); + } + } diff --git a/Source/Bogus/Extensions/Brazil/ExtensionsForBrazil.cs b/Source/Bogus/Extensions/Brazil/ExtensionsForBrazil.cs index 47378d78..76d6a978 100644 --- a/Source/Bogus/Extensions/Brazil/ExtensionsForBrazil.cs +++ b/Source/Bogus/Extensions/Brazil/ExtensionsForBrazil.cs @@ -1,113 +1,112 @@ using System.Linq; using Bogus.DataSets; -namespace Bogus.Extensions.Brazil +namespace Bogus.Extensions.Brazil; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForBrazil { + private static readonly int[] CpfWeights = {10, 9, 8, 7, 6, 5, 4, 3, 2}; + private static readonly int[] CnpjWeights = {2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6}; + /// - /// API extensions specific for a geographical location. + /// Cadastro de Pessoas Físicas /// - public static class ExtensionsForBrazil + /// Includes formatting symbols. + public static string Cpf(this Person p, bool includeFormatSymbols = true) { - private static readonly int[] CpfWeights = {10, 9, 8, 7, 6, 5, 4, 3, 2}; - private static readonly int[] CnpjWeights = {2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5, 6}; - - /// - /// Cadastro de Pessoas Físicas - /// - /// Includes formatting symbols. - public static string Cpf(this Person p, bool includeFormatSymbols = true) + int[] finalDigits; + + const string Key = nameof(ExtensionsForBrazil) + "CPF"; + if( p.context.ContainsKey(Key) ) { - int[] finalDigits; + finalDigits = p.context[Key] as int[]; + return FormatCpf(finalDigits, includeFormatSymbols); + } - const string Key = nameof(ExtensionsForBrazil) + "CPF"; - if( p.context.ContainsKey(Key) ) - { - finalDigits = p.context[Key] as int[]; - return FormatCpf(finalDigits, includeFormatSymbols); - } + var digits = p.Random.Digits(9); + var sum1 = digits.Zip(CpfWeights, (d, w) => d * w) + .Sum(); - var digits = p.Random.Digits(9); - var sum1 = digits.Zip(CpfWeights, (d, w) => d * w) - .Sum(); + var sum1mod = sum1 % 11; - var sum1mod = sum1 % 11; + int check1 = 0; + if( sum1mod >= 2 ) + { + check1 = 11 - sum1mod; + } - int check1 = 0; - if( sum1mod >= 2 ) - { - check1 = 11 - sum1mod; - } + var finalWeights = new[] {11}.Concat(CpfWeights); - var finalWeights = new[] {11}.Concat(CpfWeights); + var sum2 = digits.Concat(new[] {check1}) + .Zip(finalWeights, (d, w) => d * w) + .Sum(); - var sum2 = digits.Concat(new[] {check1}) - .Zip(finalWeights, (d, w) => d * w) - .Sum(); + var sum2mod = sum2 % 11; - var sum2mod = sum2 % 11; + var check2 = 0; + if( sum2mod >= 2 ) + { + check2 = 11 - sum2mod; + } - var check2 = 0; - if( sum2mod >= 2 ) - { - check2 = 11 - sum2mod; - } + finalDigits = digits.Concat(new[] {check1, check2}).ToArray(); - finalDigits = digits.Concat(new[] {check1, check2}).ToArray(); + p.context[Key] = finalDigits; - p.context[Key] = finalDigits; + return FormatCpf(finalDigits, includeFormatSymbols); + } - return FormatCpf(finalDigits, includeFormatSymbols); + public static string FormatCpf(int[] digits, bool includeFormatSymbols) + { + if (includeFormatSymbols) + { + return $"{digits[0]}{digits[1]}{digits[2]}.{digits[3]}{digits[4]}{digits[5]}.{digits[6]}{digits[7]}{digits[8]}-{digits[9]}{digits[10]}"; } - - public static string FormatCpf(int[] digits, bool includeFormatSymbols) + else { - if (includeFormatSymbols) - { - return $"{digits[0]}{digits[1]}{digits[2]}.{digits[3]}{digits[4]}{digits[5]}.{digits[6]}{digits[7]}{digits[8]}-{digits[9]}{digits[10]}"; - } - else - { - return $"{digits[0]}{digits[1]}{digits[2]}{digits[3]}{digits[4]}{digits[5]}{digits[6]}{digits[7]}{digits[8]}{digits[9]}{digits[10]}"; - } + return $"{digits[0]}{digits[1]}{digits[2]}{digits[3]}{digits[4]}{digits[5]}{digits[6]}{digits[7]}{digits[8]}{digits[9]}{digits[10]}"; } + } + + + /// + /// Cadastro Nacional da Pessoa Jurídica + /// + /// Includes formatting symbols. + public static string Cnpj(this Company c, bool includeFormatSymbols = true) + { + var digits = c.Random.Digits(12); + digits[8] = 0; + digits[9] = 0; + digits[10] = 0; + digits[11] = 1; + var firstDigit = digits.Reverse().Zip(CnpjWeights, (d, w) => d * w).Sum(); + firstDigit = 11 - firstDigit % 11; - /// - /// Cadastro Nacional da Pessoa Jurídica - /// - /// Includes formatting symbols. - public static string Cnpj(this Company c, bool includeFormatSymbols = true) + if( firstDigit >= 10 ) + firstDigit = 0; + + var secondDigit = firstDigit * 2 + digits.Reverse().Zip(CnpjWeights.Skip(1), (d, w) => d * w).Sum(); + secondDigit = 11 - (secondDigit % 11); + if( secondDigit >= 10 ) + secondDigit = 0; + + var all = digits.Concat(new[] {firstDigit, secondDigit}).ToArray(); + + string final; + if ( includeFormatSymbols ) { - var digits = c.Random.Digits(12); - digits[8] = 0; - digits[9] = 0; - digits[10] = 0; - digits[11] = 1; - - var firstDigit = digits.Reverse().Zip(CnpjWeights, (d, w) => d * w).Sum(); - firstDigit = 11 - firstDigit % 11; - - if( firstDigit >= 10 ) - firstDigit = 0; - - var secondDigit = firstDigit * 2 + digits.Reverse().Zip(CnpjWeights.Skip(1), (d, w) => d * w).Sum(); - secondDigit = 11 - (secondDigit % 11); - if( secondDigit >= 10 ) - secondDigit = 0; - - var all = digits.Concat(new[] {firstDigit, secondDigit}).ToArray(); - - string final; - if ( includeFormatSymbols ) - { - final = $"{all[0]}{all[1]}.{all[2]}{all[3]}{all[4]}.{all[5]}{all[6]}{all[7]}/{all[8]}{all[9]}{all[10]}{all[11]}-{all[12]}{all[13]}"; - } - else - { - final = $"{all[0]}{all[1]}{all[2]}{all[3]}{all[4]}{all[5]}{all[6]}{all[7]}{all[8]}{all[9]}{all[10]}{all[11]}{all[12]}{all[13]}"; - } - - return final; + final = $"{all[0]}{all[1]}.{all[2]}{all[3]}{all[4]}.{all[5]}{all[6]}{all[7]}/{all[8]}{all[9]}{all[10]}{all[11]}-{all[12]}{all[13]}"; } + else + { + final = $"{all[0]}{all[1]}{all[2]}{all[3]}{all[4]}{all[5]}{all[6]}{all[7]}{all[8]}{all[9]}{all[10]}{all[11]}{all[12]}{all[13]}"; + } + + return final; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Canada/ExtensionsForCanada.cs b/Source/Bogus/Extensions/Canada/ExtensionsForCanada.cs index b54994cb..af701f58 100644 --- a/Source/Bogus/Extensions/Canada/ExtensionsForCanada.cs +++ b/Source/Bogus/Extensions/Canada/ExtensionsForCanada.cs @@ -1,77 +1,76 @@ using System.Linq; -namespace Bogus.Extensions.Canada +namespace Bogus.Extensions.Canada; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForCanada { + private static int[] Mask = {1, 2, 1, 2, 1, 2, 1, 2, 1}; + /// - /// API extensions specific for a geographical location. + /// Social Insurance Number for Canada /// - public static class ExtensionsForCanada + public static string Sin(this Person p) { - private static int[] Mask = {1, 2, 1, 2, 1, 2, 1, 2, 1}; - - /// - /// Social Insurance Number for Canada - /// - public static string Sin(this Person p) + const string Key = nameof(ExtensionsForCanada) + "SIN"; + if( p.context.ContainsKey(Key) ) { - const string Key = nameof(ExtensionsForCanada) + "SIN"; - if( p.context.ContainsKey(Key) ) - { - return p.context[Key] as string; - } + return p.context[Key] as string; + } - //bit verbose, but works. :) - //could be mathematically simplified. - //brute forced this one. yeah. - //d - //should pass basic validation, but only some - //numbers dont start with 8 etc. + //bit verbose, but works. :) + //could be mathematically simplified. + //brute forced this one. yeah. + //d + //should pass basic validation, but only some + //numbers dont start with 8 etc. - /* - 1 — Atlantic Provinces: Nova Scotia, New Brunswick, Prince Edward Island, and Newfoundland and Labrador (this may also cover overseas residents). - 2–3 — Quebec - 4–5 — Ontario (#4 includes overseas forces) - 6 — Prairie Provinces (Manitoba, Saskatchewan, and Alberta), Northwest Territories, and Nunavut - 7 — Pacific Region (British Columbia and Yukon) - 8 — Not used - 9 — Temporary resident - 0 — Not used (Canada Revenue may assign fictitious SIN numbers beginning with zero to taxpayers who do not have SINs) - */ + /* + 1 — Atlantic Provinces: Nova Scotia, New Brunswick, Prince Edward Island, and Newfoundland and Labrador (this may also cover overseas residents). + 2–3 — Quebec + 4–5 — Ontario (#4 includes overseas forces) + 6 — Prairie Provinces (Manitoba, Saskatchewan, and Alberta), Northwest Territories, and Nunavut + 7 — Pacific Region (British Columbia and Yukon) + 8 — Not used + 9 — Temporary resident + 0 — Not used (Canada Revenue may assign fictitious SIN numbers beginning with zero to taxpayers who do not have SINs) + */ - var r = p.Random; - //get 8 numbers - var numbers = r.Digits(8); + var r = p.Random; + //get 8 numbers + var numbers = r.Digits(8); - // the last number that makes it pass the checksum. - var last = 10 - (numbers.Sum() % 10); - if( last == 10 ) - last = 0; + // the last number that makes it pass the checksum. + var last = 10 - (numbers.Sum() % 10); + if( last == 10 ) + last = 0; - var digits = numbers.Concat(new[] {last}).ToArray(); + var digits = numbers.Concat(new[] {last}).ToArray(); - var comp = digits - .Zip(Mask, (n, c) => + var comp = digits + .Zip(Mask, (n, c) => + { + if( c == 2 && n % c == 1 ) + { + // odd digit, it was multiplied, reverse the process + return (10 + (n - 1)) / 2; + } + if( c == 2 ) { - if( c == 2 && n % c == 1 ) - { - // odd digit, it was multiplied, reverse the process - return (10 + (n - 1)) / 2; - } - if( c == 2 ) - { - //simply divide an even number by two - return n / 2; - } - //else c == 1, and n was multiplied by 1 - return n; - }).ToArray(); + //simply divide an even number by two + return n / 2; + } + //else c == 1, and n was multiplied by 1 + return n; + }).ToArray(); - var sinstr = $"{comp[0]}{comp[1]}{comp[2]} {comp[3]}{comp[4]}{comp[5]} {comp[6]}{comp[7]}{comp[8]}"; + var sinstr = $"{comp[0]}{comp[1]}{comp[2]} {comp[3]}{comp[4]}{comp[5]} {comp[6]}{comp[7]}{comp[8]}"; - p.context[Key] = sinstr; + p.context[Key] = sinstr; - return sinstr; - } + return sinstr; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Denmark/ExtensionsForDenmark.cs b/Source/Bogus/Extensions/Denmark/ExtensionsForDenmark.cs index f2dd42d7..2efcddd4 100644 --- a/Source/Bogus/Extensions/Denmark/ExtensionsForDenmark.cs +++ b/Source/Bogus/Extensions/Denmark/ExtensionsForDenmark.cs @@ -1,26 +1,25 @@ -namespace Bogus.Extensions.Denmark +namespace Bogus.Extensions.Denmark; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForDenmark { /// - /// API extensions specific for a geographical location. + /// Danish Personal Identification number /// - public static class ExtensionsForDenmark + public static string Cpr(this Person p) { - /// - /// Danish Personal Identification number - /// - public static string Cpr(this Person p) + const string Key = nameof(ExtensionsForDenmark) + "CPR"; + if( p.context.ContainsKey(Key) ) { - const string Key = nameof(ExtensionsForDenmark) + "CPR"; - if( p.context.ContainsKey(Key) ) - { - return p.context[Key] as string; - } + return p.context[Key] as string; + } - var r = p.Random; - var final = $"{p.DateOfBirth:ddMMyy}-{r.Replace("####")}"; + var r = p.Random; + var final = $"{p.DateOfBirth:ddMMyy}-{r.Replace("####")}"; - p.context[Key] = final; - return final; - } + p.context[Key] = final; + return final; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/ExtensionsForCultureInfo.cs b/Source/Bogus/Extensions/ExtensionsForCultureInfo.cs index 5756068d..a77076dc 100644 --- a/Source/Bogus/Extensions/ExtensionsForCultureInfo.cs +++ b/Source/Bogus/Extensions/ExtensionsForCultureInfo.cs @@ -1,30 +1,30 @@ using System.Collections.Generic; using System.Globalization; -namespace Bogus.Extensions +namespace Bogus.Extensions; + +/// +/// Extension methods over . +/// +public static class ExtensionsForCultureInfo { + public static Dictionary Lookup = new Dictionary + { + {"cs", "cz"}, + {"en-IN", "en_IND"}, + {"ka", "ge"}, + {"id", "id_ID"}, + {"nb", "nb_NO"}, + {"nn", "nb_NO"}, + {"no", "nb_NO"}, + }; + /// - /// Extension methods over . + /// Helper extension that maps .NET to Bogus locale codes like 'en_US`. /// - public static class ExtensionsForCultureInfo + public static string ToBogusLocale(this CultureInfo ci) { - public static Dictionary Lookup = new Dictionary - { - {"cs", "cz"}, - {"en-IN", "en_IND"}, - {"ka", "ge"}, - {"id", "id_ID"}, - {"nb", "nb_NO"}, - {"nn", "nb_NO"}, - {"no", "nb_NO"}, - }; - - /// - /// Helper extension that maps .NET to Bogus locale codes like 'en_US`. - /// - public static string ToBogusLocale(this CultureInfo ci) - { - /* + /* Re: https://github.com/bchavez/Bogus/issues/132 Looks like the following need to be remapped for Bogus @@ -34,32 +34,31 @@ public static string ToBogusLocale(this CultureInfo ci) id -> id_ID nb,nn -> nb_NO - */ + */ - var locale = Normalize(ci); + var locale = Normalize(ci); - // try matching the full locale code with the _ character - if( Database.LocaleResourceExists(locale) ) return locale; + // try matching the full locale code with the _ character + if( Database.LocaleResourceExists(locale) ) return locale; - if( locale.Contains("_") ) - { - //try matching the locale without _ - locale = locale.Substring(0, locale.IndexOf('_')); - } + if( locale.Contains("_") ) + { + //try matching the locale without _ + locale = locale.Substring(0, locale.IndexOf('_')); + } - if( Database.LocaleResourceExists(locale) ) return locale; + if( Database.LocaleResourceExists(locale) ) return locale; - //if we tried the locale code before the _ then we have no match - //sorry :( - return null; - } + //if we tried the locale code before the _ then we have no match + //sorry :( + return null; + } - private static string Normalize(CultureInfo ci) - { - if( Lookup.TryGetValue(ci.Name, out var bogusCode) ) return bogusCode; - if( Lookup.TryGetValue(ci.TwoLetterISOLanguageName, out bogusCode) ) return bogusCode; + private static string Normalize(CultureInfo ci) + { + if( Lookup.TryGetValue(ci.Name, out var bogusCode) ) return bogusCode; + if( Lookup.TryGetValue(ci.TwoLetterISOLanguageName, out bogusCode) ) return bogusCode; - return ci.Name.Replace('-', '_'); - } + return ci.Name.Replace('-', '_'); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/ExtensionsForPropertyInfo.cs b/Source/Bogus/Extensions/ExtensionsForPropertyInfo.cs index a6fe7d10..67ce3d18 100644 --- a/Source/Bogus/Extensions/ExtensionsForPropertyInfo.cs +++ b/Source/Bogus/Extensions/ExtensionsForPropertyInfo.cs @@ -1,36 +1,35 @@ using System; using System.Reflection; -namespace Bogus.Extensions +namespace Bogus.Extensions; + +public static class ExtensionsForPropertyInfo { - public static class ExtensionsForPropertyInfo - { - private static readonly MethodInfo GenericSetterCreationMethod = - typeof(ExtensionsForPropertyInfo).GetMethod(nameof(CreateSetterGeneric), BindingFlags.Static | BindingFlags.NonPublic); + private static readonly MethodInfo GenericSetterCreationMethod = + typeof(ExtensionsForPropertyInfo).GetMethod(nameof(CreateSetterGeneric), BindingFlags.Static | BindingFlags.NonPublic); - public static Action CreateSetter(this PropertyInfo property) - { - if (property == null) throw new ArgumentNullException(nameof(property)); + public static Action CreateSetter(this PropertyInfo property) + { + if (property == null) throw new ArgumentNullException(nameof(property)); - var setter = property.GetSetMethod(true); - if (setter == null) throw new ArgumentException($"The specified property '{property.Name}' does not have a setter method."); + var setter = property.GetSetMethod(true); + if (setter == null) throw new ArgumentException($"The specified property '{property.Name}' does not have a setter method."); - var genericHelper = GenericSetterCreationMethod.MakeGenericMethod(property.DeclaringType, property.PropertyType); - return (Action)genericHelper.Invoke(null, new object[] { setter }); - } + var genericHelper = GenericSetterCreationMethod.MakeGenericMethod(property.DeclaringType, property.PropertyType); + return (Action)genericHelper.Invoke(null, new object[] { setter }); + } - private static Action CreateSetterGeneric(MethodInfo setter) where T : class - { - var setterTypedDelegate = + private static Action CreateSetterGeneric(MethodInfo setter) where T : class + { + var setterTypedDelegate = #if STANDARD - (Action) setter.CreateDelegate(typeof(Action)) + (Action) setter.CreateDelegate(typeof(Action)) #else - (Action) Delegate.CreateDelegate(typeof(Action), setter) + (Action) Delegate.CreateDelegate(typeof(Action), setter) #endif - ; - var setterDelegate = (Action)((T instance, object value) => { setterTypedDelegate(instance, (V)value); }); - return setterDelegate; - } + ; + var setterDelegate = (Action)((T instance, object value) => { setterTypedDelegate(instance, (V)value); }); + return setterDelegate; + } - } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/ExtensionsForRandomizer.cs b/Source/Bogus/Extensions/ExtensionsForRandomizer.cs index 97f82e37..5538601c 100644 --- a/Source/Bogus/Extensions/ExtensionsForRandomizer.cs +++ b/Source/Bogus/Extensions/ExtensionsForRandomizer.cs @@ -1,45 +1,44 @@ -namespace Bogus.Extensions +namespace Bogus.Extensions; + +public static class ExtensionsForRandomizer { - public static class ExtensionsForRandomizer + /// + /// Get a random decimal, between 0.0 and 1.0. + /// + /// Minimum, default 0.0 + /// Maximum, default 1.0 + public static decimal Decimal2(this Randomizer r, decimal min = 0.0m, decimal max = 1.0m) { - /// - /// Get a random decimal, between 0.0 and 1.0. - /// - /// Minimum, default 0.0 - /// Maximum, default 1.0 - public static decimal Decimal2(this Randomizer r, decimal min = 0.0m, decimal max = 1.0m) - { - // Decimal: 128 bits wide - // bit 0: sign bit - // bit 1-10: not used - // bit 11-15: scale (values 29, 30, 31 not used) - // bit 16-31: not used - // bit 32-127: mantissa (96 bits) - - // Max value: 00000000 FFFFFFFF FFFFFFFF FFFFFFFF - // = 79228162514264337593543950335 - - // Max value with max scaling: 001C0000 FFFFFFFF FFFFFFFF FFFFFFFF - // = 7.9228162514264337593543950335 - - // Step 1: Generate a value with uniform distribution between 0 and this value. - // This ensures the greatest level of precision in the distribution of bits; - // the resulting value, after it is adjusted into the caller's desired range, - // should not skip any possible values at the least significant end of the - // mantissa. - - int lowBits = r.Number(int.MinValue, int.MaxValue); - int middleBits = r.Number(int.MinValue, int.MaxValue); - int highBits = r.Number(int.MinValue, int.MaxValue); - - const int Scale = 28; - - decimal result = new decimal(lowBits, middleBits, highBits, isNegative: false, Scale); - - // Step 2: Scale the value and adjust it to the desired range. This may decrease - // the accuracy by adjusting the scale as necessary, but we get the best possible - // outcome by starting with the most precise scale. - return result * (max - min) / 7.9228162514264337593543950335m + min; - } + // Decimal: 128 bits wide + // bit 0: sign bit + // bit 1-10: not used + // bit 11-15: scale (values 29, 30, 31 not used) + // bit 16-31: not used + // bit 32-127: mantissa (96 bits) + + // Max value: 00000000 FFFFFFFF FFFFFFFF FFFFFFFF + // = 79228162514264337593543950335 + + // Max value with max scaling: 001C0000 FFFFFFFF FFFFFFFF FFFFFFFF + // = 7.9228162514264337593543950335 + + // Step 1: Generate a value with uniform distribution between 0 and this value. + // This ensures the greatest level of precision in the distribution of bits; + // the resulting value, after it is adjusted into the caller's desired range, + // should not skip any possible values at the least significant end of the + // mantissa. + + int lowBits = r.Number(int.MinValue, int.MaxValue); + int middleBits = r.Number(int.MinValue, int.MaxValue); + int highBits = r.Number(int.MinValue, int.MaxValue); + + const int Scale = 28; + + decimal result = new decimal(lowBits, middleBits, highBits, isNegative: false, Scale); + + // Step 2: Scale the value and adjust it to the desired range. This may decrease + // the accuracy by adjusting the scale as necessary, but we get the best possible + // outcome by starting with the most precise scale. + return result * (max - min) / 7.9228162514264337593543950335m + min; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/ExtensionsForString.cs b/Source/Bogus/Extensions/ExtensionsForString.cs index 20c0adb7..b14b2762 100644 --- a/Source/Bogus/Extensions/ExtensionsForString.cs +++ b/Source/Bogus/Extensions/ExtensionsForString.cs @@ -2,64 +2,63 @@ using System.Globalization; using System.Text; -namespace Bogus.Extensions +namespace Bogus.Extensions; + +/// +/// General helper string extensions for working with fake data. +/// +public static class ExtensionsForString { /// - /// General helper string extensions for working with fake data. + /// Clamps the length of a string filling between min and max characters. + /// If the string is below the minimum, the string is appended with paddingChar up to the minimum length. + /// If the string is over the maximum, the string is truncated at maximum characters; additionally, if the result string ends with + /// whitespace, it is replaced with a paddingChar characters. /// - public static class ExtensionsForString + public static string ClampLength(this string str, int? min = null, int? max = null, char paddingChar = 'A') { - /// - /// Clamps the length of a string filling between min and max characters. - /// If the string is below the minimum, the string is appended with paddingChar up to the minimum length. - /// If the string is over the maximum, the string is truncated at maximum characters; additionally, if the result string ends with - /// whitespace, it is replaced with a paddingChar characters. - /// - public static string ClampLength(this string str, int? min = null, int? max = null, char paddingChar = 'A') + if( max != null && str.Length > max ) { - if( max != null && str.Length > max ) - { - str = str.Substring(0, max.Value).Trim(); - } - if( min != null && min > str.Length ) - { - var missingChars = min - str.Length; - var fillerChars = "".PadRight(missingChars.Value, paddingChar); - return str + fillerChars; - } - return str; + str = str.Substring(0, max.Value).Trim(); } - - /// - /// A string extension method that removes the diacritics character from the strings. - /// - /// The @this to act on. - /// The string without diacritics character. - public static string RemoveDiacritics(this string @this) + if( min != null && min > str.Length ) { - string normalizedString = @this.Normalize(NormalizationForm.FormD); - var sb = new StringBuilder(); + var missingChars = min - str.Length; + var fillerChars = "".PadRight(missingChars.Value, paddingChar); + return str + fillerChars; + } + return str; + } - foreach( char t in normalizedString ) + /// + /// A string extension method that removes the diacritics character from the strings. + /// + /// The @this to act on. + /// The string without diacritics character. + public static string RemoveDiacritics(this string @this) + { + string normalizedString = @this.Normalize(NormalizationForm.FormD); + var sb = new StringBuilder(); + + foreach( char t in normalizedString ) + { + UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t); + if( uc != UnicodeCategory.NonSpacingMark ) { - UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(t); - if( uc != UnicodeCategory.NonSpacingMark ) - { - sb.Append(t); - } + sb.Append(t); } - - return sb.ToString().Normalize(NormalizationForm.FormC); } - /// - /// Transliterates Unicode characters to US-ASCII. For example, Russian cryllic "Анна Фомина" becomes "Anna Fomina". - /// - /// The @this string to act on. - /// The language character set to use. - public static string Transliterate(this string @this, string lang = "en") - { - return Transliterater.Translate(@this, lang); - } + return sb.ToString().Normalize(NormalizationForm.FormC); + } + + /// + /// Transliterates Unicode characters to US-ASCII. For example, Russian cryllic "Анна Фомина" becomes "Anna Fomina". + /// + /// The @this string to act on. + /// The language character set to use. + public static string Transliterate(this string @this, string lang = "en") + { + return Transliterater.Translate(@this, lang); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Extras/CheckDigitExtension.cs b/Source/Bogus/Extensions/Extras/CheckDigitExtension.cs index 8231467d..1b432add 100644 --- a/Source/Bogus/Extensions/Extras/CheckDigitExtension.cs +++ b/Source/Bogus/Extensions/Extras/CheckDigitExtension.cs @@ -1,88 +1,87 @@ using System.Collections.Generic; using System.Linq; -namespace Bogus.Extensions.Extras +namespace Bogus.Extensions.Extras; + +/// +/// Shamelessly copied (and modified) from here: +/// https://stackoverflow.com/questions/21249670/implementing-luhn-algorithm-using-c-sharp +/// +public static class CheckDigitExtension { + static readonly int[] Results = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; + /// - /// Shamelessly copied (and modified) from here: - /// https://stackoverflow.com/questions/21249670/implementing-luhn-algorithm-using-c-sharp + /// For a list of digits, compute the ending checkdigit /// - public static class CheckDigitExtension + /// The list of digits for which to compute the check digit + /// the check digit + public static int CheckDigit(this IList digits) { - static readonly int[] Results = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; - - /// - /// For a list of digits, compute the ending checkdigit - /// - /// The list of digits for which to compute the check digit - /// the check digit - public static int CheckDigit(this IList digits) - { - var i = 0; - var lengthMod = digits.Count % 2; - return (digits.Sum(d => i++ % 2 == lengthMod ? d : Results[d]) * 9) % 10; - } + var i = 0; + var lengthMod = digits.Count % 2; + return (digits.Sum(d => i++ % 2 == lengthMod ? d : Results[d]) * 9) % 10; + } - /// - /// Return a list of digits including the checkdigit - /// - /// The original list of digits - /// the new list of digits including checkdigit - public static IList AppendCheckDigit(this IList digits) - { - var result = digits; - result.Add(digits.CheckDigit()); - return result; - } + /// + /// Return a list of digits including the checkdigit + /// + /// The original list of digits + /// the new list of digits including checkdigit + public static IList AppendCheckDigit(this IList digits) + { + var result = digits; + result.Add(digits.CheckDigit()); + return result; + } - /// - /// Returns true when a list of digits has a valid checkdigit - /// - /// The list of digits to check - /// true/false depending on valid checkdigit - public static bool HasValidCheckDigit(this IList digits) - { - return digits.Last() == CheckDigit(digits.Take(digits.Count - 1).ToList()); - } + /// + /// Returns true when a list of digits has a valid checkdigit + /// + /// The list of digits to check + /// true/false depending on valid checkdigit + public static bool HasValidCheckDigit(this IList digits) + { + return digits.Last() == CheckDigit(digits.Take(digits.Count - 1).ToList()); + } - /// - /// Internal conversion function to convert string into a list of ints - /// - /// the original string - /// the list of ints - private static IList ToDigitList(this string digits) - { - return digits.Select(d => d - 48).ToList(); - } + /// + /// Internal conversion function to convert string into a list of ints + /// + /// the original string + /// the list of ints + private static IList ToDigitList(this string digits) + { + return digits.Select(d => d - 48).ToList(); + } - /// - /// For a string of digits, compute the ending checkdigit - /// - /// The string of digits for which to compute the check digit - /// the check digit - public static string CheckDigit(this string digits) - { - return digits.ToDigitList().CheckDigit().ToString(); - } + /// + /// For a string of digits, compute the ending checkdigit + /// + /// The string of digits for which to compute the check digit + /// the check digit + public static string CheckDigit(this string digits) + { + return digits.ToDigitList().CheckDigit().ToString(); + } - /// - /// Return a string of digits including the checkdigit - /// - /// The original string of digits - /// the new string of digits including checkdigit - public static string AppendCheckDigit(this string digits) - { - return digits + digits.CheckDigit(); - } + /// + /// Return a string of digits including the checkdigit + /// + /// The original string of digits + /// the new string of digits including checkdigit + public static string AppendCheckDigit(this string digits) + { + return digits + digits.CheckDigit(); + } - /// - /// Returns true when a string of digits has a valid checkdigit - /// - /// The string of digits to check - /// true/false depending on valid checkdigit - public static bool HasValidCheckDigit(this string digits) - { - return digits.ToDigitList().HasValidCheckDigit(); - } + /// + /// Returns true when a string of digits has a valid checkdigit + /// + /// The string of digits to check + /// true/false depending on valid checkdigit + public static bool HasValidCheckDigit(this string digits) + { + return digits.ToDigitList().HasValidCheckDigit(); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Extras/FinanceExtensions.cs b/Source/Bogus/Extensions/Extras/FinanceExtensions.cs index f8ee2086..fd30f028 100644 --- a/Source/Bogus/Extensions/Extras/FinanceExtensions.cs +++ b/Source/Bogus/Extensions/Extras/FinanceExtensions.cs @@ -1,26 +1,25 @@ using Bogus.DataSets; -namespace Bogus.Extensions.Extras +namespace Bogus.Extensions.Extras; + +public static class FinanceExtensions { - public static class FinanceExtensions + /// + /// Generate a PCI compliant obfuscated credit card number ****-****-****-1234. + /// + /// The string value to separate the obfuscated credit card. + public static string CreditCardNumberObfuscated(this Finance f, string separator = "-") { - /// - /// Generate a PCI compliant obfuscated credit card number ****-****-****-1234. - /// - /// The string value to separate the obfuscated credit card. - public static string CreditCardNumberObfuscated(this Finance f, string separator = "-") - { - separator ??= string.Empty; + separator ??= string.Empty; - return f.Random.ReplaceNumbers($"****{separator}****{separator}****{separator}####"); - } + return f.Random.ReplaceNumbers($"****{separator}****{separator}****{separator}####"); + } - /// - /// Generates the last four digits for a credit card. - /// - public static string CreditCardNumberLastFourDigits(this Finance f) - { - return f.Random.ReplaceNumbers("####"); - } + /// + /// Generates the last four digits for a credit card. + /// + public static string CreditCardNumberLastFourDigits(this Finance f) + { + return f.Random.ReplaceNumbers("####"); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Finland/ExtensionsForFinland.cs b/Source/Bogus/Extensions/Finland/ExtensionsForFinland.cs index 534fa5a2..cf9aa5ba 100644 --- a/Source/Bogus/Extensions/Finland/ExtensionsForFinland.cs +++ b/Source/Bogus/Extensions/Finland/ExtensionsForFinland.cs @@ -1,60 +1,59 @@ -namespace Bogus.Extensions.Finland +namespace Bogus.Extensions.Finland; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForFinland { /// - /// API extensions specific for a geographical location. + /// Finnish Henkilötunnus /// - public static class ExtensionsForFinland + public static string Henkilötunnus(this Person p) { - /// - /// Finnish Henkilötunnus - /// - public static string Henkilötunnus(this Person p) + const string Key = nameof(ExtensionsForFinland) + "Henkilötunnus"; + if( p.context.ContainsKey(Key) ) { - const string Key = nameof(ExtensionsForFinland) + "Henkilötunnus"; - if( p.context.ContainsKey(Key) ) - { - return p.context[Key] as string; - } - - // DDMMYYCZZZQ - // - // DD - // MM - // YY - DOB - // C - Century - // ZZZ odd for males, even for females - // Q = The control character is calculated as the remainder of DDMMYYZZZ - // divided by 31, i.e. drop the century sign and divide the resulting nine - // digit number by 31. For remainders below ten, the remainder itself is - // the control character, otherwise pick the corresponding character from - // string "0123456789ABCDEFHJKLMNPRSTUVWXY". For example, 311280888 divided by 31 - // gives the remainder as 30, and since A=10, B=11, etc. ending up with Y=30. - - var r = p.Random; - - var year = p.DateOfBirth.Year; - - var c = "A"; - if( year >= 1800 && year <= 1899 ) - c = "+"; - else if( year >= 1900 && year <= 1999 ) - c = "-"; - - var ddMMyy = $"{p.DateOfBirth:ddMMyy}"; - - var n = int.Parse(ddMMyy) % 31; - - var q = n.ToString(); - if( n >= 10 ) - q = ((char)('A' + (n - 10))).ToString(); - - //no idea if its female or male. - var zzz = r.Replace("###"); - - var final = $"{ddMMyy}{c}{zzz}{q}"; - - p.context[Key] = final; - return final; + return p.context[Key] as string; } + + // DDMMYYCZZZQ + // + // DD + // MM + // YY - DOB + // C - Century + // ZZZ odd for males, even for females + // Q = The control character is calculated as the remainder of DDMMYYZZZ + // divided by 31, i.e. drop the century sign and divide the resulting nine + // digit number by 31. For remainders below ten, the remainder itself is + // the control character, otherwise pick the corresponding character from + // string "0123456789ABCDEFHJKLMNPRSTUVWXY". For example, 311280888 divided by 31 + // gives the remainder as 30, and since A=10, B=11, etc. ending up with Y=30. + + var r = p.Random; + + var year = p.DateOfBirth.Year; + + var c = "A"; + if( year >= 1800 && year <= 1899 ) + c = "+"; + else if( year >= 1900 && year <= 1999 ) + c = "-"; + + var ddMMyy = $"{p.DateOfBirth:ddMMyy}"; + + var n = int.Parse(ddMMyy) % 31; + + var q = n.ToString(); + if( n >= 10 ) + q = ((char)('A' + (n - 10))).ToString(); + + //no idea if its female or male. + var zzz = r.Replace("###"); + + var final = $"{ddMMyy}{c}{zzz}{q}"; + + p.context[Key] = final; + return final; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Italy/ExtensionsForItaly.CodiceFiscale.cs b/Source/Bogus/Extensions/Italy/ExtensionsForItaly.CodiceFiscale.cs index a24eea9f..c6e01c29 100644 --- a/Source/Bogus/Extensions/Italy/ExtensionsForItaly.CodiceFiscale.cs +++ b/Source/Bogus/Extensions/Italy/ExtensionsForItaly.CodiceFiscale.cs @@ -4,252 +4,251 @@ using System.Text; using System.Text.RegularExpressions; -namespace Bogus.Extensions.Italy +namespace Bogus.Extensions.Italy; + +/// +/// This class implements the principal routines of the Italian fiscal code, +/// used to unambiguously identify individuals residing in Italy. +/// +/// +internal static class CodiceFiscaleGenerator { /// - /// This class implements the principal routines of the Italian fiscal code, - /// used to unambiguously identify individuals residing in Italy. - /// + /// Map used by the algorithm for even characters /// - internal static class CodiceFiscaleGenerator + private static readonly Dictionary evenMap = new Dictionary { - /// - /// Map used by the algorithm for even characters - /// - private static readonly Dictionary evenMap = new Dictionary - { - { '0', 0 }, - { '1', 1 }, - { '2', 2 }, - { '3', 3 }, - { '4', 4 }, - { '5', 5 }, - { '6', 6 }, - { '7', 7 }, - { '8', 8 }, - { '9', 9 }, - { 'A', 0 }, - { 'B', 1 }, - { 'C', 2 }, - { 'D', 3 }, - { 'E', 4 }, - { 'F', 5 }, - { 'G', 6 }, - { 'H', 7 }, - { 'I', 8 }, - { 'J', 9 }, - { 'K', 10 }, - { 'L', 11 }, - { 'M', 12 }, - { 'N', 13 }, - { 'O', 14 }, - { 'P', 15 }, - { 'Q', 16 }, - { 'R', 17 }, - { 'S', 18 }, - { 'T', 19 }, - { 'U', 20 }, - { 'V', 21 }, - { 'W', 22 }, - { 'X', 23 }, - { 'Y', 24 }, - { 'Z', 25 } - }; - - /// - /// Array that maps months onto letters - /// - private static readonly char[] monthChars = { 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' }; - - /// - /// Map used by the algorithm for odd characters - /// - private static readonly Dictionary oddMap = new Dictionary - { - { '0', 1 }, - { '1', 0 }, - { '2', 5 }, - { '3', 7 }, - { '4', 9 }, - { '5', 13 }, - { '6', 15 }, - { '7', 17 }, - { '8', 19 }, - { '9', 21 }, - { 'A', 1 }, - { 'B', 0 }, - { 'C', 5 }, - { 'D', 7 }, - { 'E', 9 }, - { 'F', 13 }, - { 'G', 15 }, - { 'H', 17 }, - { 'I', 19 }, - { 'J', 21 }, - { 'K', 2 }, - { 'L', 4 }, - { 'M', 18 }, - { 'N', 20 }, - { 'O', 11 }, - { 'P', 3 }, - { 'Q', 6 }, - { 'R', 8 }, - { 'S', 12 }, - { 'T', 14 }, - { 'U', 16 }, - { 'V', 10 }, - { 'W', 22 }, - { 'X', 25 }, - { 'Y', 24 }, - { 'Z', 23 } - }; - - /// - /// Generates an Italian Fiscal Code - /// - /// Last name of the holder - /// First name of the holder - /// Birthday of the holder - /// Indicates whether the holder is male - /// - /// Indicates whether the generated Fiscal Code has a valid checksum or not - /// - /// The generated Fiscal Code - public static string Generate( - string lastName, - string firstName, - DateTime birthday, - bool male, - bool validChecksum = true) + { '0', 0 }, + { '1', 1 }, + { '2', 2 }, + { '3', 3 }, + { '4', 4 }, + { '5', 5 }, + { '6', 6 }, + { '7', 7 }, + { '8', 8 }, + { '9', 9 }, + { 'A', 0 }, + { 'B', 1 }, + { 'C', 2 }, + { 'D', 3 }, + { 'E', 4 }, + { 'F', 5 }, + { 'G', 6 }, + { 'H', 7 }, + { 'I', 8 }, + { 'J', 9 }, + { 'K', 10 }, + { 'L', 11 }, + { 'M', 12 }, + { 'N', 13 }, + { 'O', 14 }, + { 'P', 15 }, + { 'Q', 16 }, + { 'R', 17 }, + { 'S', 18 }, + { 'T', 19 }, + { 'U', 20 }, + { 'V', 21 }, + { 'W', 22 }, + { 'X', 23 }, + { 'Y', 24 }, + { 'Z', 25 } + }; + + /// + /// Array that maps months onto letters + /// + private static readonly char[] monthChars = { 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' }; + + /// + /// Map used by the algorithm for odd characters + /// + private static readonly Dictionary oddMap = new Dictionary + { + { '0', 1 }, + { '1', 0 }, + { '2', 5 }, + { '3', 7 }, + { '4', 9 }, + { '5', 13 }, + { '6', 15 }, + { '7', 17 }, + { '8', 19 }, + { '9', 21 }, + { 'A', 1 }, + { 'B', 0 }, + { 'C', 5 }, + { 'D', 7 }, + { 'E', 9 }, + { 'F', 13 }, + { 'G', 15 }, + { 'H', 17 }, + { 'I', 19 }, + { 'J', 21 }, + { 'K', 2 }, + { 'L', 4 }, + { 'M', 18 }, + { 'N', 20 }, + { 'O', 11 }, + { 'P', 3 }, + { 'Q', 6 }, + { 'R', 8 }, + { 'S', 12 }, + { 'T', 14 }, + { 'U', 16 }, + { 'V', 10 }, + { 'W', 22 }, + { 'X', 25 }, + { 'Y', 24 }, + { 'Z', 23 } + }; + + /// + /// Generates an Italian Fiscal Code + /// + /// Last name of the holder + /// First name of the holder + /// Birthday of the holder + /// Indicates whether the holder is male + /// + /// Indicates whether the generated Fiscal Code has a valid checksum or not + /// + /// The generated Fiscal Code + public static string Generate( + string lastName, + string firstName, + DateTime birthday, + bool male, + bool validChecksum = true) + { + var sb = new StringBuilder(); + sb.Append(GetFiscalCodeSqueezedName(lastName, false)); + sb.Append(GetFiscalCodeSqueezedName(firstName, true)); + sb.Append((birthday.Year % 100).ToString("00")); + sb.Append(monthChars[birthday.Month - 1]); + sb.Append((birthday.Day + (male ? 0 : 40)).ToString("00")); + + //To guarantee code stability for a person, we generate + //fake city-of-birth code through surname and birth date. + //Using actual city code database would be too heavy. + sb.Append(lastName[0].ToString().ToUpper()); + var birthDatePositiveHash = Math.Abs(birthday.GetHashCode()); + sb.Append((birthDatePositiveHash % 1000).ToString("000")); + + var checksum = ComputeChecksumCodiceFiscale(sb.ToString(), validChecksum); + sb.Append(checksum); + + return sb.ToString(); + } + + /// + /// Checksum computation algorithm + /// + /// The code + /// Indicates whether the computed checksum must be valid or not + private static char ComputeChecksumCodiceFiscale(string prefix, bool validChecksum) + { + int total = 0; + for( int i = 0; i < 15; i += 2 ) { - var sb = new StringBuilder(); - sb.Append(GetFiscalCodeSqueezedName(lastName, false)); - sb.Append(GetFiscalCodeSqueezedName(firstName, true)); - sb.Append((birthday.Year % 100).ToString("00")); - sb.Append(monthChars[birthday.Month - 1]); - sb.Append((birthday.Day + (male ? 0 : 40)).ToString("00")); - - //To guarantee code stability for a person, we generate - //fake city-of-birth code through surname and birth date. - //Using actual city code database would be too heavy. - sb.Append(lastName[0].ToString().ToUpper()); - var birthDatePositiveHash = Math.Abs(birthday.GetHashCode()); - sb.Append((birthDatePositiveHash % 1000).ToString("000")); - - var checksum = ComputeChecksumCodiceFiscale(sb.ToString(), validChecksum); - sb.Append(checksum); - - return sb.ToString(); + total += oddMap[prefix[i]]; } - /// - /// Checksum computation algorithm - /// - /// The code - /// Indicates whether the computed checksum must be valid or not - private static char ComputeChecksumCodiceFiscale(string prefix, bool validChecksum) + for( int i = 1; i < 15; i += 2 ) { - int total = 0; - for( int i = 0; i < 15; i += 2 ) - { - total += oddMap[prefix[i]]; - } - - for( int i = 1; i < 15; i += 2 ) - { - total += evenMap[prefix[i]]; - } - - if( !validChecksum ) - { - total++; // set wrong checksum - } + total += evenMap[prefix[i]]; + } - return (char)('A' + (total % 26)); + if( !validChecksum ) + { + total++; // set wrong checksum } - /// - /// This method applies the rule giving the consonants and vowels extracted by the name, - /// according to the algorithm. - /// - /// The name to process - /// true, in case of first names - /// The squeezed name - private static string GetFiscalCodeSqueezedName(string name, bool isFirstName) + return (char)('A' + (total % 26)); + } + + /// + /// This method applies the rule giving the consonants and vowels extracted by the name, + /// according to the algorithm. + /// + /// The name to process + /// true, in case of first names + /// The squeezed name + private static string GetFiscalCodeSqueezedName(string name, bool isFirstName) + { + var sb = new StringBuilder(); + var normalizedName = name.ToUpperInvariant(); + var regex = new Regex("[^A-Z]"); + normalizedName = regex.Replace(normalizedName, string.Empty); + + // manages first name special case (first names having more than 3 consonants -> the 2nd + // is skipped) + var consonantToSkipIdx = -1; + if( isFirstName ) { - var sb = new StringBuilder(); - var normalizedName = name.ToUpperInvariant(); - var regex = new Regex("[^A-Z]"); - normalizedName = regex.Replace(normalizedName, string.Empty); - - // manages first name special case (first names having more than 3 consonants -> the 2nd - // is skipped) - var consonantToSkipIdx = -1; - if( isFirstName ) + var consonantCount = 0; + for( int i = 0; i < normalizedName.Length; i++ ) { - var consonantCount = 0; - for( int i = 0; i < normalizedName.Length; i++ ) + if( !IsVowel(normalizedName[i]) ) { - if( !IsVowel(normalizedName[i]) ) + consonantCount++; + if( consonantCount == 2 ) { - consonantCount++; - if( consonantCount == 2 ) - { - consonantToSkipIdx = i; - } + consonantToSkipIdx = i; } } - - if( consonantCount <= 3 ) - { - consonantToSkipIdx = -1; - } } - // add consonants - for( int i = 0; i < normalizedName.Length; i++ ) + if( consonantCount <= 3 ) { - if( !IsVowel(normalizedName[i]) && (i != consonantToSkipIdx) ) - { - sb.Append(normalizedName[i]); - if( sb.Length == 3 ) - { - return sb.ToString(); - } - } + consonantToSkipIdx = -1; } + } - // add vowels - for( int i = 0; i < normalizedName.Length; i++ ) + // add consonants + for( int i = 0; i < normalizedName.Length; i++ ) + { + if( !IsVowel(normalizedName[i]) && (i != consonantToSkipIdx) ) { - if( IsVowel(normalizedName[i]) ) + sb.Append(normalizedName[i]); + if( sb.Length == 3 ) { - sb.Append(normalizedName[i]); - if( sb.Length == 3 ) - { - return sb.ToString(); - } + return sb.ToString(); } } + } - // add padding X - while( sb.Length < 3 ) + // add vowels + for( int i = 0; i < normalizedName.Length; i++ ) + { + if( IsVowel(normalizedName[i]) ) { - sb.Append("X"); + sb.Append(normalizedName[i]); + if( sb.Length == 3 ) + { + return sb.ToString(); + } } - - return sb.ToString(); } - /// - /// Indicates whether a char is a vowel - /// - /// The char to test - /// True if is is a vowel, false otherwise - private static bool IsVowel(char c) + // add padding X + while( sb.Length < 3 ) { - var vowels = new[] { 'A', 'E', 'I', 'O', 'U' }; - return vowels.Contains(c); + sb.Append("X"); } + + return sb.ToString(); + } + + /// + /// Indicates whether a char is a vowel + /// + /// The char to test + /// True if is is a vowel, false otherwise + private static bool IsVowel(char c) + { + var vowels = new[] { 'A', 'E', 'I', 'O', 'U' }; + return vowels.Contains(c); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Italy/ExtensionsForItaly.cs b/Source/Bogus/Extensions/Italy/ExtensionsForItaly.cs index cfec755c..0faa9fa9 100644 --- a/Source/Bogus/Extensions/Italy/ExtensionsForItaly.cs +++ b/Source/Bogus/Extensions/Italy/ExtensionsForItaly.cs @@ -1,57 +1,56 @@ using Bogus.DataSets; using System; -namespace Bogus.Extensions.Italy +namespace Bogus.Extensions.Italy; + +/// +/// Italian class extensions +/// +public static class ExtensionsForItaly { /// - /// Italian class extensions + /// Codice Fiscale /// - public static class ExtensionsForItaly + /// The holder + /// + /// Indicates whether the generated Fiscal Code has a valid checksum or not + /// + /// The generated Fiscal Code + public static string CodiceFiscale(this Person p, bool validChecksum = true) { - /// - /// Codice Fiscale - /// - /// The holder - /// - /// Indicates whether the generated Fiscal Code has a valid checksum or not - /// - /// The generated Fiscal Code - public static string CodiceFiscale(this Person p, bool validChecksum = true) - { - return CodiceFiscaleGenerator.Generate( - p.LastName, - p.FirstName, - p.DateOfBirth, - p.Gender == Name.Gender.Male, - validChecksum); - } + return CodiceFiscaleGenerator.Generate( + p.LastName, + p.FirstName, + p.DateOfBirth, + p.Gender == Name.Gender.Male, + validChecksum); + } - /// - /// Codice Fiscale - /// - /// An instance of the extended Finance class - /// Last name of the holder - /// First name of the holder - /// Birthday of the holder - /// Indicates whether the holder is male - /// - /// Indicates whether the generated Fiscal Code has a valid checksum or not - /// - /// The generated Fiscal Code - public static string CodiceFiscale( - this Finance finance, - string lastName, - string firstName, - DateTime birthday, - bool isMale, - bool validChecksum = true) - { - return CodiceFiscaleGenerator.Generate( - lastName, - firstName, - birthday, - isMale, - validChecksum); - } + /// + /// Codice Fiscale + /// + /// An instance of the extended Finance class + /// Last name of the holder + /// First name of the holder + /// Birthday of the holder + /// Indicates whether the holder is male + /// + /// Indicates whether the generated Fiscal Code has a valid checksum or not + /// + /// The generated Fiscal Code + public static string CodiceFiscale( + this Finance finance, + string lastName, + string firstName, + DateTime birthday, + bool isMale, + bool validChecksum = true) + { + return CodiceFiscaleGenerator.Generate( + lastName, + firstName, + birthday, + isMale, + validChecksum); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs index c7e8ae69..cd30e304 100644 --- a/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs +++ b/Source/Bogus/Extensions/Norway/ExtensionsForNorway.cs @@ -1,125 +1,124 @@ using System; -namespace Bogus.Extensions.Norway +namespace Bogus.Extensions.Norway; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForNorway { /// - /// API extensions specific for a geographical location. + /// Norwegian national identity number /// - public static class ExtensionsForNorway + public static string Fødselsnummer(this Person p) { - /// - /// Norwegian national identity number - /// - public static string Fødselsnummer(this Person p) + const string Key = nameof(ExtensionsForNorway) + "Fødselsnummer"; + if (p.context.ContainsKey(Key)) { - const string Key = nameof(ExtensionsForNorway) + "Fødselsnummer"; - if (p.context.ContainsKey(Key)) - { - return p.context[Key] as string; - } + return p.context[Key] as string; + } + + /* + DDMMYYXXXCC + | | | | |--> Checksum + | | | | + | | | | + | | | |-----> Individual number + | | |-------> Year (last two digits) + | |---------> Month + |-----------> Day - /* - DDMMYYXXXCC - | | | | |--> Checksum - | | | | - | | | | - | | | |-----> Individual number - | | |-------> Year (last two digits) - | |---------> Month - |-----------> Day + The individual number has to be even for women and odd for men. - The individual number has to be even for women and odd for men. + The checksum is calculated with a modulo checksum algorithm. + If either of the checksum numbers are 10, the fødselsnummer gets + rejected, and a new individual number has to be generated. - The checksum is calculated with a modulo checksum algorithm. - If either of the checksum numbers are 10, the fødselsnummer gets - rejected, and a new individual number has to be generated. + https://www.skatteetaten.no/en/person/national-registry/birth-and-name-selection/children-born-in-norway/national-id-number/ - https://www.skatteetaten.no/en/person/national-registry/birth-and-name-selection/children-born-in-norway/national-id-number/ + https://nn.wikipedia.org/wiki/F%C3%B8dselsnummer - https://nn.wikipedia.org/wiki/F%C3%B8dselsnummer + https://github.com/deegane/NINTool/blob/master/backend/src/main/java/com/nin/validation/NorwegianNinValidator.kt - https://github.com/deegane/NINTool/blob/master/backend/src/main/java/com/nin/validation/NorwegianNinValidator.kt + https://github.com/magnuswatn/fodselsnummer/blob/master/fodselsnummer.py + */ - https://github.com/magnuswatn/fodselsnummer/blob/master/fodselsnummer.py - */ + var r = p.Random; + string birthDate = $"{p.DateOfBirth:ddMMyy}"; - var r = p.Random; - string birthDate = $"{p.DateOfBirth:ddMMyy}"; + string individualNumber; + string checksum; + bool isOkChecksum; + + do + { + individualNumber = GenerateIndividualNumber(r, p.Gender, p.DateOfBirth.Year); + isOkChecksum = GenerateChecksum(birthDate, individualNumber, out checksum); + } while (!isOkChecksum); - string individualNumber; - string checksum; - bool isOkChecksum; + string final = $"{p.DateOfBirth:ddMMyy}{individualNumber}{checksum}"; - do - { - individualNumber = GenerateIndividualNumber(r, p.Gender, p.DateOfBirth.Year); - isOkChecksum = GenerateChecksum(birthDate, individualNumber, out checksum); - } while (!isOkChecksum); + p.context[Key] = final; + return final; + } - string final = $"{p.DateOfBirth:ddMMyy}{individualNumber}{checksum}"; + private static string GenerateIndividualNumber(Randomizer r, DataSets.Name.Gender gender, int year) + { + int from; + int to; - p.context[Key] = final; - return final; + if (1854 <= year && year <= 1899) + { + from = 500; + to = 749; + } + else if (1900 <= year && year <= 1999) + { + from = 0; + to = 499; } + else if (2000 <= year && year <= 2039) + { + from = 500; + to = 999; + } + else + { + throw new ArgumentOutOfRangeException(nameof(year), $"{nameof(year)} must be between 1854 and 2039."); + } + + int individualNumber = gender == DataSets.Name.Gender.Female ? r.Even(from, to) : r.Odd(from, to); - private static string GenerateIndividualNumber(Randomizer r, DataSets.Name.Gender gender, int year) + return individualNumber.ToString("D3"); + } + + private static bool GenerateChecksum(string birthDate, string individualNumber, out string checksum) + { + int d1 = int.Parse(birthDate.Substring(0, 1)); + int d2 = int.Parse(birthDate.Substring(1, 1)); + int m1 = int.Parse(birthDate.Substring(2, 1)); + int m2 = int.Parse(birthDate.Substring(3, 1)); + int y1 = int.Parse(birthDate.Substring(4, 1)); + int y2 = int.Parse(birthDate.Substring(5, 1)); + int i1 = int.Parse(individualNumber.Substring(0, 1)); + int i2 = int.Parse(individualNumber.Substring(1, 1)); + int i3 = int.Parse(individualNumber.Substring(2, 1)); + + int cs1 = 11 - (((3 * d1) + (7 * d2) + (6 * m1) + (1 * m2) + (8 * y1) + (9 * y2) + (4 * i1) + (5 * i2) + (2 * i3)) % 11); + int cs2 = 11 - (((5 * d1) + (4 * d2) + (3 * m1) + (2 * m2) + (7 * y1) + (6 * y2) + (5 * i1) + (4 * i2) + (3 * i3) + (2 * cs1)) % 11); + + if (cs1 == 11) { - int from; - int to; - - if (1854 <= year && year <= 1899) - { - from = 500; - to = 749; - } - else if (1900 <= year && year <= 1999) - { - from = 0; - to = 499; - } - else if (2000 <= year && year <= 2039) - { - from = 500; - to = 999; - } - else - { - throw new ArgumentOutOfRangeException(nameof(year), $"{nameof(year)} must be between 1854 and 2039."); - } - - int individualNumber = gender == DataSets.Name.Gender.Female ? r.Even(from, to) : r.Odd(from, to); - - return individualNumber.ToString("D3"); + cs1 = 0; } - private static bool GenerateChecksum(string birthDate, string individualNumber, out string checksum) + if (cs2 == 11) { - int d1 = int.Parse(birthDate.Substring(0, 1)); - int d2 = int.Parse(birthDate.Substring(1, 1)); - int m1 = int.Parse(birthDate.Substring(2, 1)); - int m2 = int.Parse(birthDate.Substring(3, 1)); - int y1 = int.Parse(birthDate.Substring(4, 1)); - int y2 = int.Parse(birthDate.Substring(5, 1)); - int i1 = int.Parse(individualNumber.Substring(0, 1)); - int i2 = int.Parse(individualNumber.Substring(1, 1)); - int i3 = int.Parse(individualNumber.Substring(2, 1)); - - int cs1 = 11 - (((3 * d1) + (7 * d2) + (6 * m1) + (1 * m2) + (8 * y1) + (9 * y2) + (4 * i1) + (5 * i2) + (2 * i3)) % 11); - int cs2 = 11 - (((5 * d1) + (4 * d2) + (3 * m1) + (2 * m2) + (7 * y1) + (6 * y2) + (5 * i1) + (4 * i2) + (3 * i3) + (2 * cs1)) % 11); - - if (cs1 == 11) - { - cs1 = 0; - } - - if (cs2 == 11) - { - cs2 = 0; - } - - checksum = $"{cs1}{cs2}"; - - return cs1 < 10 && cs2 < 10; + cs2 = 0; } + + checksum = $"{cs1}{cs2}"; + + return cs1 < 10 && cs2 < 10; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Portugal/ExtensionsForPortugal.cs b/Source/Bogus/Extensions/Portugal/ExtensionsForPortugal.cs index 95ad0e99..f99b683a 100644 --- a/Source/Bogus/Extensions/Portugal/ExtensionsForPortugal.cs +++ b/Source/Bogus/Extensions/Portugal/ExtensionsForPortugal.cs @@ -1,55 +1,54 @@ using Bogus.DataSets; using System.Linq; -namespace Bogus.Extensions.Portugal +namespace Bogus.Extensions.Portugal; + +/// +/// API extensions specific for Portugal. +/// +public static class ExtensionsForPortugal { /// - /// API extensions specific for Portugal. + /// Número de Identificação Fiscal (NIF) /// - public static class ExtensionsForPortugal + /// + /// Tax identification number. Also referred to as Taxpayer Number, identifies a tax entity that is a taxpayer in Portugal, whether a company or a natural person. + /// + /// Object will receive the NIF value + public static string Nif(this Person p) { - /// - /// Número de Identificação Fiscal (NIF) - /// - /// - /// Tax identification number. Also referred to as Taxpayer Number, identifies a tax entity that is a taxpayer in Portugal, whether a company or a natural person. - /// - /// Object will receive the NIF value - public static string Nif(this Person p) + const string Key = nameof(ExtensionsForPortugal) + "NIF"; + if (p.context.ContainsKey(Key)) { - const string Key = nameof(ExtensionsForPortugal) + "NIF"; - if (p.context.ContainsKey(Key)) - { - return p.context[Key] as string; - } + return p.context[Key] as string; + } - var id = new[] {p.Random.ArrayElement(TaxNumberGenerator.NifIdentify)}; - var digits = p.Random.Digits(7); + var id = new[] {p.Random.ArrayElement(TaxNumberGenerator.NifIdentify)}; + var digits = p.Random.Digits(7); - var nifNumber = id.Concat(digits).ToArray(); + var nifNumber = id.Concat(digits).ToArray(); - var final = TaxNumberGenerator.Create(nifNumber); + var final = TaxNumberGenerator.Create(nifNumber); - p.context[Key] = final; + p.context[Key] = final; - return final; - } + return final; + } - /// - /// Número de Identificação de Pessoa Colectiva (NIPC) - /// - /// - /// Tax identification number for companies. A Collective Identification Number is the most correct term to refer to a company's NIF. The first digit can be 5, 6 public collective, 8, irregular legal person or provisional number. - /// - /// Object will receive the NIPC value - public static string Nipc(this Company c) - { - var id = new[] {c.Random.ArrayElement(TaxNumberGenerator.NipcIdentify)}; - var digits = c.Random.Digits(7); + /// + /// Número de Identificação de Pessoa Colectiva (NIPC) + /// + /// + /// Tax identification number for companies. A Collective Identification Number is the most correct term to refer to a company's NIF. The first digit can be 5, 6 public collective, 8, irregular legal person or provisional number. + /// + /// Object will receive the NIPC value + public static string Nipc(this Company c) + { + var id = new[] {c.Random.ArrayElement(TaxNumberGenerator.NipcIdentify)}; + var digits = c.Random.Digits(7); - var nipcNumber = id.Concat(digits).ToArray(); + var nipcNumber = id.Concat(digits).ToArray(); - return TaxNumberGenerator.Create(nipcNumber); - } + return TaxNumberGenerator.Create(nipcNumber); } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Portugal/TaxNumberGenerator.cs b/Source/Bogus/Extensions/Portugal/TaxNumberGenerator.cs index 41408359..19788778 100644 --- a/Source/Bogus/Extensions/Portugal/TaxNumberGenerator.cs +++ b/Source/Bogus/Extensions/Portugal/TaxNumberGenerator.cs @@ -1,33 +1,32 @@ using System.Linq; -namespace Bogus.Extensions.Portugal +namespace Bogus.Extensions.Portugal; + +internal static class TaxNumberGenerator { - internal static class TaxNumberGenerator - { - public static readonly int[] NifIdentify = { 1, 2 }; - public static readonly int[] NipcIdentify = { 5, 6, 8, 9 }; - private static readonly int[] Weights = { 9, 8, 7, 6, 5, 4, 3, 2 }; + public static readonly int[] NifIdentify = { 1, 2 }; + public static readonly int[] NipcIdentify = { 5, 6, 8, 9 }; + private static readonly int[] Weights = { 9, 8, 7, 6, 5, 4, 3, 2 }; - /// - /// Rules for generate the last number for the combination - /// - /// The array number for calculate - public static string Create(int[] arrNumber) - { - var sum1 = arrNumber.Zip(Weights, (d, w) => d * w) - .Sum(); + /// + /// Rules for generate the last number for the combination + /// + /// The array number for calculate + public static string Create(int[] arrNumber) + { + var sum1 = arrNumber.Zip(Weights, (d, w) => d * w) + .Sum(); - var sum1mod = sum1 % 11; + var sum1mod = sum1 % 11; - var check1 = 0; - if (sum1mod >= 2) - { - check1 = 11 - sum1mod; - } + var check1 = 0; + if (sum1mod >= 2) + { + check1 = 11 - sum1mod; + } - var all = arrNumber.Concat(new[] { check1 }).ToArray(); + var all = arrNumber.Concat(new[] { check1 }).ToArray(); - return $"{all[0]}{all[1]}{all[2]}{all[3]}{all[4]}{all[5]}{all[6]}{all[7]}{all[8]}"; - } + return $"{all[0]}{all[1]}{all[2]}{all[3]}{all[4]}{all[5]}{all[6]}{all[7]}{all[8]}"; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/Sweden/ExtensionsForSweden.cs b/Source/Bogus/Extensions/Sweden/ExtensionsForSweden.cs index d9eac4a0..1f34e948 100644 --- a/Source/Bogus/Extensions/Sweden/ExtensionsForSweden.cs +++ b/Source/Bogus/Extensions/Sweden/ExtensionsForSweden.cs @@ -3,89 +3,88 @@ using Bogus.DataSets; using static Bogus.DataSets.Name; -namespace Bogus.Extensions.Sweden +namespace Bogus.Extensions.Sweden; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForSweden { /// - /// API extensions specific for a geographical location. + /// Swedish national identity number /// - public static class ExtensionsForSweden + public static string Personnummer(this Person person) { - /// - /// Swedish national identity number - /// - public static string Personnummer(this Person person) + const string key = nameof(ExtensionsForSweden) + nameof(Personnummer); + if (person.context.ContainsKey(key)) { - const string key = nameof(ExtensionsForSweden) + nameof(Personnummer); - if (person.context.ContainsKey(key)) - { - return person.context[key] as string; - } + return person.context[key] as string; + } - /* + /* - YYYYMMDDBBGC - | | | | ||---> (C)Checksum - | | | | | - | | | | |----> (G)Gender - | | | |------> (B)Birthplace - | | |--------> (D)day - | |----------> (M)Month - |--------------> (Y)Year - Source: https://syntaxwarriors.com/p/1021/Generate-a-Random-Swedish-Personnumber-with-control-digit - - info: https://skatteverket.se/privat/folkbokforing/personnummerochsamordningsnummer.4.3810a01c150939e893f18c29.html - */ - var r = person.Random; - var individualNumber = GenerateIndividualNumber(r, person.Gender, person.DateOfBirth); + YYYYMMDDBBGC + | | | | ||---> (C)Checksum + | | | | | + | | | | |----> (G)Gender + | | | |------> (B)Birthplace + | | |--------> (D)day + | |----------> (M)Month + |--------------> (Y)Year + Source: https://syntaxwarriors.com/p/1021/Generate-a-Random-Swedish-Personnumber-with-control-digit + + info: https://skatteverket.se/privat/folkbokforing/personnummerochsamordningsnummer.4.3810a01c150939e893f18c29.html + */ + var r = person.Random; + var individualNumber = GenerateIndividualNumber(r, person.Gender, person.DateOfBirth); - person.context[key] = individualNumber; - return individualNumber; - } + person.context[key] = individualNumber; + return individualNumber; + } - private static string GenerateIndividualNumber(Randomizer r, Gender gender, DateTime dateOfBirth) - { - var genderNumber = GetGenderNumber(r, gender); - var p = dateOfBirth.ToString("yyyyMMddff") + genderNumber; - var checksum = GetLuhn(p.Substring(2)); - return p + checksum; - } + private static string GenerateIndividualNumber(Randomizer r, Gender gender, DateTime dateOfBirth) + { + var genderNumber = GetGenderNumber(r, gender); + var p = dateOfBirth.ToString("yyyyMMddff") + genderNumber; + var checksum = GetLuhn(p.Substring(2)); + return p + checksum; + } - private static int GetGenderNumber(Randomizer r, Gender gender) - { - if( gender is Gender.Male ) - return r.Even(1, 9); - if( gender is Gender.Female ) - return r.Odd(1, 9); - throw new ArgumentOutOfRangeException(nameof(gender), gender, "Gender not handled."); - } + private static int GetGenderNumber(Randomizer r, Gender gender) + { + if( gender is Gender.Male ) + return r.Even(1, 9); + if( gender is Gender.Female ) + return r.Odd(1, 9); + throw new ArgumentOutOfRangeException(nameof(gender), gender, "Gender not handled."); + } - private static int GetLuhn(string value) - { - // Luhn algorithm doubles every other number in the value. - // To get the correct checksum digit we aught to append a 0 on the sequence. - // If the result becomes a two digit number, subtract 9 from the value. - // If the total sum is not a 0, the last checksum value should be subtracted from 10. - // The resulting value is the check value that we use as control number. + private static int GetLuhn(string value) + { + // Luhn algorithm doubles every other number in the value. + // To get the correct checksum digit we aught to append a 0 on the sequence. + // If the result becomes a two digit number, subtract 9 from the value. + // If the total sum is not a 0, the last checksum value should be subtracted from 10. + // The resulting value is the check value that we use as control number. - // The value passed is a string, so we aught to get the actual integer value from each char (i.e., subtract '0' which is 48). - var t = value.ToCharArray().Select(d => d - 48).ToArray(); - var sum = 0; - int temp; - for (var i = 0; i < t.Length; i++) + // The value passed is a string, so we aught to get the actual integer value from each char (i.e., subtract '0' which is 48). + var t = value.ToCharArray().Select(d => d - 48).ToArray(); + var sum = 0; + int temp; + for (var i = 0; i < t.Length; i++) + { + temp = t[i]; + temp *= 2 - (i % 2); + if (temp > 9) { - temp = t[i]; - temp *= 2 - (i % 2); - if (temp > 9) - { - temp -= 9; - } - - sum += temp; + temp -= 9; } - return ((int) Math.Ceiling(sum / 10.0)) * 10 - sum; + sum += temp; } + + return ((int) Math.Ceiling(sum / 10.0)) * 10 - sum; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForGreatBritainRegistrationPlate.cs b/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForGreatBritainRegistrationPlate.cs index 881daf81..824cd9fe 100644 --- a/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForGreatBritainRegistrationPlate.cs +++ b/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForGreatBritainRegistrationPlate.cs @@ -3,153 +3,152 @@ using System.Text; using Bogus.DataSets; -namespace Bogus.Extensions.UnitedKingdom +namespace Bogus.Extensions.UnitedKingdom; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForGreatBritainRegistrationPlate { - /// - /// API extensions specific for a geographical location. - /// - public static class ExtensionsForGreatBritainRegistrationPlate - { - private static readonly DateTime StartOfCurrentStyle = new DateTime(2001, 9, 1); + private static readonly DateTime StartOfCurrentStyle = new DateTime(2001, 9, 1); - private static readonly DateTime EarliestRegistration = StartOfCurrentStyle; - private static readonly DateTime LatestRegistration = new DateTime(2051, 2, 28); + private static readonly DateTime EarliestRegistration = StartOfCurrentStyle; + private static readonly DateTime LatestRegistration = new DateTime(2051, 2, 28); - private static readonly char[] SequenceLetters = "ABCDEFGHJKLMNOPRSTUVWXYZ".ToCharArray(); - private static readonly char[] PrimaryLocations = "ABCDEFGHKLMNOPRSVWXY".ToCharArray(); - private static readonly Dictionary SecondaryLocations = new Dictionary - { - { 'A', "ABCDEFGJKMNOPRSTUVWXY".ToCharArray() }, // Anglia - { 'B', "ABCDEFGHJKLMNOPRSTUVWX".ToCharArray() }, // Birmingham - { 'C', "ABCDEFGHJKLMNOPRSTUVWX".ToCharArray() }, // Cymru (Wales) - { 'D', "ABCDEFGHJKLMNOPSTUVWXY".ToCharArray() }, // Deeside - { 'E', "ABCEFGJKLMNOPRSTUVWXY".ToCharArray() }, // Essex - { 'F', "ABCDEFGHJKLMNPRSTVWXY".ToCharArray() }, // Forest and Fens - { 'G', "ABCDEFGHJKLMNPRSTUVWXY".ToCharArray() }, // Garden of England - { 'H', "ABCDEFGHJKLMNPRSTUVWY".ToCharArray() }, // Hampshire and Dorset - { 'K', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // Milton Keynes (not official mnemonic) - { 'L', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // London - { 'M', "ABCDEFGHJKLMPTUVWX".ToCharArray() }, // Manchester and Merseyside - { 'N', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // North - { 'O', "ABCDEFGHJLMOPTUVWXY".ToCharArray() }, // Oxford - { 'P', "ABCDEFGHJKLMNOPRSTUVWXYZ".ToCharArray() }, // Preston - { 'R', "ABCDEFGHJKLMNOPRSTVWXY".ToCharArray() }, // Reading - { 'S', "ABCDEFGHJKLMNOPRSTVWXY".ToCharArray() }, // Scotland - { 'V', "ABCEFGHJKLMNOPRSTUVXY".ToCharArray() }, // Severn Valley - { 'W', "ABDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // West of England - { 'Y', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // Yorkshire - }; + private static readonly char[] SequenceLetters = "ABCDEFGHJKLMNOPRSTUVWXYZ".ToCharArray(); + private static readonly char[] PrimaryLocations = "ABCDEFGHKLMNOPRSVWXY".ToCharArray(); + private static readonly Dictionary SecondaryLocations = new Dictionary + { + { 'A', "ABCDEFGJKMNOPRSTUVWXY".ToCharArray() }, // Anglia + { 'B', "ABCDEFGHJKLMNOPRSTUVWX".ToCharArray() }, // Birmingham + { 'C', "ABCDEFGHJKLMNOPRSTUVWX".ToCharArray() }, // Cymru (Wales) + { 'D', "ABCDEFGHJKLMNOPSTUVWXY".ToCharArray() }, // Deeside + { 'E', "ABCEFGJKLMNOPRSTUVWXY".ToCharArray() }, // Essex + { 'F', "ABCDEFGHJKLMNPRSTVWXY".ToCharArray() }, // Forest and Fens + { 'G', "ABCDEFGHJKLMNPRSTUVWXY".ToCharArray() }, // Garden of England + { 'H', "ABCDEFGHJKLMNPRSTUVWY".ToCharArray() }, // Hampshire and Dorset + { 'K', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // Milton Keynes (not official mnemonic) + { 'L', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // London + { 'M', "ABCDEFGHJKLMPTUVWX".ToCharArray() }, // Manchester and Merseyside + { 'N', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // North + { 'O', "ABCDEFGHJLMOPTUVWXY".ToCharArray() }, // Oxford + { 'P', "ABCDEFGHJKLMNOPRSTUVWXYZ".ToCharArray() }, // Preston + { 'R', "ABCDEFGHJKLMNOPRSTVWXY".ToCharArray() }, // Reading + { 'S', "ABCDEFGHJKLMNOPRSTVWXY".ToCharArray() }, // Scotland + { 'V', "ABCEFGHJKLMNOPRSTUVXY".ToCharArray() }, // Severn Valley + { 'W', "ABDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // West of England + { 'Y', "ABCDEFGHJKLMNOPRSTUVWXY".ToCharArray() }, // Yorkshire + }; - /// - /// GB Vehicle Registration Plate - /// - /// Object to extend. - /// The start of the range of registration dates. - /// The end of the range of registration dates. - /// A string containing a GB registration plate. - /// - /// This is based on the information in the Wikipedia article on - /// Vehicle registration plates of the United Kingdom. - /// https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_the_United_Kingdom - /// At present this only handles registration plates in the current - /// scheme (September 2001 to February 2051). - /// - public static string GbRegistrationPlate(this Vehicle vehicle, DateTime dateFrom, DateTime dateTo) - { - DateTime registrationDate = GenerateRegistrationDate(vehicle, dateFrom, dateTo); - return GenerateCurrentStylePlates(vehicle, registrationDate); - } + /// + /// GB Vehicle Registration Plate + /// + /// Object to extend. + /// The start of the range of registration dates. + /// The end of the range of registration dates. + /// A string containing a GB registration plate. + /// + /// This is based on the information in the Wikipedia article on + /// Vehicle registration plates of the United Kingdom. + /// https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_the_United_Kingdom + /// At present this only handles registration plates in the current + /// scheme (September 2001 to February 2051). + /// + public static string GbRegistrationPlate(this Vehicle vehicle, DateTime dateFrom, DateTime dateTo) + { + DateTime registrationDate = GenerateRegistrationDate(vehicle, dateFrom, dateTo); + return GenerateCurrentStylePlates(vehicle, registrationDate); + } - private static string GenerateCurrentStylePlates(Vehicle vehicle, DateTime registrationDate) - { - StringBuilder sb = new StringBuilder(); - char primaryLocation = vehicle.Random.ArrayElement(PrimaryLocations); - sb.Append(primaryLocation); + private static string GenerateCurrentStylePlates(Vehicle vehicle, DateTime registrationDate) + { + StringBuilder sb = new StringBuilder(); + char primaryLocation = vehicle.Random.ArrayElement(PrimaryLocations); + sb.Append(primaryLocation); - char secondaryLocation = GetSecondaryLocation(vehicle, primaryLocation, registrationDate); - sb.Append(secondaryLocation); - int year = registrationDate.Year - 2000; - if (registrationDate.Month < 3) - year += 49; - else if (registrationDate.Month >= 9) - year += 50; - sb.Append($"{year:D2}"); + char secondaryLocation = GetSecondaryLocation(vehicle, primaryLocation, registrationDate); + sb.Append(secondaryLocation); + int year = registrationDate.Year - 2000; + if (registrationDate.Month < 3) + year += 49; + else if (registrationDate.Month >= 9) + year += 50; + sb.Append($"{year:D2}"); - ApplySN07Exception(sb); + ApplySN07Exception(sb); - char[] sequence = vehicle.Random.ArrayElements(SequenceLetters, 3); - sb.Append(sequence); - return sb.ToString(); - } + char[] sequence = vehicle.Random.ArrayElements(SequenceLetters, 3); + sb.Append(sequence); + return sb.ToString(); + } - private static void ApplySN07Exception(StringBuilder sb) - { - // The DVLA don't permit plates starting SN07 (i.e. Registered in - // Edinburgh between 1/Mar/2007 and 31/Aug/2007) because it looks - // too much like the word "snot". + private static void ApplySN07Exception(StringBuilder sb) + { + // The DVLA don't permit plates starting SN07 (i.e. Registered in + // Edinburgh between 1/Mar/2007 and 31/Aug/2007) because it looks + // too much like the word "snot". - sb.Replace("SN07", "TN07"); - } + sb.Replace("SN07", "TN07"); + } - private static char GetSecondaryLocation(Vehicle vehicle, char primaryLocation, DateTime registrationDate) + private static char GetSecondaryLocation(Vehicle vehicle, char primaryLocation, DateTime registrationDate) + { + if (primaryLocation == 'X') { - if (primaryLocation == 'X') + // Export vehicles have their secondary location marker based on + // the registration date rather than a specific DVLA office. + switch (registrationDate.Month) { - // Export vehicles have their secondary location marker based on - // the registration date rather than a specific DVLA office. - switch (registrationDate.Month) - { - case 1: - case 7: - return 'E'; - case 2: - case 8: - return 'F'; - case 3: - case 9: - return 'A'; - case 4: - case 10: - return 'B'; - case 5: - case 11: - return 'C'; - case 6: - case 12: - return 'D'; - } - - throw new InvalidOperationException( - $"This code path should never be accessible. {nameof(primaryLocation)}={primaryLocation}; {nameof(registrationDate)}={registrationDate:O}"); + case 1: + case 7: + return 'E'; + case 2: + case 8: + return 'F'; + case 3: + case 9: + return 'A'; + case 4: + case 10: + return 'B'; + case 5: + case 11: + return 'C'; + case 6: + case 12: + return 'D'; } - - char[] secondaryLocationChoices = SecondaryLocations[primaryLocation]; - char secondaryLocation = vehicle.Random.ArrayElement(secondaryLocationChoices); - return secondaryLocation; + + throw new InvalidOperationException( + $"This code path should never be accessible. {nameof(primaryLocation)}={primaryLocation}; {nameof(registrationDate)}={registrationDate:O}"); } + + char[] secondaryLocationChoices = SecondaryLocations[primaryLocation]; + char secondaryLocation = vehicle.Random.ArrayElement(secondaryLocationChoices); + return secondaryLocation; + } - private static DateTime GenerateRegistrationDate(Vehicle vehicle, DateTime dateFrom, DateTime dateTo) + private static DateTime GenerateRegistrationDate(Vehicle vehicle, DateTime dateFrom, DateTime dateTo) + { + if (dateFrom < EarliestRegistration || dateFrom > LatestRegistration) + throw new ArgumentOutOfRangeException(nameof(dateFrom), $"Can only accept registration dates between {EarliestRegistration:yyyy-MM-dd} and {LatestRegistration:yyyy-MM-dd}."); + if (dateTo < EarliestRegistration || dateTo > LatestRegistration) + throw new ArgumentOutOfRangeException(nameof(dateTo),$"Can only accept registration dates between {EarliestRegistration:yyyy-MM-dd} and {LatestRegistration:yyyy-MM-dd}."); + + // Swap the values of the to and from dates if they're the wrong way around. + if (dateFrom > dateTo) { - if (dateFrom < EarliestRegistration || dateFrom > LatestRegistration) - throw new ArgumentOutOfRangeException(nameof(dateFrom), $"Can only accept registration dates between {EarliestRegistration:yyyy-MM-dd} and {LatestRegistration:yyyy-MM-dd}."); - if (dateTo < EarliestRegistration || dateTo > LatestRegistration) - throw new ArgumentOutOfRangeException(nameof(dateTo),$"Can only accept registration dates between {EarliestRegistration:yyyy-MM-dd} and {LatestRegistration:yyyy-MM-dd}."); - - // Swap the values of the to and from dates if they're the wrong way around. - if (dateFrom > dateTo) - { - // Syntax not supported: (dateFrom, dateTo) = (dateTo, dateFrom); - DateTime valueHolder = dateFrom; - dateFrom = dateTo; - dateTo = valueHolder; - } - - dateFrom = dateFrom.Date; - dateTo = dateTo.Date; - int duration = (int) (dateTo - dateFrom).TotalDays; - int offset = vehicle.Random.Int(0, duration); - DateTime registrationDate = dateFrom.AddDays(offset); - return registrationDate; + // Syntax not supported: (dateFrom, dateTo) = (dateTo, dateFrom); + DateTime valueHolder = dateFrom; + dateFrom = dateTo; + dateTo = valueHolder; } + + dateFrom = dateFrom.Date; + dateTo = dateTo.Date; + int duration = (int) (dateTo - dateFrom).TotalDays; + int offset = vehicle.Random.Int(0, duration); + DateTime registrationDate = dateFrom.AddDays(offset); + return registrationDate; } } \ No newline at end of file diff --git a/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForUnitedKingdom.cs b/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForUnitedKingdom.cs index 00d702e5..3a51306a 100644 --- a/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForUnitedKingdom.cs +++ b/Source/Bogus/Extensions/UnitedKingdom/ExtensionsForUnitedKingdom.cs @@ -1,72 +1,71 @@ using Bogus.Bson; using Bogus.DataSets; -namespace Bogus.Extensions.UnitedKingdom +namespace Bogus.Extensions.UnitedKingdom; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForUnitedKingdom { /// - /// API extensions specific for a geographical location. + /// Banking Sort Code /// - public static class ExtensionsForUnitedKingdom + public static string SortCode(this Finance finance, bool includeSeparator = true) { - /// - /// Banking Sort Code - /// - public static string SortCode(this Finance finance, bool includeSeparator = true) - { - const string withSeparator = "##-##-##"; - const string withoutSeparator = "######"; - - if( includeSeparator ) - { - return finance.Random.ReplaceNumbers(withSeparator); - } - - return finance.Random.ReplaceNumbers(withoutSeparator); - } + const string withSeparator = "##-##-##"; + const string withoutSeparator = "######"; - /// - /// National Insurance Number - /// - public static string Nino(this Finance finance, bool includeSeparator = true) + if( includeSeparator ) { - const string valid1stPrefixChars = "ABCEGHJKLMNOPRSTWXYZ"; - //const string valid2ndPrefixChars = "ABCEGHJKLMN PRSTWXYZ"; - const string validSuffixChars = "ABCD"; + return finance.Random.ReplaceNumbers(withSeparator); + } - var prefix = finance.Random.String2(2, chars: valid1stPrefixChars); + return finance.Random.ReplaceNumbers(withoutSeparator); + } - if (prefix.EndsWith("O")) - { //second character in prefix can't end with an 'O' - //Remap O to an X. - prefix = prefix.Replace('O', 'X'); - } + /// + /// National Insurance Number + /// + public static string Nino(this Finance finance, bool includeSeparator = true) + { + const string valid1stPrefixChars = "ABCEGHJKLMNOPRSTWXYZ"; + //const string valid2ndPrefixChars = "ABCEGHJKLMN PRSTWXYZ"; + const string validSuffixChars = "ABCD"; - //check for invalid prefixes - if (prefix == "GB" || prefix == "BG" || prefix == "NK" || - prefix == "KN" || prefix == "TN" || prefix == "NT" || - prefix == "ZZ") - { - //if the prefix is any of the invalid prefixes, - //Remap an invalid prefix to a well known valid one. - prefix = "CE"; - } + var prefix = finance.Random.String2(2, chars: valid1stPrefixChars); - var suffix = finance.Random.String2(1, validSuffixChars); + if (prefix.EndsWith("O")) + { //second character in prefix can't end with an 'O' + //Remap O to an X. + prefix = prefix.Replace('O', 'X'); + } - if (includeSeparator) - { - return finance.Random.ReplaceNumbers($"{prefix} ## ## ## {suffix}"); - } - return finance.Random.ReplaceNumbers($"{prefix}######{suffix}"); + //check for invalid prefixes + if (prefix == "GB" || prefix == "BG" || prefix == "NK" || + prefix == "KN" || prefix == "TN" || prefix == "NT" || + prefix == "ZZ") + { + //if the prefix is any of the invalid prefixes, + //Remap an invalid prefix to a well known valid one. + prefix = "CE"; } - /// - /// Country of the United Kingdom - /// - public static string CountryOfUnitedKingdom(this Address address) + var suffix = finance.Random.String2(1, validSuffixChars); + + if (includeSeparator) { - var countries = Database.Get(nameof(address), "uk_country", "en_GB") as BArray; - return address.Random.ArrayElement(countries); + return finance.Random.ReplaceNumbers($"{prefix} ## ## ## {suffix}"); } + return finance.Random.ReplaceNumbers($"{prefix}######{suffix}"); + } + + /// + /// Country of the United Kingdom + /// + public static string CountryOfUnitedKingdom(this Address address) + { + var countries = Database.Get(nameof(address), "uk_country", "en_GB") as BArray; + return address.Random.ArrayElement(countries); } } diff --git a/Source/Bogus/Extensions/UnitedStates/ExtensionsForUnitedStates.cs b/Source/Bogus/Extensions/UnitedStates/ExtensionsForUnitedStates.cs index bba8164a..a1f60182 100644 --- a/Source/Bogus/Extensions/UnitedStates/ExtensionsForUnitedStates.cs +++ b/Source/Bogus/Extensions/UnitedStates/ExtensionsForUnitedStates.cs @@ -1,48 +1,47 @@ using Bogus.DataSets; -namespace Bogus.Extensions.UnitedStates +namespace Bogus.Extensions.UnitedStates; + +/// +/// API extensions specific for a geographical location. +/// +public static class ExtensionsForUnitedStates { /// - /// API extensions specific for a geographical location. + /// Social Security Number /// - public static class ExtensionsForUnitedStates + public static string Ssn(this Person p) { - /// - /// Social Security Number - /// - public static string Ssn(this Person p) - { - const string Key = nameof(ExtensionsForUnitedStates) + "SSN"; + const string Key = nameof(ExtensionsForUnitedStates) + "SSN"; - if( p.context.ContainsKey(Key) ) - { - return p.context[Key] as string; - } + if( p.context.ContainsKey(Key) ) + { + return p.context[Key] as string; + } - var randomizer = p.Random; + var randomizer = p.Random; - //See Issue 260, SSN validity: - // https://secure.ssa.gov/apps10/poms.nsf/lnx/0110201035 + //See Issue 260, SSN validity: + // https://secure.ssa.gov/apps10/poms.nsf/lnx/0110201035 - var a = randomizer.Int(1, 898); - if (a == 666) a++; + var a = randomizer.Int(1, 898); + if (a == 666) a++; - var b = randomizer.Int(1, 99); - var c = randomizer.Int(1, 9999); + var b = randomizer.Int(1, 99); + var c = randomizer.Int(1, 9999); - var ssn = $"{a:000}-{b:00}-{c:0000}"; + var ssn = $"{a:000}-{b:00}-{c:0000}"; - p.context[Key] = ssn; + p.context[Key] = ssn; - return ssn; - } + return ssn; + } - /// - /// Employer Identification Number - /// - public static string Ein(this Company c) - { - return c.Random.ReplaceNumbers("##-#######"); - } + /// + /// Employer Identification Number + /// + public static string Ein(this Company c) + { + return c.Random.ReplaceNumbers("##-#######"); } } \ No newline at end of file diff --git a/Source/Bogus/Faker.cs b/Source/Bogus/Faker.cs index 958ea097..a2202f19 100644 --- a/Source/Bogus/Faker.cs +++ b/Source/Bogus/Faker.cs @@ -4,383 +4,382 @@ using Bogus.DataSets; using System; -namespace Bogus +namespace Bogus; + +/// +/// A hub of all the categories merged into a single class to ease fluent syntax API. +/// +public class Faker : ILocaleAware, IHasRandomizer, IHasContext { /// - /// A hub of all the categories merged into a single class to ease fluent syntax API. + /// The default mode to use when generating objects. Strict mode ensures that all properties have rules. + /// + public static bool DefaultStrictMode = false; + + /// + /// Create a Faker with a specific locale. /// - public class Faker : ILocaleAware, IHasRandomizer, IHasContext + public Faker(string locale = "en") { - /// - /// The default mode to use when generating objects. Strict mode ensures that all properties have rules. - /// - public static bool DefaultStrictMode = false; - - /// - /// Create a Faker with a specific locale. - /// - public Faker(string locale = "en") - { - Locale = locale; - - this.Address = this.Notifier.Flow(new Address(locale)); - this.Company = this.Notifier.Flow(new Company(locale)); - this.Date = this.Notifier.Flow(new Date (locale)); - this.Finance = this.Notifier.Flow(new Finance {Locale = locale}); - this.Hacker = this.Notifier.Flow(new Hacker(locale)); - this.Image = this.Notifier.Flow(new Images(locale)); - this.Internet = this.Notifier.Flow(new Internet(locale)); - this.Lorem = this.Notifier.Flow(new Lorem(locale)); - this.Name = this.Notifier.Flow(new Name(locale)); - - this.Phone = this.Notifier.Flow(new PhoneNumbers(locale)); - this.System = this.Notifier.Flow(new DataSets.System(locale)); - this.Commerce = this.Notifier.Flow(new Commerce(locale)); - this.Database = this.Notifier.Flow(new DataSets.Database()); - this.Rant = this.Notifier.Flow(new Rant()); - this.Vehicle = this.Notifier.Flow(new Vehicle()); - - this.Music = this.Notifier.Flow(new Music()); - - this.Hashids = new Hashids(); - } + Locale = locale; + + this.Address = this.Notifier.Flow(new Address(locale)); + this.Company = this.Notifier.Flow(new Company(locale)); + this.Date = this.Notifier.Flow(new Date (locale)); + this.Finance = this.Notifier.Flow(new Finance {Locale = locale}); + this.Hacker = this.Notifier.Flow(new Hacker(locale)); + this.Image = this.Notifier.Flow(new Images(locale)); + this.Internet = this.Notifier.Flow(new Internet(locale)); + this.Lorem = this.Notifier.Flow(new Lorem(locale)); + this.Name = this.Notifier.Flow(new Name(locale)); + + this.Phone = this.Notifier.Flow(new PhoneNumbers(locale)); + this.System = this.Notifier.Flow(new DataSets.System(locale)); + this.Commerce = this.Notifier.Flow(new Commerce(locale)); + this.Database = this.Notifier.Flow(new DataSets.Database()); + this.Rant = this.Notifier.Flow(new Rant()); + this.Vehicle = this.Notifier.Flow(new Vehicle()); + + this.Music = this.Notifier.Flow(new Music()); + + this.Hashids = new Hashids(); + } - Dictionary IHasContext.Context { get; } = new Dictionary(); + Dictionary IHasContext.Context { get; } = new Dictionary(); - /// - /// See - /// - protected SeedNotifier Notifier = new SeedNotifier(); + /// + /// See + /// + protected SeedNotifier Notifier = new SeedNotifier(); - SeedNotifier IHasRandomizer.GetNotifier() - { - return this.Notifier; - } + SeedNotifier IHasRandomizer.GetNotifier() + { + return this.Notifier; + } - private Randomizer randomizer; + private Randomizer randomizer; - /// - /// Generate numbers, booleans, and decimals. - /// - [RegisterMustasheMethods] - public Randomizer Random + /// + /// Generate numbers, booleans, and decimals. + /// + [RegisterMustasheMethods] + public Randomizer Random + { + get => this.randomizer ?? (this.Random = new Randomizer()); + set { - get => this.randomizer ?? (this.Random = new Randomizer()); - set - { - this.randomizer = value; - this.Notifier.Notify(value); - } + this.randomizer = value; + this.Notifier.Notify(value); } + } - /// - /// Can parse a handle bar expression like "{{name.lastName}}, {{name.firstName}} {{name.suffix}}". - /// - public string Parse(string str) - { - return Tokenizer.Parse(str, - this.Address, - this.Company, - this.Date, - this.Finance, - this.Hacker, - this.Image, - this.Internet, - this.Lorem, - this.Name, - this.Phone, - this.System, - this.Commerce, - this.Database, - this.Random); - } + /// + /// Can parse a handle bar expression like "{{name.lastName}}, {{name.firstName}} {{name.suffix}}". + /// + public string Parse(string str) + { + return Tokenizer.Parse(str, + this.Address, + this.Company, + this.Date, + this.Finance, + this.Hacker, + this.Image, + this.Internet, + this.Lorem, + this.Name, + this.Phone, + this.System, + this.Commerce, + this.Database, + this.Random); + } - private Person person; - - /// - /// A contextually relevant fields of a person. - /// - public Person Person => person ??= new Person(this.Random, this.Locale); - - /// - /// Creates hacker gibberish. - /// - [RegisterMustasheMethods] - public Hacker Hacker { get; set; } - - /// - /// Generate Phone Numbers - /// - [RegisterMustasheMethods] - public PhoneNumbers Phone { get; set; } - - /// - /// Generate Names - /// - [RegisterMustasheMethods] - public Name Name { get; set; } - - /// - /// Generate Words - /// - [RegisterMustasheMethods] - public Lorem Lorem { get; set; } - - /// - /// Generate Image URL Links - /// - [RegisterMustasheMethods] - public Images Image { get; set; } - - /// - /// Generate Finance Items - /// - [RegisterMustasheMethods] - public Finance Finance { get; set; } - - /// - /// Generate Addresses - /// - [RegisterMustasheMethods] - public Address Address { get; set; } - - /// - /// Generate Dates - /// - [RegisterMustasheMethods] - public Date Date { get; set; } - - /// - /// Generates company names, titles and BS. - /// - [RegisterMustasheMethods] - public Company Company { get; set; } - - /// - /// Generate Internet stuff like Emails and UserNames. - /// - [RegisterMustasheMethods] - public Internet Internet { get; set; } - - /// - /// Generates data related to commerce - /// - [RegisterMustasheMethods] - public Commerce Commerce { get; set; } - - /// - /// Generates fake data for many computer systems properties - /// - [RegisterMustasheMethods] - public DataSets.System System { get; set; } - - /// - /// Generates fake database things. - /// - [RegisterMustasheMethods] - public DataSets.Database Database { get; set; } - - /// - /// Generates random user content. - /// - [RegisterMustasheMethods] - public Rant Rant { get; set; } - - /// - /// Generates data related to vehicles. - /// - [RegisterMustasheMethods] - public Vehicle Vehicle { get; set; } - - /// - /// Generates data related to music. - /// - [RegisterMustasheMethods] - public Music Music { get; set; } - - /// - /// Helper method to pick a random element. - /// - public T PickRandom(IEnumerable items) - { - return this.Random.ArrayElement(items.ToArray()); - } + private Person person; - /// - /// Helper method to pick a random element. - /// - public T PickRandom(IList items) - { - return this.Random.ListItem(items); - } + /// + /// A contextually relevant fields of a person. + /// + public Person Person => person ??= new Person(this.Random, this.Locale); - /// - /// Helper method to pick a random element. - /// - public T PickRandom(ICollection items) - { - return this.Random.CollectionItem(items); - } + /// + /// Creates hacker gibberish. + /// + [RegisterMustasheMethods] + public Hacker Hacker { get; set; } - /// - /// Helper method to pick a random element. - /// - public T PickRandom(List items) - { - return this.Random.ListItem(items); - } + /// + /// Generate Phone Numbers + /// + [RegisterMustasheMethods] + public PhoneNumbers Phone { get; set; } - /// - /// Picks a random item of T specified in the parameter list. - /// - public T PickRandom(params T[] items) - { - return this.Random.ArrayElement(items); - } + /// + /// Generate Names + /// + [RegisterMustasheMethods] + public Name Name { get; set; } - /// - /// Picks a random item of T specified in the parameter list. - /// - public T PickRandomParam(params T[] items) - { - return this.Random.ArrayElement(items); - } + /// + /// Generate Words + /// + [RegisterMustasheMethods] + public Lorem Lorem { get; set; } - /// - /// Helper to pick random subset of elements out of the list. - /// - /// amount of elements to pick of the list. - /// if amountToPick is lower than zero. - public IEnumerable PickRandom(IEnumerable items, int amountToPick) - { - if( amountToPick < 0 ) - { - throw new ArgumentOutOfRangeException($"{nameof(amountToPick)} needs to be a positive integer."); - } - var size = items.Count(); - if( amountToPick > size ) - { - throw new ArgumentOutOfRangeException($"{nameof(amountToPick)} is greater than the number of items."); - } - return this.Random.Shuffle(items).Take(amountToPick); - } + /// + /// Generate Image URL Links + /// + [RegisterMustasheMethods] + public Images Image { get; set; } - /// - /// Helper method to call faker actions multiple times and return the result as IList of T - /// - public IList Make(int count, Func action) - { - return Enumerable.Range(1, count).Select(n => action()).ToList(); - } + /// + /// Generate Finance Items + /// + [RegisterMustasheMethods] + public Finance Finance { get; set; } - /// - /// Helper method to call faker actions multiple times and return the result as IList of T. - /// This method passes in the current index of the generation. - /// - public IList Make(int count, Func action) - { - return Enumerable.Range(1, count).Select(action).ToList(); - } + /// + /// Generate Addresses + /// + [RegisterMustasheMethods] + public Address Address { get; set; } - /// - /// Returns an IEnumerable[T] with LINQ deferred execution. Generated values - /// are not guaranteed to be repeatable until .ToList() is called. - /// - public IEnumerable MakeLazy(int count, Func action) - { - return Enumerable.Range(1, count).Select(n => action()); - } + /// + /// Generate Dates + /// + [RegisterMustasheMethods] + public Date Date { get; set; } - /// - /// Same as Make() except this method passes in the current index of the generation. Also, - /// returns an IEnumerable[T] with LINQ deferred execution. Generated values are not - /// guaranteed to be repeatable until .ToList() is called. - /// - public IEnumerable MakeLazy(int count, Func action) - { - return Enumerable.Range(1, count).Select(action); - } + /// + /// Generates company names, titles and BS. + /// + [RegisterMustasheMethods] + public Company Company { get; set; } + + /// + /// Generate Internet stuff like Emails and UserNames. + /// + [RegisterMustasheMethods] + public Internet Internet { get; set; } - /// - /// Picks a random Enum of T. Works only with Enums. - /// - /// Must be an Enum - public T PickRandom() where T : struct, Enum + /// + /// Generates data related to commerce + /// + [RegisterMustasheMethods] + public Commerce Commerce { get; set; } + + /// + /// Generates fake data for many computer systems properties + /// + [RegisterMustasheMethods] + public DataSets.System System { get; set; } + + /// + /// Generates fake database things. + /// + [RegisterMustasheMethods] + public DataSets.Database Database { get; set; } + + /// + /// Generates random user content. + /// + [RegisterMustasheMethods] + public Rant Rant { get; set; } + + /// + /// Generates data related to vehicles. + /// + [RegisterMustasheMethods] + public Vehicle Vehicle { get; set; } + + /// + /// Generates data related to music. + /// + [RegisterMustasheMethods] + public Music Music { get; set; } + + /// + /// Helper method to pick a random element. + /// + public T PickRandom(IEnumerable items) + { + return this.Random.ArrayElement(items.ToArray()); + } + + /// + /// Helper method to pick a random element. + /// + public T PickRandom(IList items) + { + return this.Random.ListItem(items); + } + + /// + /// Helper method to pick a random element. + /// + public T PickRandom(ICollection items) + { + return this.Random.CollectionItem(items); + } + + /// + /// Helper method to pick a random element. + /// + public T PickRandom(List items) + { + return this.Random.ListItem(items); + } + + /// + /// Picks a random item of T specified in the parameter list. + /// + public T PickRandom(params T[] items) + { + return this.Random.ArrayElement(items); + } + + /// + /// Picks a random item of T specified in the parameter list. + /// + public T PickRandomParam(params T[] items) + { + return this.Random.ArrayElement(items); + } + + /// + /// Helper to pick random subset of elements out of the list. + /// + /// amount of elements to pick of the list. + /// if amountToPick is lower than zero. + public IEnumerable PickRandom(IEnumerable items, int amountToPick) + { + if( amountToPick < 0 ) { - return this.Random.Enum(); + throw new ArgumentOutOfRangeException($"{nameof(amountToPick)} needs to be a positive integer."); } - - /// - /// Picks a random Enum of T, excluding those passed as parameters. - /// - /// The items in the Enum of T to exclude from selection. - public T PickRandomWithout(params T[] exclude) where T : struct, Enum + var size = items.Count(); + if( amountToPick > size ) { - return this.Random.Enum(exclude); + throw new ArgumentOutOfRangeException($"{nameof(amountToPick)} is greater than the number of items."); } + return this.Random.Shuffle(items).Take(amountToPick); + } - /// - /// The current locale for the dataset. - /// - /// The locale. - public string Locale { get; set; } + /// + /// Helper method to call faker actions multiple times and return the result as IList of T + /// + public IList Make(int count, Func action) + { + return Enumerable.Range(1, count).Select(n => action()).ToList(); + } - /// - /// Triggers a new generation context - /// - internal void NewContext() - { - person = null; - this.capturedGlobalIndex = Interlocked.Increment(ref GlobalUniqueIndex); - Interlocked.Increment(ref IndexFaker); - } + /// + /// Helper method to call faker actions multiple times and return the result as IList of T. + /// This method passes in the current index of the generation. + /// + public IList Make(int count, Func action) + { + return Enumerable.Range(1, count).Select(action).ToList(); + } - /// - /// Checks if the internal state is ready to be used by . - /// In other words, has NewContext ever been called since this object was created? - /// See Issue 143. https://github.com/bchavez/Bogus/issues/143 - /// - internal bool HasContext => this.IndexFaker != -1; - - /// - /// A global variable that is automatically incremented on every - /// new object created by Bogus. Useful for composing property values that require - /// uniqueness. - /// - public static int GlobalUniqueIndex = -1; - - private int capturedGlobalIndex; - - /// - /// Alias for IndexGlobal. - /// - //[Obsolete("Please use IndexGlobal instead.")] - public int UniqueIndex => capturedGlobalIndex; - - /// - /// A global static variable that is automatically incremented on every - /// new object created by Bogus across all Faker[T]s in the entire application. - /// Useful for composing property values that require uniqueness across - /// the entire application. - /// - public int IndexGlobal => capturedGlobalIndex; - - /// - /// A local variable that is automatically incremented on every - /// new object generated by the Faker[T] instance for lifetime of Faker[T]. - /// - public int IndexFaker = -1; - - /// - /// A local index variable that can be controlled inside rules with ++ and --. - /// This variable's lifetime exists for the lifetime of Faker[T]. - /// - public int IndexVariable = 0; - - /// - /// HashID generator with default (string.Empty) salt. See: https://github.com/ullmark/hashids.net - /// - public Hashids Hashids { get; set; } + /// + /// Returns an IEnumerable[T] with LINQ deferred execution. Generated values + /// are not guaranteed to be repeatable until .ToList() is called. + /// + public IEnumerable MakeLazy(int count, Func action) + { + return Enumerable.Range(1, count).Select(n => action()); } + + /// + /// Same as Make() except this method passes in the current index of the generation. Also, + /// returns an IEnumerable[T] with LINQ deferred execution. Generated values are not + /// guaranteed to be repeatable until .ToList() is called. + /// + public IEnumerable MakeLazy(int count, Func action) + { + return Enumerable.Range(1, count).Select(action); + } + + /// + /// Picks a random Enum of T. Works only with Enums. + /// + /// Must be an Enum + public T PickRandom() where T : struct, Enum + { + return this.Random.Enum(); + } + + /// + /// Picks a random Enum of T, excluding those passed as parameters. + /// + /// The items in the Enum of T to exclude from selection. + public T PickRandomWithout(params T[] exclude) where T : struct, Enum + { + return this.Random.Enum(exclude); + } + + /// + /// The current locale for the dataset. + /// + /// The locale. + public string Locale { get; set; } + + /// + /// Triggers a new generation context + /// + internal void NewContext() + { + person = null; + this.capturedGlobalIndex = Interlocked.Increment(ref GlobalUniqueIndex); + Interlocked.Increment(ref IndexFaker); + } + + /// + /// Checks if the internal state is ready to be used by . + /// In other words, has NewContext ever been called since this object was created? + /// See Issue 143. https://github.com/bchavez/Bogus/issues/143 + /// + internal bool HasContext => this.IndexFaker != -1; + + /// + /// A global variable that is automatically incremented on every + /// new object created by Bogus. Useful for composing property values that require + /// uniqueness. + /// + public static int GlobalUniqueIndex = -1; + + private int capturedGlobalIndex; + + /// + /// Alias for IndexGlobal. + /// + //[Obsolete("Please use IndexGlobal instead.")] + public int UniqueIndex => capturedGlobalIndex; + + /// + /// A global static variable that is automatically incremented on every + /// new object created by Bogus across all Faker[T]s in the entire application. + /// Useful for composing property values that require uniqueness across + /// the entire application. + /// + public int IndexGlobal => capturedGlobalIndex; + + /// + /// A local variable that is automatically incremented on every + /// new object generated by the Faker[T] instance for lifetime of Faker[T]. + /// + public int IndexFaker = -1; + + /// + /// A local index variable that can be controlled inside rules with ++ and --. + /// This variable's lifetime exists for the lifetime of Faker[T]. + /// + public int IndexVariable = 0; + + /// + /// HashID generator with default (string.Empty) salt. See: https://github.com/ullmark/hashids.net + /// + public Hashids Hashids { get; set; } } \ No newline at end of file diff --git a/Source/Bogus/Faker[T].Extensions.cs b/Source/Bogus/Faker[T].Extensions.cs index 3aee86fa..ba4a02b2 100644 --- a/Source/Bogus/Faker[T].Extensions.cs +++ b/Source/Bogus/Faker[T].Extensions.cs @@ -1,65 +1,64 @@ using System; using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Extensions for . +/// +public static class ExtensionsForFakerT { /// - /// Extensions for . + /// Generate multiple fake objects of T. The number of generated items is randomly chosen between and values. + /// The random number between and should be considered non-deterministic but technically depends on the parameters each time this method was called. /// - public static class ExtensionsForFakerT + /// The to extend with this extension method. + /// Minimum number of T objects to create. Inclusive. + /// Maximum number of T objects to create. Inclusive. + public static List GenerateBetween(this Faker faker, int min, int max, string ruleSets = null) where T : class { - /// - /// Generate multiple fake objects of T. The number of generated items is randomly chosen between and values. - /// The random number between and should be considered non-deterministic but technically depends on the parameters each time this method was called. - /// - /// The to extend with this extension method. - /// Minimum number of T objects to create. Inclusive. - /// Maximum number of T objects to create. Inclusive. - public static List GenerateBetween(this Faker faker, int min, int max, string ruleSets = null) where T : class - { - var internals = faker as IFakerTInternal; - var r = internals.FakerHub.Random; - var n = r.Number(min, max); - return faker.Generate(n, ruleSets); - } + var internals = faker as IFakerTInternal; + var r = internals.FakerHub.Random; + var n = r.Number(min, max); + return faker.Generate(n, ruleSets); + } - /// - /// Helpful extension for creating randomly null values for .RuleFor() rules. - /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Word().OrNull(f)) - /// - /// Any reference type. - /// The Faker facade. This is usually the f from RuleFor(.., f => lambda). - /// The probability of null occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% null is desired pass nullWeight = 0.15f. - public static T OrNull(this T value, in Faker f, float nullWeight = 0.5f) where T : class - { - if (nullWeight > 1 || nullWeight < 0) throw new ArgumentOutOfRangeException(nameof(nullWeight), $".{nameof(OrNull)}() {nameof(nullWeight)} of '{nullWeight}' must be between 1.0f and 0.0f."); - return f.Random.Float() > nullWeight ? value : null; - } + /// + /// Helpful extension for creating randomly null values for .RuleFor() rules. + /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Word().OrNull(f)) + /// + /// Any reference type. + /// The Faker facade. This is usually the f from RuleFor(.., f => lambda). + /// The probability of null occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% null is desired pass nullWeight = 0.15f. + public static T OrNull(this T value, in Faker f, float nullWeight = 0.5f) where T : class + { + if (nullWeight > 1 || nullWeight < 0) throw new ArgumentOutOfRangeException(nameof(nullWeight), $".{nameof(OrNull)}() {nameof(nullWeight)} of '{nullWeight}' must be between 1.0f and 0.0f."); + return f.Random.Float() > nullWeight ? value : null; + } - /// - /// Helpful extension for creating randomly null values for .RuleFor() rules. - /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Int().OrNull(f)) - /// - /// Any nullable type. ie: int?, Guid?, etc. - /// The Faker facade. This is usually the f from RuleFor(.., f => lambda). - /// The probability of null occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% null is desired pass nullWeight = 0.15f. - public static T? OrNull(this T value, Faker f, float nullWeight = 0.5f) where T : struct - { - if (nullWeight > 1 || nullWeight < 0) throw new ArgumentOutOfRangeException(nameof(nullWeight), $".{nameof(OrNull)}() {nameof(nullWeight)} of '{nullWeight}' must be between 1.0f and 0.0f."); - return f.Random.Float() > nullWeight ? new T?(value) : null; - } + /// + /// Helpful extension for creating randomly null values for .RuleFor() rules. + /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Int().OrNull(f)) + /// + /// Any nullable type. ie: int?, Guid?, etc. + /// The Faker facade. This is usually the f from RuleFor(.., f => lambda). + /// The probability of null occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% null is desired pass nullWeight = 0.15f. + public static T? OrNull(this T value, Faker f, float nullWeight = 0.5f) where T : struct + { + if (nullWeight > 1 || nullWeight < 0) throw new ArgumentOutOfRangeException(nameof(nullWeight), $".{nameof(OrNull)}() {nameof(nullWeight)} of '{nullWeight}' must be between 1.0f and 0.0f."); + return f.Random.Float() > nullWeight ? new T?(value) : null; + } - /// - /// Helpful extension for creating randomly default(T) values for .RuleFor() rules. - /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Word().OrDefault(f)) - /// - /// The Faker facade. This is usually the f from f => lambda. - /// The probability of the default value occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% default(T) is desired pass defaultWeight = 0.15f. - /// The default value to return. - public static T OrDefault(this T value, Faker f, float defaultWeight = 0.5f, T defaultValue = default) - { - if (defaultWeight > 1 || defaultWeight < 0) throw new ArgumentOutOfRangeException(nameof(defaultWeight), $".{nameof(OrDefault)}() {nameof(defaultWeight)} of '{defaultWeight}' must be between 1.0f and 0.0f. "); - return f.Random.Float() > defaultWeight ? value : defaultValue; - } + /// + /// Helpful extension for creating randomly default(T) values for .RuleFor() rules. + /// Example: .RuleFor(x=>x.Prop, f=>f.Random.Word().OrDefault(f)) + /// + /// The Faker facade. This is usually the f from f => lambda. + /// The probability of the default value occurring. Range [1.0f - 0.0f] (100% and 0%) respectively. For example, if 15% default(T) is desired pass defaultWeight = 0.15f. + /// The default value to return. + public static T OrDefault(this T value, Faker f, float defaultWeight = 0.5f, T defaultValue = default) + { + if (defaultWeight > 1 || defaultWeight < 0) throw new ArgumentOutOfRangeException(nameof(defaultWeight), $".{nameof(OrDefault)}() {nameof(defaultWeight)} of '{defaultWeight}' must be between 1.0f and 0.0f. "); + return f.Random.Float() > defaultWeight ? value : defaultValue; } } \ No newline at end of file diff --git a/Source/Bogus/Faker[T].cs b/Source/Bogus/Faker[T].cs index 5c942ea6..6d733398 100644 --- a/Source/Bogus/Faker[T].cs +++ b/Source/Bogus/Faker[T].cs @@ -6,781 +6,780 @@ using System.Text; using Bogus.Extensions; -namespace Bogus +namespace Bogus; + +/// +/// Hidden API implemented explicitly on . When is casted explicitly to , +/// the cast reveals some protected internal objects of without needing to derive +/// from . This is useful for extensions methods that need access internal variables of like , , , and type of T. +/// +public interface IFakerTInternal { /// - /// Hidden API implemented explicitly on . When is casted explicitly to , - /// the cast reveals some protected internal objects of without needing to derive - /// from . This is useful for extensions methods that need access internal variables of like , , , and type of T. + /// The internal FakerHub object that is used in f => f rules. Usually used to gain access to a source of randomness by extension methods. /// - public interface IFakerTInternal - { - /// - /// The internal FakerHub object that is used in f => f rules. Usually used to gain access to a source of randomness by extension methods. - /// - Faker FakerHub { get; } - - /// - /// The field/property binder used by . - /// - IBinder Binder { get; } + Faker FakerHub { get; } - /// - /// The local seed of if available. Null local seed means the Global property is being used. - /// - int? LocalSeed { get; } + /// + /// The field/property binder used by . + /// + IBinder Binder { get; } - /// - /// The type of T in . - /// - Type TypeOfT { get; } - } + /// + /// The local seed of if available. Null local seed means the Global property is being used. + /// + int? LocalSeed { get; } /// - /// Generates fake objects of . + /// The type of T in . /// - /// The object to fake. - public class Faker : IFakerTInternal, ILocaleAware, IRuleSet where T : class - { + Type TypeOfT { get; } +} + +/// +/// Generates fake objects of . +/// +/// The object to fake. +public class Faker : IFakerTInternal, ILocaleAware, IRuleSet where T : class +{ #pragma warning disable 1591 - protected const string Default = "default"; - private static readonly string[] DefaultRuleSet = {Default}; - protected internal Faker FakerHub; - protected internal IBinder binder; - - protected internal readonly MultiDictionary> Actions = - new MultiDictionary>(StringComparer.OrdinalIgnoreCase); - - protected internal readonly Dictionary> FinalizeActions = new Dictionary>(StringComparer.OrdinalIgnoreCase); - protected internal Dictionary> CreateActions = new Dictionary>(StringComparer.OrdinalIgnoreCase); - protected internal readonly Dictionary TypeProperties; - protected internal readonly Dictionary> SetterCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); - - protected internal Dictionary StrictModes = new Dictionary(); - protected internal bool? IsValid; - protected internal string currentRuleSet = Default; - protected internal int? localSeed; // if null, the global Randomizer.Seed is used. + protected const string Default = "default"; + private static readonly string[] DefaultRuleSet = {Default}; + protected internal Faker FakerHub; + protected internal IBinder binder; + + protected internal readonly MultiDictionary> Actions = + new MultiDictionary>(StringComparer.OrdinalIgnoreCase); + + protected internal readonly Dictionary> FinalizeActions = new Dictionary>(StringComparer.OrdinalIgnoreCase); + protected internal Dictionary> CreateActions = new Dictionary>(StringComparer.OrdinalIgnoreCase); + protected internal readonly Dictionary TypeProperties; + protected internal readonly Dictionary> SetterCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + protected internal Dictionary StrictModes = new Dictionary(); + protected internal bool? IsValid; + protected internal string currentRuleSet = Default; + protected internal int? localSeed; // if null, the global Randomizer.Seed is used. #pragma warning restore 1591 - Faker IFakerTInternal.FakerHub => this.FakerHub; - - IBinder IFakerTInternal.Binder => this.binder; - - int? IFakerTInternal.LocalSeed => this.localSeed; - - Type IFakerTInternal.TypeOfT => typeof(T); - - /// - /// Clones the internal state of a into a new so that - /// both are isolated from each other. The clone will have internal state - /// reset as if was never called. - /// - public Faker Clone() - { - var clone = new Faker(this.Locale, this.binder); - - //copy internal state. - //strict modes. - foreach( var root in this.StrictModes ) - { - clone.StrictModes.Add(root.Key, root.Value); - } - - //create actions - foreach( var root in this.CreateActions ) - { - clone.CreateActions[root.Key] = root.Value; - } - //finalize actions - foreach( var root in this.FinalizeActions ) - { - clone.FinalizeActions.Add(root.Key, root.Value); - } + Faker IFakerTInternal.FakerHub => this.FakerHub; - //actions - foreach( var root in this.Actions ) - { - foreach( var kv in root.Value ) - { - clone.Actions.Add(root.Key, kv.Key, kv.Value); - } - } + IBinder IFakerTInternal.Binder => this.binder; - if( localSeed.HasValue ) - { - clone.UseSeed(localSeed.Value); - } + int? IFakerTInternal.LocalSeed => this.localSeed; - return clone; - } + Type IFakerTInternal.TypeOfT => typeof(T); - /// - /// The current locale. - /// - public string Locale { get; set; } + /// + /// Clones the internal state of a into a new so that + /// both are isolated from each other. The clone will have internal state + /// reset as if was never called. + /// + public Faker Clone() + { + var clone = new Faker(this.Locale, this.binder); - /// - /// Creates a Faker with default 'en' locale. - /// - public Faker() : this("en", null) + //copy internal state. + //strict modes. + foreach( var root in this.StrictModes ) { + clone.StrictModes.Add(root.Key, root.Value); } - /// - /// Creates a Faker with a locale - /// - public Faker(string locale) : this(locale, null) + //create actions + foreach( var root in this.CreateActions ) { + clone.CreateActions[root.Key] = root.Value; } - - /// - /// Creates a Faker with a locale. - /// - /// language - /// A binder that discovers properties or fields on T that are candidates for faking. Null uses the default Binder. - public Faker(string locale = "en", IBinder binder = null) + //finalize actions + foreach( var root in this.FinalizeActions ) { - this.binder = binder ?? new Binder(); - this.Locale = locale; - FakerHub = new Faker(locale); - TypeProperties = this.binder.GetMembers(typeof(T)); - this.CreateActions[Default] = faker => Activator.CreateInstance(); + clone.FinalizeActions.Add(root.Key, root.Value); } - /// - /// Creates a seed locally scoped within this ignoring the globally scoped . - /// If this method is never called the global is used. - /// - /// The seed value to use within this instance. - public virtual Faker UseSeed(int seed) + //actions + foreach( var root in this.Actions ) { - this.localSeed = seed; - this.FakerHub.Random = new Randomizer(seed); - return this; + foreach( var kv in root.Value ) + { + clone.Actions.Add(root.Key, kv.Key, kv.Value); + } } - /// - /// Instructs to use the factory method as a source - /// for new instances of . - /// - public virtual Faker CustomInstantiator(Func factoryMethod) + if( localSeed.HasValue ) { - this.CreateActions[currentRuleSet] = factoryMethod; - return this; + clone.UseSeed(localSeed.Value); } - /// - /// Creates a rule for a compound property and providing access to the instance being generated. - /// - public virtual Faker RuleFor(Expression> property, Func setter) - { - var propName = PropertyName.For(property); + return clone; + } - return AddRule(propName, (f, t) => setter(f, t)); - } + /// + /// The current locale. + /// + public string Locale { get; set; } - /// - /// Creates a rule for a property. - /// - public virtual Faker RuleFor(Expression> property, TProperty value) - { - var propName = PropertyName.For(property); + /// + /// Creates a Faker with default 'en' locale. + /// + public Faker() : this("en", null) + { + } - return AddRule(propName, (f, t) => value); - } + /// + /// Creates a Faker with a locale + /// + public Faker(string locale) : this(locale, null) + { + } - /// - /// Creates a rule for a property. - /// - public virtual Faker RuleFor(Expression> property, Func valueFunction) - { - var propName = PropertyName.For(property); + /// + /// Creates a Faker with a locale. + /// + /// language + /// A binder that discovers properties or fields on T that are candidates for faking. Null uses the default Binder. + public Faker(string locale = "en", IBinder binder = null) + { + this.binder = binder ?? new Binder(); + this.Locale = locale; + FakerHub = new Faker(locale); + TypeProperties = this.binder.GetMembers(typeof(T)); + this.CreateActions[Default] = faker => Activator.CreateInstance(); + } - return AddRule(propName, (f, t) => valueFunction()); - } + /// + /// Creates a seed locally scoped within this ignoring the globally scoped . + /// If this method is never called the global is used. + /// + /// The seed value to use within this instance. + public virtual Faker UseSeed(int seed) + { + this.localSeed = seed; + this.FakerHub.Random = new Randomizer(seed); + return this; + } - /// - /// Creates a rule for a property. - /// - public virtual Faker RuleFor(Expression> property, Func setter) - { - var propName = PropertyName.For(property); + /// + /// Instructs to use the factory method as a source + /// for new instances of . + /// + public virtual Faker CustomInstantiator(Func factoryMethod) + { + this.CreateActions[currentRuleSet] = factoryMethod; + return this; + } - return AddRule(propName, (f, t) => setter(f)); - } + /// + /// Creates a rule for a compound property and providing access to the instance being generated. + /// + public virtual Faker RuleFor(Expression> property, Func setter) + { + var propName = PropertyName.For(property); - /// - /// Create a rule for a hidden property or field. - /// Used in advanced scenarios to create rules for hidden properties or fields. - /// - /// The property name or field name of the member to create a rule for. - public virtual Faker RuleFor(string propertyOrFieldName, Func setter) - { - EnsureMemberExists(propertyOrFieldName, - $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + - $"Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + - $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + - $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); + return AddRule(propName, (f, t) => setter(f, t)); + } - return AddRule(propertyOrFieldName, (f, t) => setter(f)); - } + /// + /// Creates a rule for a property. + /// + public virtual Faker RuleFor(Expression> property, TProperty value) + { + var propName = PropertyName.For(property); - /// - /// Create a rule for a hidden property or field. - /// Used in advanced scenarios to create rules for hidden properties or fields. - /// - /// The property name or field name of the member to create a rule for. - public virtual Faker RuleFor(string propertyOrFieldName, Func setter) - { - EnsureMemberExists(propertyOrFieldName, - $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + - $"Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + - $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + - $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); - - return AddRule(propertyOrFieldName, (f, t) => setter(f, t)); - } + return AddRule(propName, (f, t) => value); + } - protected virtual Faker AddRule(string propertyOrField, Func invoker) - { - var rule = new PopulateAction - { - Action = invoker, - RuleSet = currentRuleSet, - PropertyName = propertyOrField, - }; + /// + /// Creates a rule for a property. + /// + public virtual Faker RuleFor(Expression> property, Func valueFunction) + { + var propName = PropertyName.For(property); - this.Actions.Add(currentRuleSet, propertyOrField, rule); + return AddRule(propName, (f, t) => valueFunction()); + } - return this; - } + /// + /// Creates a rule for a property. + /// + public virtual Faker RuleFor(Expression> property, Func setter) + { + var propName = PropertyName.For(property); - /// - /// Specify multiple rules inside an action without having to call - /// RuleFor multiple times. Note: must be false - /// since rules for properties and fields cannot be individually checked when - /// using this method. - /// - public virtual Faker Rules(Action setActions) - { - Func invoker = (f, t) => - { - setActions(f, t); - return null; - }; - var guid = Guid.NewGuid().ToString(); - var rule = new PopulateAction - { - Action = invoker, - RuleSet = currentRuleSet, - PropertyName = guid, - ProhibitInStrictMode = true - }; - this.Actions.Add(currentRuleSet, guid, rule); - return this; - } + return AddRule(propName, (f, t) => setter(f)); + } - /// - /// Creates one rule for all types of on type . - /// In other words, if you have with many fields or properties of - /// type this method allows you to specify a rule for all fields or - /// properties of type . - /// - public virtual Faker RuleForType(Type type, Func setterForType) - { - if( typeof(TType) != type ) - { - throw new ArgumentException($"{nameof(TType)} must be the same type as parameter named '{nameof(type)}'"); - } + /// + /// Create a rule for a hidden property or field. + /// Used in advanced scenarios to create rules for hidden properties or fields. + /// + /// The property name or field name of the member to create a rule for. + public virtual Faker RuleFor(string propertyOrFieldName, Func setter) + { + EnsureMemberExists(propertyOrFieldName, + $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + + $"Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + + $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + + $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); + + return AddRule(propertyOrFieldName, (f, t) => setter(f)); + } + + /// + /// Create a rule for a hidden property or field. + /// Used in advanced scenarios to create rules for hidden properties or fields. + /// + /// The property name or field name of the member to create a rule for. + public virtual Faker RuleFor(string propertyOrFieldName, Func setter) + { + EnsureMemberExists(propertyOrFieldName, + $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + + $"Can't create a rule for {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + + $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + + $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); + + return AddRule(propertyOrFieldName, (f, t) => setter(f, t)); + } - foreach( var kvp in this.TypeProperties ) + protected virtual Faker AddRule(string propertyOrField, Func invoker) + { + var rule = new PopulateAction { - var propOrFieldType = GetFieldOrPropertyType(kvp.Value); - var propOrFieldName = kvp.Key; + Action = invoker, + RuleSet = currentRuleSet, + PropertyName = propertyOrField, + }; - if( propOrFieldType == type ) - { - RuleFor(propOrFieldName, setterForType); - } - } + this.Actions.Add(currentRuleSet, propertyOrField, rule); - return this; - } + return this; + } - /// - /// Utility method to get the Type of a Property or Field - /// - protected virtual Type GetFieldOrPropertyType(MemberInfo mi) - { - if( mi is PropertyInfo pi ) + /// + /// Specify multiple rules inside an action without having to call + /// RuleFor multiple times. Note: must be false + /// since rules for properties and fields cannot be individually checked when + /// using this method. + /// + public virtual Faker Rules(Action setActions) + { + Func invoker = (f, t) => { - return pi.PropertyType; - } - if( mi is FieldInfo fi ) + setActions(f, t); + return null; + }; + var guid = Guid.NewGuid().ToString(); + var rule = new PopulateAction { - return fi.FieldType; - } - return null; - } + Action = invoker, + RuleSet = currentRuleSet, + PropertyName = guid, + ProhibitInStrictMode = true + }; + this.Actions.Add(currentRuleSet, guid, rule); + return this; + } - /// - /// Defines a set of rules under a specific name. Useful for defining - /// rules for special cases. Note: The name `default` is the name of all rules that are - /// defined without an explicit rule set. - /// - /// The rule set name. - /// The set of rules to apply when this rules set is specified. - public virtual Faker RuleSet(string ruleSetName, Action> action) + /// + /// Creates one rule for all types of on type . + /// In other words, if you have with many fields or properties of + /// type this method allows you to specify a rule for all fields or + /// properties of type . + /// + public virtual Faker RuleForType(Type type, Func setterForType) + { + if( typeof(TType) != type ) { - if( currentRuleSet != Default ) throw new ArgumentException("Cannot create a rule set within a rule set."); - currentRuleSet = ruleSetName; - action(this); - currentRuleSet = Default; - return this; + throw new ArgumentException($"{nameof(TType)} must be the same type as parameter named '{nameof(type)}'"); } - /// - /// Ensures a member exists provided by the IBinder. - /// - protected virtual void EnsureMemberExists(string propNameOrField, string exceptionMessage) + foreach( var kvp in this.TypeProperties ) { - if (!this.TypeProperties.TryGetValue(propNameOrField, out MemberInfo mi)) + var propOrFieldType = GetFieldOrPropertyType(kvp.Value); + var propOrFieldName = kvp.Key; + + if( propOrFieldType == type ) { - throw new ArgumentException(exceptionMessage); + RuleFor(propOrFieldName, setterForType); } } - /// - /// Ignores a property or field when is enabled. - /// Used in advanced scenarios to ignore hidden properties or fields. - /// - /// The property name or field name of the member to create a rule for. - public virtual Faker Ignore(string propertyOrFieldName) - { - EnsureMemberExists(propertyOrFieldName, - $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + - $"Can't ignore member {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + - $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + - $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); - - var rule = new PopulateAction - { - Action = null, - RuleSet = currentRuleSet, - PropertyName = propertyOrFieldName - }; - - this.Actions.Add(currentRuleSet, propertyOrFieldName, rule); - - return this; - } + return this; + } - /// - /// Ignores a property or field when is enabled. - /// - public virtual Faker Ignore(Expression> propertyOrField) + /// + /// Utility method to get the Type of a Property or Field + /// + protected virtual Type GetFieldOrPropertyType(MemberInfo mi) + { + if( mi is PropertyInfo pi ) { - var propNameOrField = PropertyName.For(propertyOrField); - - return Ignore(propNameOrField); + return pi.PropertyType; } - - /// - /// When set to true, ensures all properties and public fields of have rules - /// before an object of is populated or generated. Manual assertion - /// can be invoked using and . - /// - /// Overrides any global setting in . - public virtual Faker StrictMode(bool ensureRulesForAllProperties) + if( mi is FieldInfo fi ) { - this.StrictModes[currentRuleSet] = ensureRulesForAllProperties; - return this; + return fi.FieldType; } + return null; + } - /// - /// A finalizing action rule applied to after all the rules - /// are executed. - /// - public virtual Faker FinishWith(Action action) - { - var rule = new FinalizeAction - { - Action = action, - RuleSet = currentRuleSet - }; - this.FinalizeActions[currentRuleSet] = rule; - return this; - } + /// + /// Defines a set of rules under a specific name. Useful for defining + /// rules for special cases. Note: The name `default` is the name of all rules that are + /// defined without an explicit rule set. + /// + /// The rule set name. + /// The set of rules to apply when this rules set is specified. + public virtual Faker RuleSet(string ruleSetName, Action> action) + { + if( currentRuleSet != Default ) throw new ArgumentException("Cannot create a rule set within a rule set."); + currentRuleSet = ruleSetName; + action(this); + currentRuleSet = Default; + return this; + } - /// - /// Utility method to parse out rule sets form user input. - /// - protected virtual string[] ParseDirtyRulesSets(string dirtyRules) + /// + /// Ensures a member exists provided by the IBinder. + /// + protected virtual void EnsureMemberExists(string propNameOrField, string exceptionMessage) + { + if (!this.TypeProperties.TryGetValue(propNameOrField, out MemberInfo mi)) { - dirtyRules = dirtyRules?.Trim(',').Trim(); - if( string.IsNullOrWhiteSpace(dirtyRules) ) return DefaultRuleSet; - return dirtyRules.Split(',') - .Where(s => !string.IsNullOrWhiteSpace(s)) - .Select(s => s.Trim()).ToArray(); + throw new ArgumentException(exceptionMessage); } + } - /// - /// Generates a fake object of using the specified rules in this - /// . - /// - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - public virtual T Generate(string ruleSets = null) - { - Func createRule = null; - var cleanRules = ParseDirtyRulesSets(ruleSets); + /// + /// Ignores a property or field when is enabled. + /// Used in advanced scenarios to ignore hidden properties or fields. + /// + /// The property name or field name of the member to create a rule for. + public virtual Faker Ignore(string propertyOrFieldName) + { + EnsureMemberExists(propertyOrFieldName, + $"The property or field {propertyOrFieldName} was not found on {typeof(T)}. " + + $"Can't ignore member {typeof(T)}.{propertyOrFieldName} when {propertyOrFieldName} " + + $"cannot be found. Try creating a custom IBinder for Faker with the appropriate " + + $"System.Reflection.BindingFlags that allows deeper reflection into {typeof(T)}."); - if( string.IsNullOrWhiteSpace(ruleSets) ) + var rule = new PopulateAction { - createRule = CreateActions[Default]; - } - else + Action = null, + RuleSet = currentRuleSet, + PropertyName = propertyOrFieldName + }; + + this.Actions.Add(currentRuleSet, propertyOrFieldName, rule); + + return this; + } + + /// + /// Ignores a property or field when is enabled. + /// + public virtual Faker Ignore(Expression> propertyOrField) + { + var propNameOrField = PropertyName.For(propertyOrField); + + return Ignore(propNameOrField); + } + + /// + /// When set to true, ensures all properties and public fields of have rules + /// before an object of is populated or generated. Manual assertion + /// can be invoked using and . + /// + /// Overrides any global setting in . + public virtual Faker StrictMode(bool ensureRulesForAllProperties) + { + this.StrictModes[currentRuleSet] = ensureRulesForAllProperties; + return this; + } + + /// + /// A finalizing action rule applied to after all the rules + /// are executed. + /// + public virtual Faker FinishWith(Action action) + { + var rule = new FinalizeAction { - var firstRule = cleanRules[0]; - createRule = CreateActions.TryGetValue(firstRule, out createRule) ? createRule : CreateActions[Default]; - } + Action = action, + RuleSet = currentRuleSet + }; + this.FinalizeActions[currentRuleSet] = rule; + return this; + } - //Issue 143 - We need a new FakerHub context before calling the - // constructor. Associated Issue 57: Again, before any - // rules execute, we need a context to capture IndexGlobal - // and IndexFaker variables. - FakerHub.NewContext(); - var instance = createRule(this.FakerHub); + /// + /// Utility method to parse out rule sets form user input. + /// + protected virtual string[] ParseDirtyRulesSets(string dirtyRules) + { + dirtyRules = dirtyRules?.Trim(',').Trim(); + if( string.IsNullOrWhiteSpace(dirtyRules) ) return DefaultRuleSet; + return dirtyRules.Split(',') + .Where(s => !string.IsNullOrWhiteSpace(s)) + .Select(s => s.Trim()).ToArray(); + } - PopulateInternal(instance, cleanRules); + /// + /// Generates a fake object of using the specified rules in this + /// . + /// + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + public virtual T Generate(string ruleSets = null) + { + Func createRule = null; + var cleanRules = ParseDirtyRulesSets(ruleSets); - return instance; + if( string.IsNullOrWhiteSpace(ruleSets) ) + { + createRule = CreateActions[Default]; } - - /// - /// Generates a fake objects of type using the specified rules in - /// this . - /// - /// The number of items to create in the . - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - public virtual List Generate(int count, string ruleSets = null) + else { - return Enumerable.Range(1, count) - .Select(i => Generate(ruleSets)) - .ToList(); + var firstRule = cleanRules[0]; + createRule = CreateActions.TryGetValue(firstRule, out createRule) ? createRule : CreateActions[Default]; } - /// - /// Returns an with LINQ deferred execution. Generated values - /// are not guaranteed to be repeatable until is called. - /// - /// The number of items to create in the . - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - public virtual IEnumerable GenerateLazy(int count, string ruleSets = null) + //Issue 143 - We need a new FakerHub context before calling the + // constructor. Associated Issue 57: Again, before any + // rules execute, we need a context to capture IndexGlobal + // and IndexFaker variables. + FakerHub.NewContext(); + var instance = createRule(this.FakerHub); + + PopulateInternal(instance, cleanRules); + + return instance; + } + + /// + /// Generates a fake objects of type using the specified rules in + /// this . + /// + /// The number of items to create in the . + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + public virtual List Generate(int count, string ruleSets = null) + { + return Enumerable.Range(1, count) + .Select(i => Generate(ruleSets)) + .ToList(); + } + + /// + /// Returns an with LINQ deferred execution. Generated values + /// are not guaranteed to be repeatable until is called. + /// + /// The number of items to create in the . + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + public virtual IEnumerable GenerateLazy(int count, string ruleSets = null) + { + return Enumerable.Range(1, count) + .Select(i => Generate(ruleSets)); + } + + /// + /// Returns an that can be used as an unlimited source + /// of when iterated over. Useful for generating unlimited + /// amounts of data in a memory efficient way. Generated values *should* be repeatable + /// for a given seed when starting with the first item in the sequence. + /// + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + public virtual IEnumerable GenerateForever(string ruleSets = null) + { + while( true ) { - return Enumerable.Range(1, count) - .Select(i => Generate(ruleSets)); + yield return this.Generate(ruleSets); } + } - /// - /// Returns an that can be used as an unlimited source - /// of when iterated over. Useful for generating unlimited - /// amounts of data in a memory efficient way. Generated values *should* be repeatable - /// for a given seed when starting with the first item in the sequence. - /// - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - public virtual IEnumerable GenerateForever(string ruleSets = null) + /// + /// Populates an instance of according to the rules + /// defined in this . + /// + /// The instance of to populate. + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + public virtual void Populate(T instance, string ruleSets = null) + { + var cleanRules = ParseDirtyRulesSets(ruleSets); + PopulateInternal(instance, cleanRules); + } + + /// + /// Populates an instance of according to the rules + /// defined in this . + /// + /// The instance of to populate. + /// A comma separated list of rule sets to execute. + /// Note: The name `default` is the name of all rules defined without an explicit rule set. + /// When a custom rule set name is provided in as parameter, + /// the `default` rules will not run. If you want rules without an explicit rule set to run + /// you'll need to include the `default` rule set name in the comma separated + /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") + /// + protected virtual void PopulateInternal(T instance, string[] ruleSets) + { + ValidationResult vr = null; + if( !IsValid.HasValue ) { - while( true ) - { - yield return this.Generate(ruleSets); - } + //run validation + vr = ValidateInternal(ruleSets); + this.IsValid = vr.IsValid; } - - /// - /// Populates an instance of according to the rules - /// defined in this . - /// - /// The instance of to populate. - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - public virtual void Populate(T instance, string ruleSets = null) + if( !IsValid.GetValueOrDefault() ) { - var cleanRules = ParseDirtyRulesSets(ruleSets); - PopulateInternal(instance, cleanRules); + throw MakeValidationException(vr ?? ValidateInternal(ruleSets)); } - /// - /// Populates an instance of according to the rules - /// defined in this . - /// - /// The instance of to populate. - /// A comma separated list of rule sets to execute. - /// Note: The name `default` is the name of all rules defined without an explicit rule set. - /// When a custom rule set name is provided in as parameter, - /// the `default` rules will not run. If you want rules without an explicit rule set to run - /// you'll need to include the `default` rule set name in the comma separated - /// list of rules to run. (ex: "ruleSetA, ruleSetB, default") - /// - protected virtual void PopulateInternal(T instance, string[] ruleSets) + lock( Randomizer.Locker.Value ) { - ValidationResult vr = null; - if( !IsValid.HasValue ) - { - //run validation - vr = ValidateInternal(ruleSets); - this.IsValid = vr.IsValid; - } - if( !IsValid.GetValueOrDefault() ) - { - throw MakeValidationException(vr ?? ValidateInternal(ruleSets)); - } + //Issue 57 - Make sure you generate a new context + // before executing any rules. + //Issue 143 - If the FakerHub doesn't have any context + // (eg NewContext() has never been called), then call it + // so we can increment IndexGlobal and IndexFaker. + if( !this.FakerHub.HasContext ) FakerHub.NewContext(); - lock( Randomizer.Locker.Value ) + foreach( var ruleSet in ruleSets ) { - //Issue 57 - Make sure you generate a new context - // before executing any rules. - //Issue 143 - If the FakerHub doesn't have any context - // (eg NewContext() has never been called), then call it - // so we can increment IndexGlobal and IndexFaker. - if( !this.FakerHub.HasContext ) FakerHub.NewContext(); - - foreach( var ruleSet in ruleSets ) + if( this.Actions.TryGetValue(ruleSet, out var populateActions) ) { - if( this.Actions.TryGetValue(ruleSet, out var populateActions) ) + foreach( var action in populateActions.Values ) { - foreach( var action in populateActions.Values ) - { - PopulateProperty(instance, action); - } + PopulateProperty(instance, action); } } + } - foreach( var ruleSet in ruleSets ) + foreach( var ruleSet in ruleSets ) + { + if( this.FinalizeActions.TryGetValue(ruleSet, out FinalizeAction finalizer) ) { - if( this.FinalizeActions.TryGetValue(ruleSet, out FinalizeAction finalizer) ) - { - finalizer.Action(this.FakerHub, instance); - } + finalizer.Action(this.FakerHub, instance); } } } + } - private readonly object _setterCreateLock = new object(); - private void PopulateProperty(T instance, PopulateAction action) + private readonly object _setterCreateLock = new object(); + private void PopulateProperty(T instance, PopulateAction action) + { + var valueFactory = action.Action; + if (valueFactory is null) return; // An .Ignore() rule. + + var value = valueFactory(FakerHub, instance); + + if (SetterCache.TryGetValue(action.PropertyName, out var setter)) { - var valueFactory = action.Action; - if (valueFactory is null) return; // An .Ignore() rule. + setter(instance, value); + return; + } + + if (!TypeProperties.TryGetValue(action.PropertyName, out var member)) return; + if (member == null) return; // Member would be null if this was a .Rules() + // The valueFactory is already invoked + // which does not select a property or field. - var value = valueFactory(FakerHub, instance); - - if (SetterCache.TryGetValue(action.PropertyName, out var setter)) + lock (_setterCreateLock) + { + if (SetterCache.TryGetValue(action.PropertyName, out setter)) { setter(instance, value); return; } - - if (!TypeProperties.TryGetValue(action.PropertyName, out var member)) return; - if (member == null) return; // Member would be null if this was a .Rules() - // The valueFactory is already invoked - // which does not select a property or field. - lock (_setterCreateLock) - { - if (SetterCache.TryGetValue(action.PropertyName, out setter)) - { - setter(instance, value); - return; - } + if (member is PropertyInfo prop) + setter = prop.CreateSetter(); + // TODO FieldInfo will need to rely on ILEmit to create a delegate + else if (member is FieldInfo field) + setter = (i, v) => field?.SetValue(i, v); + if (setter == null) return; + + SetterCache.Add(action.PropertyName, setter); + setter(instance, value); + } + } + /// + /// When is enabled, checks if all properties or fields of have + /// rules defined. Returns true if all rules are defined, false otherwise. + /// The difference between and + /// is that will *not* throw + /// if some rules are missing when is enabled. + /// + /// True if validation passes, false otherwise. + public virtual bool Validate(string ruleSets = null) + { + var rules = ruleSets == null + ? this.Actions.Keys.ToArray() + : ParseDirtyRulesSets(ruleSets); + var result = ValidateInternal(rules); + return result.IsValid; + } - if (member is PropertyInfo prop) - setter = prop.CreateSetter(); - // TODO FieldInfo will need to rely on ILEmit to create a delegate - else if (member is FieldInfo field) - setter = (i, v) => field?.SetValue(i, v); - if (setter == null) return; - - SetterCache.Add(action.PropertyName, setter); - setter(instance, value); - } + /// + /// Asserts that all properties have rules. When is enabled, an exception will be raised + /// with complete list of missing rules. Useful in unit tests to catch missing rules at development + /// time. The difference between and + /// is that will throw + /// if some rules are missing when is enabled. + /// will not throw an exception and will return true or false accordingly if + /// rules are missing when is enabled. + /// + /// + public virtual void AssertConfigurationIsValid(string ruleSets = null) + { + string[] rules; + if( ruleSets is null ) + { + rules = this.Actions.Keys.ToArray(); } - /// - /// When is enabled, checks if all properties or fields of have - /// rules defined. Returns true if all rules are defined, false otherwise. - /// The difference between and - /// is that will *not* throw - /// if some rules are missing when is enabled. - /// - /// True if validation passes, false otherwise. - public virtual bool Validate(string ruleSets = null) + else { - var rules = ruleSets == null - ? this.Actions.Keys.ToArray() - : ParseDirtyRulesSets(ruleSets); - var result = ValidateInternal(rules); - return result.IsValid; + rules = ParseDirtyRulesSets(ruleSets); } - /// - /// Asserts that all properties have rules. When is enabled, an exception will be raised - /// with complete list of missing rules. Useful in unit tests to catch missing rules at development - /// time. The difference between and - /// is that will throw - /// if some rules are missing when is enabled. - /// will not throw an exception and will return true or false accordingly if - /// rules are missing when is enabled. - /// - /// - public virtual void AssertConfigurationIsValid(string ruleSets = null) + var result = ValidateInternal(rules); + if( !result.IsValid ) { - string[] rules; - if( ruleSets is null ) - { - rules = this.Actions.Keys.ToArray(); - } - else - { - rules = ParseDirtyRulesSets(ruleSets); - } + throw MakeValidationException(result); + } + } - var result = ValidateInternal(rules); - if( !result.IsValid ) + /// + /// Composes a based on the failed validation + /// results that can be readily used to raise the exception. + /// + protected virtual ValidationException MakeValidationException(ValidationResult result) + { + var builder = new StringBuilder(); + + result.ExtraMessages.ForEach(m => { - throw MakeValidationException(result); - } - } + builder.AppendLine(m); + builder.AppendLine(); + }); + + builder.AppendLine("Validation was called to ensure all properties / fields have rules.") + .AppendLine($"There are missing rules for Faker '{typeof(T).Name}'.") + .AppendLine("=========== Missing Rules ==========="); - /// - /// Composes a based on the failed validation - /// results that can be readily used to raise the exception. - /// - protected virtual ValidationException MakeValidationException(ValidationResult result) + foreach( var fieldOrProp in result.MissingRules ) { - var builder = new StringBuilder(); + builder.AppendLine(fieldOrProp); + } - result.ExtraMessages.ForEach(m => - { - builder.AppendLine(m); - builder.AppendLine(); - }); + return new ValidationException(builder.ToString().Trim()); + } - builder.AppendLine("Validation was called to ensure all properties / fields have rules.") - .AppendLine($"There are missing rules for Faker '{typeof(T).Name}'.") - .AppendLine("=========== Missing Rules ==========="); + private ValidationResult ValidateInternal(string[] ruleSets) + { + var result = new ValidationResult {IsValid = true}; - foreach( var fieldOrProp in result.MissingRules ) + var binderPropsOrFieldsOfT = this.TypeProperties.Keys; + foreach( var rule in ruleSets ) + { + if( this.StrictModes.TryGetValue(rule, out var strictMode) ) { - builder.AppendLine(fieldOrProp); } - - return new ValidationException(builder.ToString().Trim()); - } - - private ValidationResult ValidateInternal(string[] ruleSets) - { - var result = new ValidationResult {IsValid = true}; - - var binderPropsOrFieldsOfT = this.TypeProperties.Keys; - foreach( var rule in ruleSets ) + else { - if( this.StrictModes.TryGetValue(rule, out var strictMode) ) - { - } - else - { - strictMode = Faker.DefaultStrictMode; - } + strictMode = Faker.DefaultStrictMode; + } - //If strictMode is not enabled, skip and move on to the next ruleSet. - if( !strictMode ) continue; + //If strictMode is not enabled, skip and move on to the next ruleSet. + if( !strictMode ) continue; - this.Actions.TryGetValue(rule, out var populateActions); + this.Actions.TryGetValue(rule, out var populateActions); - var userSet = new HashSet(StringComparer.OrdinalIgnoreCase); + var userSet = new HashSet(StringComparer.OrdinalIgnoreCase); - if( populateActions != null ) - { - userSet.UnionWith(populateActions.Keys); - } + if( populateActions != null ) + { + userSet.UnionWith(populateActions.Keys); + } - //Get the set properties or fields that are only - //known to the binder, while removing - //items in userSet that are known to both the user and binder. + //Get the set properties or fields that are only + //known to the binder, while removing + //items in userSet that are known to both the user and binder. - userSet.SymmetricExceptWith(binderPropsOrFieldsOfT); + userSet.SymmetricExceptWith(binderPropsOrFieldsOfT); - //What's left in userSet is the set of properties or fields - //that the user does not know about + .Rule() methods. + //What's left in userSet is the set of properties or fields + //that the user does not know about + .Rule() methods. - if( userSet.Count > 0 ) + if( userSet.Count > 0 ) + { + foreach( var propOrFieldOfT in userSet ) { - foreach( var propOrFieldOfT in userSet ) + if( populateActions is not null && populateActions.TryGetValue(propOrFieldOfT, out var populateAction) ) { - if( populateActions is not null && populateActions.TryGetValue(propOrFieldOfT, out var populateAction) ) + // Very much a .Rules() action + if( populateAction.ProhibitInStrictMode ) { - // Very much a .Rules() action - if( populateAction.ProhibitInStrictMode ) - { - result.ExtraMessages.Add( - $"When StrictMode is set to True the Faker<{typeof(T).Name}>.Rules(...) method cannot verify that all properties have rules. You need to use Faker<{typeof(T).Name}>.RuleFor( x => x.Prop, ...) for each property to ensure each property has an associated rule when StrictMode is true; otherwise, set StrictMode to False in order to use Faker<{typeof(T).Name}>.Rules() method."); - result.IsValid = false; - } - } - else //The user doesn't know about this property or field. Log it as a validation error. - { - result.MissingRules.Add(propOrFieldOfT); + result.ExtraMessages.Add( + $"When StrictMode is set to True the Faker<{typeof(T).Name}>.Rules(...) method cannot verify that all properties have rules. You need to use Faker<{typeof(T).Name}>.RuleFor( x => x.Prop, ...) for each property to ensure each property has an associated rule when StrictMode is true; otherwise, set StrictMode to False in order to use Faker<{typeof(T).Name}>.Rules() method."); result.IsValid = false; } } + else //The user doesn't know about this property or field. Log it as a validation error. + { + result.MissingRules.Add(propOrFieldOfT); + result.IsValid = false; + } } } - return result; } + return result; + } - /// - /// Provides implicit type conversion from to . IE: Order testOrder = faker; - /// - public static implicit operator T(Faker faker) - { - return faker.Generate(); - } + /// + /// Provides implicit type conversion from to . IE: Order testOrder = faker; + /// + public static implicit operator T(Faker faker) + { + return faker.Generate(); + } - /// - /// Not Implemented: This method only exists as a work around for Visual Studio IntelliSense. See: https://github.com/bchavez/Bogus/issues/54 - /// - [Obsolete("This exists here only as a Visual Studio IntelliSense work around. See: https://github.com/bchavez/Bogus/issues/54", true)] - public void RuleFor(Expression> property) - { - throw new NotImplementedException(); - } + /// + /// Not Implemented: This method only exists as a work around for Visual Studio IntelliSense. See: https://github.com/bchavez/Bogus/issues/54 + /// + [Obsolete("This exists here only as a Visual Studio IntelliSense work around. See: https://github.com/bchavez/Bogus/issues/54", true)] + public void RuleFor(Expression> property) + { + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Source/Bogus/Hashids.cs b/Source/Bogus/Hashids.cs index 7df80d74..b195508e 100644 --- a/Source/Bogus/Hashids.cs +++ b/Source/Bogus/Hashids.cs @@ -4,412 +4,411 @@ using System.Text; using System.Text.RegularExpressions; -namespace Bogus +namespace Bogus; + +/// +/// Generate YouTube-like hashes from one or many numbers. Use hashids when you do not want to expose your database ids to the user. +/// +public class Hashids : IHashids { + public const string DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + public const string DEFAULT_SEPS = "cfhistuCFHISTU"; + + private const int MIN_ALPHABET_LENGTH = 16; + private const double SEP_DIV = 3.5; + private const double GUARD_DIV = 12.0; + + private string alphabet; + private string salt; + private string seps; + private string guards; + private int minHashLength; + + private Regex guardsRegex; + private Regex sepsRegex; + private static Regex hexValidator = new Regex("^[0-9a-fA-F]+$", RegexOptions.Compiled); + private static Regex hexSplitter = new Regex(@"[\w\W]{1,12}", RegexOptions.Compiled); + /// - /// Generate YouTube-like hashes from one or many numbers. Use hashids when you do not want to expose your database ids to the user. + /// Instantiates a new Hashids with the default setup. /// - public class Hashids : IHashids + public Hashids() : this(string.Empty, 0, DEFAULT_ALPHABET, DEFAULT_SEPS) { - public const string DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - public const string DEFAULT_SEPS = "cfhistuCFHISTU"; - - private const int MIN_ALPHABET_LENGTH = 16; - private const double SEP_DIV = 3.5; - private const double GUARD_DIV = 12.0; - - private string alphabet; - private string salt; - private string seps; - private string guards; - private int minHashLength; - - private Regex guardsRegex; - private Regex sepsRegex; - private static Regex hexValidator = new Regex("^[0-9a-fA-F]+$", RegexOptions.Compiled); - private static Regex hexSplitter = new Regex(@"[\w\W]{1,12}", RegexOptions.Compiled); - - /// - /// Instantiates a new Hashids with the default setup. - /// - public Hashids() : this(string.Empty, 0, DEFAULT_ALPHABET, DEFAULT_SEPS) - { - } + } - /// - /// Instantiates a new Hashids en/de-coder. - /// - /// - /// - /// - public Hashids(string salt = "", int minHashLength = 0, string alphabet = DEFAULT_ALPHABET, string seps = DEFAULT_SEPS) - { - if( string.IsNullOrWhiteSpace(alphabet) ) - throw new ArgumentNullException("alphabet"); + /// + /// Instantiates a new Hashids en/de-coder. + /// + /// + /// + /// + public Hashids(string salt = "", int minHashLength = 0, string alphabet = DEFAULT_ALPHABET, string seps = DEFAULT_SEPS) + { + if( string.IsNullOrWhiteSpace(alphabet) ) + throw new ArgumentNullException("alphabet"); - this.salt = salt; - this.alphabet = string.Join(string.Empty, alphabet.Distinct()); - this.seps = seps; - this.minHashLength = minHashLength; + this.salt = salt; + this.alphabet = string.Join(string.Empty, alphabet.Distinct()); + this.seps = seps; + this.minHashLength = minHashLength; - if( this.alphabet.Length < 16 ) - throw new ArgumentException("alphabet must contain at least 4 unique characters.", "alphabet"); + if( this.alphabet.Length < 16 ) + throw new ArgumentException("alphabet must contain at least 4 unique characters.", "alphabet"); - this.SetupSeps(); - this.SetupGuards(); - } + this.SetupSeps(); + this.SetupGuards(); + } - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers to encode - /// the hashed string - public virtual string Encode(params int[] numbers) - { - return this.GenerateHashFrom(numbers.Select(n => (long)n).ToArray()); - } + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers to encode + /// the hashed string + public virtual string Encode(params int[] numbers) + { + return this.GenerateHashFrom(numbers.Select(n => (long)n).ToArray()); + } - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers to encode - /// the hashed string - public virtual string Encode(IEnumerable numbers) - { - return this.Encode(numbers.ToArray()); - } + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers to encode + /// the hashed string + public virtual string Encode(IEnumerable numbers) + { + return this.Encode(numbers.ToArray()); + } + + /// + /// Decodes the provided hash into + /// + /// the hash + /// if the decoded number overflows integer + /// the numbers + public virtual int[] Decode(string hash) + { + return this.GetNumbersFrom(hash).Select(n => (int)n).ToArray(); + } - /// - /// Decodes the provided hash into - /// - /// the hash - /// if the decoded number overflows integer - /// the numbers - public virtual int[] Decode(string hash) + /// + /// Encodes the provided hex string to a hashids hash. + /// + /// + /// + public virtual string EncodeHex(string hex) + { + if( !hexValidator.IsMatch(hex) ) + return string.Empty; + + var numbers = new List(); + var matches = hexSplitter.Matches(hex); + + foreach( Match match in matches ) { - return this.GetNumbersFrom(hash).Select(n => (int)n).ToArray(); + var number = Convert.ToInt64(string.Concat("1", match.Value), 16); + numbers.Add(number); } - /// - /// Encodes the provided hex string to a hashids hash. - /// - /// - /// - public virtual string EncodeHex(string hex) - { - if( !hexValidator.IsMatch(hex) ) - return string.Empty; + return this.EncodeLong(numbers.ToArray()); + } - var numbers = new List(); - var matches = hexSplitter.Matches(hex); + /// + /// Decodes the provided hash into a hex-string + /// + /// + /// + public virtual string DecodeHex(string hash) + { + var ret = new StringBuilder(); + var numbers = this.DecodeLong(hash); - foreach( Match match in matches ) - { - var number = Convert.ToInt64(string.Concat("1", match.Value), 16); - numbers.Add(number); - } + foreach( var number in numbers ) + ret.Append(string.Format("{0:X}", number).Substring(1)); - return this.EncodeLong(numbers.ToArray()); - } + return ret.ToString(); + } - /// - /// Decodes the provided hash into a hex-string - /// - /// - /// - public virtual string DecodeHex(string hash) - { - var ret = new StringBuilder(); - var numbers = this.DecodeLong(hash); + /// + /// Decodes the provided hashed string into an array of longs + /// + /// the hashed string + /// the numbers + public long[] DecodeLong(string hash) + { + return this.GetNumbersFrom(hash); + } - foreach( var number in numbers ) - ret.Append(string.Format("{0:X}", number).Substring(1)); + /// + /// Encodes the provided longs to a hashed string + /// + /// the numbers + /// the hashed string + public string EncodeLong(params long[] numbers) + { + return this.GenerateHashFrom(numbers); + } - return ret.ToString(); - } + /// + /// Encodes the provided longs to a hashed string + /// + /// the numbers + /// the hashed string + public string EncodeLong(IEnumerable numbers) + { + return this.EncodeLong(numbers.ToArray()); + } - /// - /// Decodes the provided hashed string into an array of longs - /// - /// the hashed string - /// the numbers - public long[] DecodeLong(string hash) - { - return this.GetNumbersFrom(hash); - } + /// + /// Encodes the provided numbers into a string. + /// + /// the numbers + /// the hash + [Obsolete("Use 'Encode' instead. The method was renamed to better explain what it actually does.")] + public virtual string Encrypt(params int[] numbers) + { + return Encode(numbers); + } - /// - /// Encodes the provided longs to a hashed string - /// - /// the numbers - /// the hashed string - public string EncodeLong(params long[] numbers) - { - return this.GenerateHashFrom(numbers); - } + /// + /// Encrypts the provided hex string to a hashids hash. + /// + /// + /// + [Obsolete("Use 'EncodeHex' instead. The method was renamed to better explain what it actually does.")] + public virtual string EncryptHex(string hex) + { + return EncodeHex(hex); + } - /// - /// Encodes the provided longs to a hashed string - /// - /// the numbers - /// the hashed string - public string EncodeLong(IEnumerable numbers) - { - return this.EncodeLong(numbers.ToArray()); - } + /// + /// Decodes the provided numbers into a array of numbers + /// + /// hash + /// array of numbers. + [Obsolete("Use 'Decode' instead. Method was renamed to better explain what it actually does.")] + public virtual int[] Decrypt(string hash) + { + return Decode(hash); + } - /// - /// Encodes the provided numbers into a string. - /// - /// the numbers - /// the hash - [Obsolete("Use 'Encode' instead. The method was renamed to better explain what it actually does.")] - public virtual string Encrypt(params int[] numbers) - { - return Encode(numbers); - } + /// + /// Decodes the provided hash to a hex-string + /// + /// + /// + [Obsolete("Use 'DecodeHex' instead. The method was renamed to better explain what it actually does.")] + public virtual string DecryptHex(string hash) + { + return DecodeHex(hash); + } - /// - /// Encrypts the provided hex string to a hashids hash. - /// - /// - /// - [Obsolete("Use 'EncodeHex' instead. The method was renamed to better explain what it actually does.")] - public virtual string EncryptHex(string hex) - { - return EncodeHex(hex); - } + private void SetupSeps() + { + // seps should contain only characters present in alphabet; + seps = new String(seps.Intersect(alphabet.ToArray()).ToArray()); - /// - /// Decodes the provided numbers into a array of numbers - /// - /// hash - /// array of numbers. - [Obsolete("Use 'Decode' instead. Method was renamed to better explain what it actually does.")] - public virtual int[] Decrypt(string hash) - { - return Decode(hash); - } + // alphabet should not contain seps. + alphabet = new String(alphabet.Except(seps.ToArray()).ToArray()); - /// - /// Decodes the provided hash to a hex-string - /// - /// - /// - [Obsolete("Use 'DecodeHex' instead. The method was renamed to better explain what it actually does.")] - public virtual string DecryptHex(string hash) - { - return DecodeHex(hash); - } + seps = ConsistentShuffle(seps, salt); - private void SetupSeps() + if( seps.Length == 0 || (alphabet.Length / seps.Length) > SEP_DIV ) { - // seps should contain only characters present in alphabet; - seps = new String(seps.Intersect(alphabet.ToArray()).ToArray()); - - // alphabet should not contain seps. - alphabet = new String(alphabet.Except(seps.ToArray()).ToArray()); + var sepsLength = (int)Math.Ceiling(alphabet.Length / SEP_DIV); + if( sepsLength == 1 ) + sepsLength = 2; - seps = ConsistentShuffle(seps, salt); - - if( seps.Length == 0 || (alphabet.Length / seps.Length) > SEP_DIV ) + if( sepsLength > seps.Length ) { - var sepsLength = (int)Math.Ceiling(alphabet.Length / SEP_DIV); - if( sepsLength == 1 ) - sepsLength = 2; - - if( sepsLength > seps.Length ) - { - var diff = sepsLength - seps.Length; - seps += alphabet.Substring(0, diff); - alphabet = alphabet.Substring(diff); - } - - else seps = seps.Substring(0, sepsLength); + var diff = sepsLength - seps.Length; + seps += alphabet.Substring(0, diff); + alphabet = alphabet.Substring(diff); } - sepsRegex = new Regex(string.Concat("[", seps, "]"), RegexOptions.Compiled); - alphabet = ConsistentShuffle(alphabet, salt); + else seps = seps.Substring(0, sepsLength); } - private void SetupGuards() - { - var guardCount = (int)Math.Ceiling(alphabet.Length / GUARD_DIV); - - if( alphabet.Length < 3 ) - { - guards = seps.Substring(0, guardCount); - seps = seps.Substring(guardCount); - } + sepsRegex = new Regex(string.Concat("[", seps, "]"), RegexOptions.Compiled); + alphabet = ConsistentShuffle(alphabet, salt); + } - else - { - guards = alphabet.Substring(0, guardCount); - alphabet = alphabet.Substring(guardCount); - } + private void SetupGuards() + { + var guardCount = (int)Math.Ceiling(alphabet.Length / GUARD_DIV); - guardsRegex = new Regex(string.Concat("[", guards, "]"), RegexOptions.Compiled); + if( alphabet.Length < 3 ) + { + guards = seps.Substring(0, guardCount); + seps = seps.Substring(guardCount); } - /// - /// Internal function that does the work of creating the hash - /// - /// - /// - private string GenerateHashFrom(long[] numbers) + else { - if( numbers == null || numbers.Length == 0 ) - return string.Empty; + guards = alphabet.Substring(0, guardCount); + alphabet = alphabet.Substring(guardCount); + } - var ret = new StringBuilder(); - var alphabet = this.alphabet; + guardsRegex = new Regex(string.Concat("[", guards, "]"), RegexOptions.Compiled); + } - long numbersHashInt = 0; - for( var i = 0; i < numbers.Length; i++ ) - numbersHashInt += (int)(numbers[i] % (i + 100)); + /// + /// Internal function that does the work of creating the hash + /// + /// + /// + private string GenerateHashFrom(long[] numbers) + { + if( numbers == null || numbers.Length == 0 ) + return string.Empty; - var lottery = alphabet[(int)(numbersHashInt % alphabet.Length)]; - ret.Append(lottery.ToString()); + var ret = new StringBuilder(); + var alphabet = this.alphabet; - for( var i = 0; i < numbers.Length; i++ ) - { - var number = numbers[i]; - var buffer = lottery + this.salt + alphabet; + long numbersHashInt = 0; + for( var i = 0; i < numbers.Length; i++ ) + numbersHashInt += (int)(numbers[i] % (i + 100)); - alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length)); - var last = this.Hash(number, alphabet); + var lottery = alphabet[(int)(numbersHashInt % alphabet.Length)]; + ret.Append(lottery.ToString()); - ret.Append(last); + for( var i = 0; i < numbers.Length; i++ ) + { + var number = numbers[i]; + var buffer = lottery + this.salt + alphabet; - if( i + 1 < numbers.Length ) - { - number %= ((int)last[0] + i); - var sepsIndex = ((int)number % this.seps.Length); + alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length)); + var last = this.Hash(number, alphabet); - ret.Append(this.seps[sepsIndex]); - } - } + ret.Append(last); - if( ret.Length < this.minHashLength ) + if( i + 1 < numbers.Length ) { - var guardIndex = ((int)(numbersHashInt + (int)ret[0]) % this.guards.Length); - var guard = this.guards[guardIndex]; - - ret.Insert(0, guard); - - if( ret.Length < this.minHashLength ) - { - guardIndex = ((int)(numbersHashInt + (int)ret[2]) % this.guards.Length); - guard = this.guards[guardIndex]; - - ret.Append(guard); - } - } + number %= ((int)last[0] + i); + var sepsIndex = ((int)number % this.seps.Length); - var halfLength = (int)(alphabet.Length / 2); - while( ret.Length < this.minHashLength ) - { - alphabet = ConsistentShuffle(alphabet, alphabet); - ret.Insert(0, alphabet.Substring(halfLength)); - ret.Append(alphabet.Substring(0, halfLength)); - - var excess = ret.Length - this.minHashLength; - if( excess > 0 ) - { - ret.Remove(0, excess / 2); - ret.Remove(this.minHashLength, ret.Length - this.minHashLength); - } + ret.Append(this.seps[sepsIndex]); } - - return ret.ToString(); } - private string Hash(long input, string alphabet) + if( ret.Length < this.minHashLength ) { - var hash = new StringBuilder(); + var guardIndex = ((int)(numbersHashInt + (int)ret[0]) % this.guards.Length); + var guard = this.guards[guardIndex]; + + ret.Insert(0, guard); - do + if( ret.Length < this.minHashLength ) { - hash.Insert(0, alphabet[(int)(input % alphabet.Length)]); - input = (input / alphabet.Length); - } while( input > 0 ); + guardIndex = ((int)(numbersHashInt + (int)ret[2]) % this.guards.Length); + guard = this.guards[guardIndex]; - return hash.ToString(); + ret.Append(guard); + } } - private long Unhash(string input, string alphabet) + var halfLength = (int)(alphabet.Length / 2); + while( ret.Length < this.minHashLength ) { - long number = 0; + alphabet = ConsistentShuffle(alphabet, alphabet); + ret.Insert(0, alphabet.Substring(halfLength)); + ret.Append(alphabet.Substring(0, halfLength)); - for( var i = 0; i < input.Length; i++ ) + var excess = ret.Length - this.minHashLength; + if( excess > 0 ) { - var pos = alphabet.IndexOf(input[i]); - number += (long)(pos * Math.Pow(alphabet.Length, input.Length - i - 1)); + ret.Remove(0, excess / 2); + ret.Remove(this.minHashLength, ret.Length - this.minHashLength); } - - return number; } - private long[] GetNumbersFrom(string hash) - { - if( string.IsNullOrWhiteSpace(hash) ) - return new long[0]; + return ret.ToString(); + } - var alphabet = new string(this.alphabet.ToCharArray()); - var ret = new List(); - int i = 0; + private string Hash(long input, string alphabet) + { + var hash = new StringBuilder(); - var hashBreakdown = guardsRegex.Replace(hash, " "); - var hashArray = hashBreakdown.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); + do + { + hash.Insert(0, alphabet[(int)(input % alphabet.Length)]); + input = (input / alphabet.Length); + } while( input > 0 ); - if( hashArray.Length == 3 || hashArray.Length == 2 ) - i = 1; + return hash.ToString(); + } - hashBreakdown = hashArray[i]; - if( hashBreakdown[0] != default(char) ) - { - var lottery = hashBreakdown[0]; - hashBreakdown = hashBreakdown.Substring(1); + private long Unhash(string input, string alphabet) + { + long number = 0; - hashBreakdown = sepsRegex.Replace(hashBreakdown, " "); - hashArray = hashBreakdown.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); + for( var i = 0; i < input.Length; i++ ) + { + var pos = alphabet.IndexOf(input[i]); + number += (long)(pos * Math.Pow(alphabet.Length, input.Length - i - 1)); + } - for( var j = 0; j < hashArray.Length; j++ ) - { - var subHash = hashArray[j]; - var buffer = lottery + this.salt + alphabet; + return number; + } - alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length)); - ret.Add(Unhash(subHash, alphabet)); - } + private long[] GetNumbersFrom(string hash) + { + if( string.IsNullOrWhiteSpace(hash) ) + return new long[0]; - if( EncodeLong(ret.ToArray()) != hash ) - ret.Clear(); - } + var alphabet = new string(this.alphabet.ToCharArray()); + var ret = new List(); + int i = 0; - return ret.ToArray(); - } + var hashBreakdown = guardsRegex.Replace(hash, " "); + var hashArray = hashBreakdown.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - private string ConsistentShuffle(string alphabet, string salt) + if( hashArray.Length == 3 || hashArray.Length == 2 ) + i = 1; + + hashBreakdown = hashArray[i]; + if( hashBreakdown[0] != default(char) ) { - if( string.IsNullOrWhiteSpace(salt) ) - return alphabet; + var lottery = hashBreakdown[0]; + hashBreakdown = hashBreakdown.Substring(1); - int v, p, n, j; - v = p = n = j = 0; + hashBreakdown = sepsRegex.Replace(hashBreakdown, " "); + hashArray = hashBreakdown.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - for( var i = alphabet.Length - 1; i > 0; i--, v++ ) + for( var j = 0; j < hashArray.Length; j++ ) { - v %= salt.Length; - p += n = (int)salt[v]; - j = (n + v + p) % i; + var subHash = hashArray[j]; + var buffer = lottery + this.salt + alphabet; - var temp = alphabet[j]; - alphabet = alphabet.Substring(0, j) + alphabet[i] + alphabet.Substring(j + 1); - alphabet = alphabet.Substring(0, i) + temp + alphabet.Substring(i + 1); + alphabet = ConsistentShuffle(alphabet, buffer.Substring(0, alphabet.Length)); + ret.Add(Unhash(subHash, alphabet)); } + if( EncodeLong(ret.ToArray()) != hash ) + ret.Clear(); + } + + return ret.ToArray(); + } + + private string ConsistentShuffle(string alphabet, string salt) + { + if( string.IsNullOrWhiteSpace(salt) ) return alphabet; + + int v, p, n, j; + v = p = n = j = 0; + + for( var i = alphabet.Length - 1; i > 0; i--, v++ ) + { + v %= salt.Length; + p += n = (int)salt[v]; + j = (n + v + p) % i; + + var temp = alphabet[j]; + alphabet = alphabet.Substring(0, j) + alphabet[i] + alphabet.Substring(j + 1); + alphabet = alphabet.Substring(0, i) + temp + alphabet.Substring(i + 1); } + + return alphabet; } } \ No newline at end of file diff --git a/Source/Bogus/IHashids.cs b/Source/Bogus/IHashids.cs index acf229f6..860a96f2 100644 --- a/Source/Bogus/IHashids.cs +++ b/Source/Bogus/IHashids.cs @@ -1,67 +1,66 @@ using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Describes a Hashids provider +/// +public interface IHashids { /// - /// Describes a Hashids provider + /// Decodes the provided hashed string. /// - public interface IHashids - { - /// - /// Decodes the provided hashed string. - /// - /// the hashed string - /// if one or many of the numbers in the hash overflowing the integer storage - /// the numbers - int[] Decode(string hash); + /// the hashed string + /// if one or many of the numbers in the hash overflowing the integer storage + /// the numbers + int[] Decode(string hash); - /// - /// Decodes the provided hashed string into longs - /// - /// the hashed string - /// the numbers - long[] DecodeLong(string hash); + /// + /// Decodes the provided hashed string into longs + /// + /// the hashed string + /// the numbers + long[] DecodeLong(string hash); - /// - /// Decodes the provided hashed string into a hex string - /// - /// the hashed string - /// the hex string - string DecodeHex(string hash); + /// + /// Decodes the provided hashed string into a hex string + /// + /// the hashed string + /// the hex string + string DecodeHex(string hash); - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers - /// the hashed string - string Encode(params int[] numbers); + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers + /// the hashed string + string Encode(params int[] numbers); - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers - /// the hashed string - string Encode(IEnumerable numbers); + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers + /// the hashed string + string Encode(IEnumerable numbers); - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers - /// the hashed string - string EncodeLong(params long[] numbers); + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers + /// the hashed string + string EncodeLong(params long[] numbers); - /// - /// Encodes the provided numbers into a hashed string - /// - /// the numbers - /// the hashed string - string EncodeLong(IEnumerable numbers); + /// + /// Encodes the provided numbers into a hashed string + /// + /// the numbers + /// the hashed string + string EncodeLong(IEnumerable numbers); - /// - /// Encodes the provided hex string - /// - /// the hex string - /// the hashed string - string EncodeHex(string hex); - } + /// + /// Encodes the provided hex string + /// + /// the hex string + /// the hashed string + string EncodeHex(string hex); } \ No newline at end of file diff --git a/Source/Bogus/ILocaleAware.cs b/Source/Bogus/ILocaleAware.cs index 6beb2ae6..1f00400d 100644 --- a/Source/Bogus/ILocaleAware.cs +++ b/Source/Bogus/ILocaleAware.cs @@ -1,23 +1,22 @@ using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Marker interface for datasets that are locale aware. +/// +public interface ILocaleAware { /// - /// Marker interface for datasets that are locale aware. + /// The current locale for the dataset. /// - public interface ILocaleAware - { - /// - /// The current locale for the dataset. - /// - string Locale { get; set; } - } + string Locale { get; set; } +} - /// - /// Marker interface for objects that have a context storage property. - /// - public interface IHasContext - { - Dictionary Context { get; } - } +/// +/// Marker interface for objects that have a context storage property. +/// +public interface IHasContext +{ + Dictionary Context { get; } } \ No newline at end of file diff --git a/Source/Bogus/IRuleSet.cs b/Source/Bogus/IRuleSet.cs index 6f549ab4..2ff92fdd 100644 --- a/Source/Bogus/IRuleSet.cs +++ b/Source/Bogus/IRuleSet.cs @@ -1,59 +1,58 @@ using System; using System.Linq.Expressions; -namespace Bogus +namespace Bogus; + +/// +/// An interface for defining a set of rules. +/// +public interface IRuleSet where T : class { /// - /// An interface for defining a set of rules. - /// - public interface IRuleSet where T : class - { - /// - /// Uses the factory method to generate new instances. - /// - Faker CustomInstantiator(Func factoryMethod); - - /// - /// Creates a rule for a compound property and providing access to the instance being generated. - /// - Faker RuleFor(Expression> property, Func setter); - - /// - /// Creates a rule for a property. - /// - Faker RuleFor(Expression> property, Func setter); - - /// - /// Creates a rule for a property. - /// - Faker RuleFor(Expression> property, Func valueFunction); - - /// - /// Ignore a property or field when using StrictMode. - /// - Faker Ignore(Expression> propertyOrField); - - /// - /// Ensures all properties of T have rules. - /// - /// Overrides any global setting in Faker.DefaultStrictMode - Faker StrictMode(bool ensureRulesForAllProperties); - - /// - /// Action is invoked after all the rules are applied. - /// - Faker FinishWith(Action action); - - /// - /// Creates a rule for a property. - /// - Faker RuleFor(Expression> property, TProperty value); - - /// - /// Gives you a way to specify multiple rules inside an action - /// without having to call RuleFor multiple times. Note: StrictMode - /// must be false since property rules cannot be individually checked. - /// - Faker Rules(Action setActions); - } + /// Uses the factory method to generate new instances. + /// + Faker CustomInstantiator(Func factoryMethod); + + /// + /// Creates a rule for a compound property and providing access to the instance being generated. + /// + Faker RuleFor(Expression> property, Func setter); + + /// + /// Creates a rule for a property. + /// + Faker RuleFor(Expression> property, Func setter); + + /// + /// Creates a rule for a property. + /// + Faker RuleFor(Expression> property, Func valueFunction); + + /// + /// Ignore a property or field when using StrictMode. + /// + Faker Ignore(Expression> propertyOrField); + + /// + /// Ensures all properties of T have rules. + /// + /// Overrides any global setting in Faker.DefaultStrictMode + Faker StrictMode(bool ensureRulesForAllProperties); + + /// + /// Action is invoked after all the rules are applied. + /// + Faker FinishWith(Action action); + + /// + /// Creates a rule for a property. + /// + Faker RuleFor(Expression> property, TProperty value); + + /// + /// Gives you a way to specify multiple rules inside an action + /// without having to call RuleFor multiple times. Note: StrictMode + /// must be false since property rules cannot be individually checked. + /// + Faker Rules(Action setActions); } \ No newline at end of file diff --git a/Source/Bogus/Person.cs b/Source/Bogus/Person.cs index 40e626ad..a3622ea5 100644 --- a/Source/Bogus/Person.cs +++ b/Source/Bogus/Person.cs @@ -4,152 +4,151 @@ using System.Collections.Generic; using Bogus.DataSets; -namespace Bogus +namespace Bogus; + +/// +/// Uses Faker to generate a person with contextually relevant fields. +/// +public class Person : IHasRandomizer, IHasContext { - /// - /// Uses Faker to generate a person with contextually relevant fields. - /// - public class Person : IHasRandomizer, IHasContext - { - //context variable to store state from Bogus.Extensions so, they - //keep returning the result on each person. - internal Dictionary context = new Dictionary(); + //context variable to store state from Bogus.Extensions so, they + //keep returning the result on each person. + internal Dictionary context = new Dictionary(); - Dictionary IHasContext.Context => this.context; + Dictionary IHasContext.Context => this.context; - public class CardAddress + public class CardAddress + { + public class CardGeo { - public class CardGeo - { - public double Lat; - public double Lng; - } - - public string Street; - public string Suite; - public string City; - public string State; - public string ZipCode; - public CardGeo Geo; + public double Lat; + public double Lng; } - public class CardCompany - { - public string Name; - public string CatchPhrase; - public string Bs; - } + public string Street; + public string Suite; + public string City; + public string State; + public string ZipCode; + public CardGeo Geo; + } - protected Name DsName { get; set; } - protected Internet DsInternet { get; set; } - protected Date DsDate { get; set; } - protected PhoneNumbers DsPhoneNumbers { get; set; } - protected Address DsAddress { get; set; } - protected Company DsCompany { get; set; } - - /// - /// Creates a new Person object. - /// - /// The locale to use. Defaults to 'en'. - /// The seed used to generate person data. When a is specified, - /// the Randomizer.Seed global static is ignored and a locally isolated derived seed is used to derive randomness. - /// However, if the parameter is null, then the Randomizer.Seed global static is used to derive randomness. - /// - public Person(string locale = "en", int? seed = null) - { - this.GetDataSources(locale); - if( seed.HasValue ) - { - this.Random = new Randomizer(seed.Value); - } - this.Populate(); - } + public class CardCompany + { + public string Name; + public string CatchPhrase; + public string Bs; + } - internal Person(Randomizer randomizer, string locale = "en") - { - this.GetDataSources(locale); - this.Random = randomizer; - this.Populate(); - } + protected Name DsName { get; set; } + protected Internet DsInternet { get; set; } + protected Date DsDate { get; set; } + protected PhoneNumbers DsPhoneNumbers { get; set; } + protected Address DsAddress { get; set; } + protected Company DsCompany { get; set; } - private void GetDataSources(string locale) + /// + /// Creates a new Person object. + /// + /// The locale to use. Defaults to 'en'. + /// The seed used to generate person data. When a is specified, + /// the Randomizer.Seed global static is ignored and a locally isolated derived seed is used to derive randomness. + /// However, if the parameter is null, then the Randomizer.Seed global static is used to derive randomness. + /// + public Person(string locale = "en", int? seed = null) + { + this.GetDataSources(locale); + if( seed.HasValue ) { - this.DsName = this.Notifier.Flow(new Name(locale)); - this.DsInternet = this.Notifier.Flow(new Internet(locale)); - this.DsDate = this.Notifier.Flow(new Date {Locale = locale}); - this.DsPhoneNumbers = this.Notifier.Flow(new PhoneNumbers(locale)); - this.DsAddress = this.Notifier.Flow(new Address(locale)); - this.DsCompany = this.Notifier.Flow(new Company(locale)); + this.Random = new Randomizer(seed.Value); } + this.Populate(); + } - protected internal virtual void Populate() - { - this.Gender = this.Random.Enum(); - this.FirstName = this.DsName.FirstName(this.Gender); - this.LastName = this.DsName.LastName(this.Gender); - this.FullName = $"{this.FirstName} {this.LastName}"; - - this.UserName = this.DsInternet.UserName(this.FirstName, this.LastName); - this.Email = this.DsInternet.Email(this.FirstName, this.LastName); - this.Website = this.DsInternet.DomainName(); - this.Avatar = this.DsInternet.Avatar(); - - this.DateOfBirth = this.DsDate.Past(50, Date.SystemClock().AddYears(-20)); - - this.Phone = this.DsPhoneNumbers.PhoneNumber(); - - this.Address = new CardAddress - { - Street = this.DsAddress.StreetAddress(), - Suite = this.DsAddress.SecondaryAddress(), - City = this.DsAddress.City(), - State = this.DsAddress.State(), - ZipCode = this.DsAddress.ZipCode(), - Geo = new CardAddress.CardGeo - { - Lat = this.DsAddress.Latitude(), - Lng = this.DsAddress.Longitude() - } - }; - - this.Company = new CardCompany - { - Name = this.DsCompany.CompanyName(), - CatchPhrase = this.DsCompany.CatchPhrase(), - Bs = this.DsCompany.Bs() - }; - } + internal Person(Randomizer randomizer, string locale = "en") + { + this.GetDataSources(locale); + this.Random = randomizer; + this.Populate(); + } + + private void GetDataSources(string locale) + { + this.DsName = this.Notifier.Flow(new Name(locale)); + this.DsInternet = this.Notifier.Flow(new Internet(locale)); + this.DsDate = this.Notifier.Flow(new Date {Locale = locale}); + this.DsPhoneNumbers = this.Notifier.Flow(new PhoneNumbers(locale)); + this.DsAddress = this.Notifier.Flow(new Address(locale)); + this.DsCompany = this.Notifier.Flow(new Company(locale)); + } - protected SeedNotifier Notifier = new SeedNotifier(); + protected internal virtual void Populate() + { + this.Gender = this.Random.Enum(); + this.FirstName = this.DsName.FirstName(this.Gender); + this.LastName = this.DsName.LastName(this.Gender); + this.FullName = $"{this.FirstName} {this.LastName}"; - private Randomizer randomizer; + this.UserName = this.DsInternet.UserName(this.FirstName, this.LastName); + this.Email = this.DsInternet.Email(this.FirstName, this.LastName); + this.Website = this.DsInternet.DomainName(); + this.Avatar = this.DsInternet.Avatar(); - public Randomizer Random - { - get => this.randomizer ?? (this.Random = new Randomizer()); - set + this.DateOfBirth = this.DsDate.Past(50, Date.SystemClock().AddYears(-20)); + + this.Phone = this.DsPhoneNumbers.PhoneNumber(); + + this.Address = new CardAddress { - this.randomizer = value; - this.Notifier.Notify(value); - } - } + Street = this.DsAddress.StreetAddress(), + Suite = this.DsAddress.SecondaryAddress(), + City = this.DsAddress.City(), + State = this.DsAddress.State(), + ZipCode = this.DsAddress.ZipCode(), + Geo = new CardAddress.CardGeo + { + Lat = this.DsAddress.Latitude(), + Lng = this.DsAddress.Longitude() + } + }; + + this.Company = new CardCompany + { + Name = this.DsCompany.CompanyName(), + CatchPhrase = this.DsCompany.CatchPhrase(), + Bs = this.DsCompany.Bs() + }; + } + + protected SeedNotifier Notifier = new SeedNotifier(); - SeedNotifier IHasRandomizer.GetNotifier() + private Randomizer randomizer; + + public Randomizer Random + { + get => this.randomizer ?? (this.Random = new Randomizer()); + set { - return this.Notifier; + this.randomizer = value; + this.Notifier.Notify(value); } + } - public Name.Gender Gender; - public string FirstName; - public string LastName; - public string FullName; - public string UserName; - public string Avatar; - public string Email; - public DateTime DateOfBirth; - public CardAddress Address; - public string Phone; - public string Website; - public CardCompany Company; + SeedNotifier IHasRandomizer.GetNotifier() + { + return this.Notifier; } + + public Name.Gender Gender; + public string FirstName; + public string LastName; + public string FullName; + public string UserName; + public string Avatar; + public string Email; + public DateTime DateOfBirth; + public CardAddress Address; + public string Phone; + public string Website; + public CardCompany Company; } \ No newline at end of file diff --git a/Source/Bogus/Platform/ExtensionsForType.cs b/Source/Bogus/Platform/ExtensionsForType.cs index acfc4fdb..ad09dd5e 100644 --- a/Source/Bogus/Platform/ExtensionsForType.cs +++ b/Source/Bogus/Platform/ExtensionsForType.cs @@ -3,68 +3,67 @@ using System.Linq; using System.Reflection; -namespace Bogus.Platform +namespace Bogus.Platform; + +/// +/// Extension methods on . +/// +public static class ExtensionsForType { - /// - /// Extension methods on . - /// - public static class ExtensionsForType + public static T GetCustomAttributeX(this Type type) where T : Attribute { - public static T GetCustomAttributeX(this Type type) where T : Attribute - { #if STANDARD20 - return type.GetCustomAttribute(); + return type.GetCustomAttribute(); #elif STANDARD13 - return type.GetTypeInfo().GetCustomAttribute(); + return type.GetTypeInfo().GetCustomAttribute(); #else - return Attribute.GetCustomAttribute(type, typeof(T)) as T; + return Attribute.GetCustomAttribute(type, typeof(T)) as T; #endif - } + } - public static bool IsEnum(this Type type) - { + public static bool IsEnum(this Type type) + { #if STANDARD13 - return type.GetTypeInfo().IsEnum; + return type.GetTypeInfo().IsEnum; #else - return type.IsEnum; + return type.IsEnum; #endif - } + } - public static Assembly GetAssembly(this Type type) - { + public static Assembly GetAssembly(this Type type) + { #if STANDARD13 - return type.GetTypeInfo().Assembly; + return type.GetTypeInfo().Assembly; #else - return type.Assembly; + return type.Assembly; #endif - } + } - /// - /// Returns all the members of a type, based on . - /// - /// - /// For class types, it will simply call . - /// For interface types however, it will inspect *all* interfaces that implements, - /// and return all the members. - /// - /// The type to inspect. - /// The binding flags to use. - /// - /// The relevant members of - public static IEnumerable GetAllMembers(this Type type, BindingFlags bindingFlags) - { + /// + /// Returns all the members of a type, based on . + /// + /// + /// For class types, it will simply call . + /// For interface types however, it will inspect *all* interfaces that implements, + /// and return all the members. + /// + /// The type to inspect. + /// The binding flags to use. + /// + /// The relevant members of + public static IEnumerable GetAllMembers(this Type type, BindingFlags bindingFlags) + { #if NETSTANDARD1_3 - if (type.GetTypeInfo().IsInterface) + if (type.GetTypeInfo().IsInterface) #else - if (type.IsInterface) + if (type.IsInterface) #endif - { - return type.GetInterfaces().Union(new[] { type }).SelectMany(i => i.GetMembers(bindingFlags)).Distinct(); - } - else - { - return type.GetMembers(bindingFlags); - } + { + return type.GetInterfaces().Union(new[] { type }).SelectMany(i => i.GetMembers(bindingFlags)).Distinct(); + } + else + { + return type.GetMembers(bindingFlags); } } } \ No newline at end of file diff --git a/Source/Bogus/Premium/ContextHelper.cs b/Source/Bogus/Premium/ContextHelper.cs index 6534fe9f..8edb3593 100644 --- a/Source/Bogus/Premium/ContextHelper.cs +++ b/Source/Bogus/Premium/ContextHelper.cs @@ -2,31 +2,30 @@ using System; -namespace Bogus.Premium +namespace Bogus.Premium; + +public static class ContextHelper { - public static class ContextHelper + public static T GetOrSet(string key, Faker f, Func factory) where T : DataSet { - public static T GetOrSet(string key, Faker f, Func factory) where T : DataSet - { - var context = (f as IHasContext).Context; + var context = (f as IHasContext).Context; - if( context.TryGetValue(key, out var t) ) - { - return t as T; - } + if( context.TryGetValue(key, out var t) ) + { + return t as T; + } - var dataset = factory(); - var notifier = (f as IHasRandomizer).GetNotifier(); - notifier.Flow(dataset); + var dataset = factory(); + var notifier = (f as IHasRandomizer).GetNotifier(); + notifier.Flow(dataset); - context[key] = dataset; - return dataset; - } + context[key] = dataset; + return dataset; + } - public static T GetOrSet(Faker f, Func factory) where T : DataSet - { - var key = typeof(T).Name.ToLowerInvariant(); - return GetOrSet($"__{key}", f, factory); - } + public static T GetOrSet(Faker f, Func factory) where T : DataSet + { + var key = typeof(T).Name.ToLowerInvariant(); + return GetOrSet($"__{key}", f, factory); } } \ No newline at end of file diff --git a/Source/Bogus/Premium/License.cs b/Source/Bogus/Premium/License.cs index 663c212e..22239a9d 100644 --- a/Source/Bogus/Premium/License.cs +++ b/Source/Bogus/Premium/License.cs @@ -1,8 +1,7 @@ -namespace Bogus.Premium +namespace Bogus.Premium; + +public class License { - public class License - { - public static string LicenseTo { get; set; } - public static string LicenseKey { get; set; } - } + public static string LicenseTo { get; set; } + public static string LicenseKey { get; set; } } \ No newline at end of file diff --git a/Source/Bogus/Premium/LicenseVerifier.cs b/Source/Bogus/Premium/LicenseVerifier.cs index 07803fd7..f83034c4 100644 --- a/Source/Bogus/Premium/LicenseVerifier.cs +++ b/Source/Bogus/Premium/LicenseVerifier.cs @@ -8,103 +8,102 @@ using System.Text; using Bogus.Platform; -namespace Bogus.Premium +namespace Bogus.Premium; + +public static class LicenseVerifier { - public static class LicenseVerifier + public static bool VerifyLicense(string licenseTo, string licenseKey) { - public static bool VerifyLicense(string licenseTo, string licenseKey) - { - AssertKeyIsNotBanned(licenseKey); + AssertKeyIsNotBanned(licenseKey); - const string modulusString = - "vBgOPQiBhRR22ClUzIBJCmxcaOWfuAweUNpodRuZWDn8whviOe4JdA/sjzqw54KGh1qHJIc7JY5sGTCxNZQiSuyZQ6iHK2ykmU0Yb+QBvbqG33x2R7Di8MoNA1Tv2fX7SSny++IKEOQEEvwYhYr6oRU8sVItMcybUjiaaSw1rbU="; - const string exponentString = "AQAB"; + const string modulusString = + "vBgOPQiBhRR22ClUzIBJCmxcaOWfuAweUNpodRuZWDn8whviOe4JdA/sjzqw54KGh1qHJIc7JY5sGTCxNZQiSuyZQ6iHK2ykmU0Yb+QBvbqG33x2R7Di8MoNA1Tv2fX7SSny++IKEOQEEvwYhYr6oRU8sVItMcybUjiaaSw1rbU="; + const string exponentString = "AQAB"; - var data = Encoding.UTF8.GetBytes(licenseTo); + var data = Encoding.UTF8.GetBytes(licenseTo); - var rsaParameters = new RSAParameters - { - Modulus = Convert.FromBase64String(modulusString), - Exponent = Convert.FromBase64String(exponentString) - }; - var licenseData = Convert.FromBase64String(licenseKey); + var rsaParameters = new RSAParameters + { + Modulus = Convert.FromBase64String(modulusString), + Exponent = Convert.FromBase64String(exponentString) + }; + var licenseData = Convert.FromBase64String(licenseKey); #if STANDARD - using var rsa = RSA.Create(); - rsa.ImportParameters(rsaParameters); - return rsa.VerifyData(data, licenseData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + using var rsa = RSA.Create(); + rsa.ImportParameters(rsaParameters); + return rsa.VerifyData(data, licenseData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); #else - using var rsa = new RSACryptoServiceProvider(); - rsa.ImportParameters(rsaParameters); - return rsa.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), licenseData); + using var rsa = new RSACryptoServiceProvider(); + rsa.ImportParameters(rsaParameters); + return rsa.VerifyData(data, CryptoConfig.MapNameToOID("SHA256"), licenseData); #endif - } + } + + private static void AssertKeyIsNotBanned(string licenseKey) + { + } - private static void AssertKeyIsNotBanned(string licenseKey) + public const string LicenseFile = "Bogus.Premium.LicenseKey"; + + public static string FindLicense() + { + foreach (var probePath in ProbePaths) { + var licFile = FindLicense(probePath); + if (licFile != null) return licFile; } - public const string LicenseFile = "Bogus.Premium.LicenseKey"; + return null; + } - public static string FindLicense() - { - foreach (var probePath in ProbePaths) - { - var licFile = FindLicense(probePath); - if (licFile != null) return licFile; - } + public static string FindLicense(string probePath) + { + if (probePath.EndsWith(LicenseFile) && File.Exists(probePath)) return probePath; - return null; + string dir; + if (Directory.Exists(probePath)) + { + dir = probePath; + } + else + { + dir = Path.GetDirectoryName(probePath); } - public static string FindLicense(string probePath) + while (dir != null) { - if (probePath.EndsWith(LicenseFile) && File.Exists(probePath)) return probePath; + var licFile = Path.Combine(dir, LicenseFile); - string dir; - if (Directory.Exists(probePath)) - { - dir = probePath; - } - else + if (File.Exists(licFile)) { - dir = Path.GetDirectoryName(probePath); + return licFile; } - while (dir != null) - { - var licFile = Path.Combine(dir, LicenseFile); - - if (File.Exists(licFile)) - { - return licFile; - } + if (dir == Path.GetPathRoot(dir) || string.IsNullOrWhiteSpace(dir)) + break; - if (dir == Path.GetPathRoot(dir) || string.IsNullOrWhiteSpace(dir)) - break; - - dir = Path.GetFullPath(Path.Combine(dir, "..")); - } - - return null; + dir = Path.GetFullPath(Path.Combine(dir, "..")); } - public static void ReadLicense(string path, out string name, out string key) - { - var lines = File.ReadLines(path).Take(2).ToArray(); - name = lines[0]; - key = lines[1]; - } + return null; + } - public static List ProbePaths { get; } = new List - { + public static void ReadLicense(string path, out string name, out string key) + { + var lines = File.ReadLines(path).Take(2).ToArray(); + name = lines[0]; + key = lines[1]; + } + + public static List ProbePaths { get; } = new List + { #if STANDARD - AppContext.BaseDirectory, + AppContext.BaseDirectory, #endif #if !STANDARD13 - typeof(License).GetAssembly().Location, + typeof(License).GetAssembly().Location, #endif - Directory.GetCurrentDirectory() - }; + Directory.GetCurrentDirectory() + }; - } } \ No newline at end of file diff --git a/Source/Bogus/Premium/PremiumDataSet.cs b/Source/Bogus/Premium/PremiumDataSet.cs index a07df419..5a611753 100644 --- a/Source/Bogus/Premium/PremiumDataSet.cs +++ b/Source/Bogus/Premium/PremiumDataSet.cs @@ -3,75 +3,74 @@ using System.Reflection; using Bogus.Bson; -namespace Bogus.Premium +namespace Bogus.Premium; + +/// +/// Root object for premium data sets. +/// +public abstract class PremiumDataSet : DataSet { - /// - /// Root object for premium data sets. - /// - public abstract class PremiumDataSet : DataSet + protected internal override BValue Get(string path) { - protected internal override BValue Get(string path) - { - CheckLicense(); - return base.Get(path); - } + CheckLicense(); + return base.Get(path); + } - protected internal override BValue Get(string category, string path) - { - CheckLicense(); - return base.Get(category, path); - } + protected internal override BValue Get(string category, string path) + { + CheckLicense(); + return base.Get(category, path); + } - protected internal override bool HasKey(string path, bool includeFallback = true) - { - CheckLicense(); - return base.HasKey(path, includeFallback); - } + protected internal override bool HasKey(string path, bool includeFallback = true) + { + CheckLicense(); + return base.HasKey(path, includeFallback); + } - protected virtual void CheckLicense() + protected virtual void CheckLicense() + { + if( string.IsNullOrWhiteSpace(License.LicenseTo) || + string.IsNullOrWhiteSpace(License.LicenseKey)) { - if( string.IsNullOrWhiteSpace(License.LicenseTo) || - string.IsNullOrWhiteSpace(License.LicenseKey)) + var path = LicenseVerifier.FindLicense(); + if( path != null ) { - var path = LicenseVerifier.FindLicense(); - if( path != null ) - { - LicenseVerifier.ReadLicense(path, out var licenseTo, out var licenseKey); - License.LicenseTo = licenseTo; - License.LicenseKey = licenseKey; - } + LicenseVerifier.ReadLicense(path, out var licenseTo, out var licenseKey); + License.LicenseTo = licenseTo; + License.LicenseKey = licenseKey; } - if( !string.IsNullOrWhiteSpace(License.LicenseTo) && - !string.IsNullOrWhiteSpace(License.LicenseKey) && - LicenseVerifier.VerifyLicense(License.LicenseTo, License.LicenseKey) ) - { - this.Initialize(); - return; - } - - throw new BogusException( - "A premium license is required to use this API. " + - $"Please double check that your '{LicenseVerifier.LicenseFile}' file exists in the same folder as Bogus.dll. " + - $"Also, you can add additional probing paths for the license file in {nameof(LicenseVerifier)}.{nameof(LicenseVerifier.ProbePaths)}. " + - $"Lastly, you can set the following static properties manually: " + - $"{nameof(Bogus)}.{nameof(License)}.{nameof(License.LicenseTo)} and " + - $"{nameof(Bogus)}.{nameof(License)}.{nameof(License.LicenseKey)}. "+ - "For more information, please visit: https://github.com/bchavez/Bogus/wiki/Bogus-Premium"); } + if( !string.IsNullOrWhiteSpace(License.LicenseTo) && + !string.IsNullOrWhiteSpace(License.LicenseKey) && + LicenseVerifier.VerifyLicense(License.LicenseTo, License.LicenseKey) ) + { + this.Initialize(); + return; + } + + throw new BogusException( + "A premium license is required to use this API. " + + $"Please double check that your '{LicenseVerifier.LicenseFile}' file exists in the same folder as Bogus.dll. " + + $"Also, you can add additional probing paths for the license file in {nameof(LicenseVerifier)}.{nameof(LicenseVerifier.ProbePaths)}. " + + $"Lastly, you can set the following static properties manually: " + + $"{nameof(Bogus)}.{nameof(License)}.{nameof(License.LicenseTo)} and " + + $"{nameof(Bogus)}.{nameof(License)}.{nameof(License.LicenseKey)}. "+ + "For more information, please visit: https://github.com/bchavez/Bogus/wiki/Bogus-Premium"); + } - protected abstract void Initialize(); + protected abstract void Initialize(); - protected void LoadResource(Assembly asm, string resourceName) - { - var obj = ResourceHelper.ReadBObjectResource(asm, resourceName); - //patch - var enLocale = Database.GetLocale("en"); + protected void LoadResource(Assembly asm, string resourceName) + { + var obj = ResourceHelper.ReadBObjectResource(asm, resourceName); + //patch + var enLocale = Database.GetLocale("en"); - foreach( var val in obj.Keys ) - { - enLocale[val] = obj[val]; - } + foreach( var val in obj.Keys ) + { + enLocale[val] = obj[val]; } } } \ No newline at end of file diff --git a/Source/Bogus/PropertyName.cs b/Source/Bogus/PropertyName.cs index ad249c66..4be8abae 100644 --- a/Source/Bogus/PropertyName.cs +++ b/Source/Bogus/PropertyName.cs @@ -2,60 +2,59 @@ using System.ComponentModel; using System.Linq.Expressions; -namespace Bogus +namespace Bogus; + +[EditorBrowsable(EditorBrowsableState.Never)] +public static class PropertyName { - [EditorBrowsable(EditorBrowsableState.Never)] - public static class PropertyName + public static string For(Expression> expression) { - public static string For(Expression> expression) - { - Expression body = expression.Body; - return GetMemberName(body); - } + Expression body = expression.Body; + return GetMemberName(body); + } - public static string For(Expression> expression) - { - Expression body = expression.Body; - return GetMemberName(body); - } + public static string For(Expression> expression) + { + Expression body = expression.Body; + return GetMemberName(body); + } - public static string For(Expression> expression) - { - Expression body = expression.Body; - return GetMemberName(body); - } + public static string For(Expression> expression) + { + Expression body = expression.Body; + return GetMemberName(body); + } - public static string GetMemberName(Expression expression) + public static string GetMemberName(Expression expression) + { + var expressionString = expression.ToString(); + if( expressionString.IndexOf('.') != expressionString.LastIndexOf('.') ) { - var expressionString = expression.ToString(); - if( expressionString.IndexOf('.') != expressionString.LastIndexOf('.') ) - { - throw new ArgumentException( - $"Your expression '{expressionString}' cant be used. Nested accessors like 'o => o.NestedObject.Foo' at " + - $"a parent level are not allowed. You should create a dedicated faker for " + - $"NestedObject like new Faker().RuleFor(o => o.Foo, ...) with its own rules " + - $"that define how 'Foo' is generated. " + - "See this GitHub issue for more info: https://github.com/bchavez/Bogus/issues/115"); - } - - MemberExpression memberExpression; - - if( expression is UnaryExpression unary ) - //In this case the return type of the property was not object, - //so .Net wrapped the expression inside of a unary Convert() - //expression that casts it to type object. In this case, the - //Operand of the Convert expression has the original expression. - memberExpression = unary.Operand as MemberExpression; - else - //when the property is of type object the body itself is the - //correct expression - memberExpression = expression as MemberExpression; - - if( memberExpression == null ) - throw new ArgumentException( - "Expression was not of the form 'x => x.Property or x => x.Field'."); - - return memberExpression.Member.Name; + throw new ArgumentException( + $"Your expression '{expressionString}' cant be used. Nested accessors like 'o => o.NestedObject.Foo' at " + + $"a parent level are not allowed. You should create a dedicated faker for " + + $"NestedObject like new Faker().RuleFor(o => o.Foo, ...) with its own rules " + + $"that define how 'Foo' is generated. " + + "See this GitHub issue for more info: https://github.com/bchavez/Bogus/issues/115"); } + + MemberExpression memberExpression; + + if( expression is UnaryExpression unary ) + //In this case the return type of the property was not object, + //so .Net wrapped the expression inside of a unary Convert() + //expression that casts it to type object. In this case, the + //Operand of the Convert expression has the original expression. + memberExpression = unary.Operand as MemberExpression; + else + //when the property is of type object the body itself is the + //correct expression + memberExpression = expression as MemberExpression; + + if( memberExpression == null ) + throw new ArgumentException( + "Expression was not of the form 'x => x.Property or x => x.Field'."); + + return memberExpression.Member.Name; } } \ No newline at end of file diff --git a/Source/Bogus/Randomizer.cs b/Source/Bogus/Randomizer.cs index 76fe7c5c..73a96d74 100644 --- a/Source/Bogus/Randomizer.cs +++ b/Source/Bogus/Randomizer.cs @@ -7,938 +7,937 @@ using Bogus.DataSets; using Bogus.Platform; -namespace Bogus +namespace Bogus; + +/// +/// A randomizer that randomizes things. +/// +public class Randomizer { /// - /// A randomizer that randomizes things. + /// Set the random number generator manually with a seed to get reproducible results. /// - public class Randomizer - { - /// - /// Set the random number generator manually with a seed to get reproducible results. - /// - public static Random Seed = new Random(); + public static Random Seed = new Random(); - internal static Lazy Locker = new Lazy(() => new object(), LazyThreadSafetyMode.ExecutionAndPublication); + internal static Lazy Locker = new Lazy(() => new object(), LazyThreadSafetyMode.ExecutionAndPublication); - /// - /// Constructor that uses the global static `. - /// Changing the global static seed after this constructor runs - /// will have no effect. A new randomizer is needed to capture a new - /// global seed. - /// - public Randomizer() - { - this.localSeed = Seed; - } + /// + /// Constructor that uses the global static `. + /// Changing the global static seed after this constructor runs + /// will have no effect. A new randomizer is needed to capture a new + /// global seed. + /// + public Randomizer() + { + this.localSeed = Seed; + } - /// - /// Constructor that uses parameter as a seed. - /// Completely ignores the global static . - /// - public Randomizer(int localSeed) - { - this.localSeed = new Random(localSeed); - } + /// + /// Constructor that uses parameter as a seed. + /// Completely ignores the global static . + /// + public Randomizer(int localSeed) + { + this.localSeed = new Random(localSeed); + } - /// - /// The pseudo-random number generator that is used for all random number generation in this instance. - /// - protected Random localSeed; + /// + /// The pseudo-random number generator that is used for all random number generation in this instance. + /// + protected Random localSeed; - /// - /// Get an int from 0 to max. - /// - /// Upper bound, inclusive. - public int Number(int max) - { - return Number(0, max); - } + /// + /// Get an int from 0 to max. + /// + /// Upper bound, inclusive. + public int Number(int max) + { + return Number(0, max); + } - /// - /// Get a random sequence of digits. - /// - /// How many - /// minimum digit, inclusive - /// maximum digit, inclusive - public int[] Digits(int count, int minDigit = 0, int maxDigit = 9) - { - if( maxDigit > 9 || maxDigit < 0 ) throw new ArgumentException("max digit can't be lager than 9 or smaller than 0", nameof(maxDigit)); - if( minDigit > 9 || minDigit < 0 ) throw new ArgumentException("min digit can't be lager than 9 or smaller than 0", nameof(minDigit)); + /// + /// Get a random sequence of digits. + /// + /// How many + /// minimum digit, inclusive + /// maximum digit, inclusive + public int[] Digits(int count, int minDigit = 0, int maxDigit = 9) + { + if( maxDigit > 9 || maxDigit < 0 ) throw new ArgumentException("max digit can't be lager than 9 or smaller than 0", nameof(maxDigit)); + if( minDigit > 9 || minDigit < 0 ) throw new ArgumentException("min digit can't be lager than 9 or smaller than 0", nameof(minDigit)); - var digits = new int[count]; - for( var i = 0; i < count; i++ ) - { - digits[i] = Number(min: minDigit, max: maxDigit); - } - return digits; + var digits = new int[count]; + for( var i = 0; i < count; i++ ) + { + digits[i] = Number(min: minDigit, max: maxDigit); } + return digits; + } - /// - /// Get an int from min to max. - /// - /// Lower bound, inclusive - /// Upper bound, inclusive - public int Number(int min = 0, int max = 1) + /// + /// Get an int from min to max. + /// + /// Lower bound, inclusive + /// Upper bound, inclusive + public int Number(int min = 0, int max = 1) + { + //lock any seed access, for thread safety. + lock( Locker.Value ) { - //lock any seed access, for thread safety. - lock( Locker.Value ) - { - // Adjust the range as needed to make max inclusive. The Random.Next function uses exclusive upper bounds. + // Adjust the range as needed to make max inclusive. The Random.Next function uses exclusive upper bounds. - // If max can be extended by 1, just do that. - if( max < int.MaxValue ) return localSeed.Next(min, max + 1); + // If max can be extended by 1, just do that. + if( max < int.MaxValue ) return localSeed.Next(min, max + 1); - // If max is exactly int.MaxValue, then check if min can be used to push the range out by one the other way. - // If so, then we can simply add one to the result to put it back in the correct range. - if( min > int.MinValue ) return 1 + localSeed.Next(min - 1, max); + // If max is exactly int.MaxValue, then check if min can be used to push the range out by one the other way. + // If so, then we can simply add one to the result to put it back in the correct range. + if( min > int.MinValue ) return 1 + localSeed.Next(min - 1, max); - // If we hit this line, then min is int.MinValue and max is int.MaxValue, which mean the caller wants a - // number from a range spanning all possible values of int. The Random class only supports exclusive - // upper bounds, period, and the upper bound must be specified as an int, so the best we can get in a - // single call is a value in the range (int.MinValue, int.MaxValue - 1). Instead, what we do is get two - // samples, each of which has just under 31 bits of entropy, and use 16 bits from each to assemble a - // single 16-bit number. - int sample1 = localSeed.Next(); - int sample2 = localSeed.Next(); + // If we hit this line, then min is int.MinValue and max is int.MaxValue, which mean the caller wants a + // number from a range spanning all possible values of int. The Random class only supports exclusive + // upper bounds, period, and the upper bound must be specified as an int, so the best we can get in a + // single call is a value in the range (int.MinValue, int.MaxValue - 1). Instead, what we do is get two + // samples, each of which has just under 31 bits of entropy, and use 16 bits from each to assemble a + // single 16-bit number. + int sample1 = localSeed.Next(); + int sample2 = localSeed.Next(); - int topHalf = (sample1 >> 8) & 0xFFFF; - int bottomHalf = (sample2 >> 8) & 0xFFFF; + int topHalf = (sample1 >> 8) & 0xFFFF; + int bottomHalf = (sample2 >> 8) & 0xFFFF; - return unchecked((topHalf << 16) | bottomHalf); - } + return unchecked((topHalf << 16) | bottomHalf); } + } - /// - /// Returns a random even number. If the range does not contain any even numbers, an is thrown. - /// - /// Lower bound, inclusive - /// Upper bound, inclusive - /// Thrown if it is impossible to select an odd number satisfying the specified range. - public int Even(int min = 0, int max = 1) - { - // Ensure that we have a valid range. - if( min > max ) - throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", nameof(max)); - if( ((min & 1) == 1) && (max - 1 < min) ) - throw new ArgumentException("The specified range does not contain any even numbers.", nameof(max)); - - // Adjust the range to ensure that we always get the same number of even values as odd values. - // For example, - // if the input is min = 1, max = 3, the new range should be min = 2, max = 3. - // if the input is min = 2, max = 3, the range should remain min = 2, max = 3. - min = (min + 1) & ~1; - max = max | 1; - - if( min > max ) - return min; - - // Strip off the last bit of a random number to make the number even. - return Number(min, max) & ~1; - } + /// + /// Returns a random even number. If the range does not contain any even numbers, an is thrown. + /// + /// Lower bound, inclusive + /// Upper bound, inclusive + /// Thrown if it is impossible to select an odd number satisfying the specified range. + public int Even(int min = 0, int max = 1) + { + // Ensure that we have a valid range. + if( min > max ) + throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", nameof(max)); + if( ((min & 1) == 1) && (max - 1 < min) ) + throw new ArgumentException("The specified range does not contain any even numbers.", nameof(max)); + + // Adjust the range to ensure that we always get the same number of even values as odd values. + // For example, + // if the input is min = 1, max = 3, the new range should be min = 2, max = 3. + // if the input is min = 2, max = 3, the range should remain min = 2, max = 3. + min = (min + 1) & ~1; + max = max | 1; + + if( min > max ) + return min; + + // Strip off the last bit of a random number to make the number even. + return Number(min, max) & ~1; + } - /// - /// Returns a random odd number. If the range does not contain any odd numbers, an is thrown. - /// - /// Lower bound, inclusive - /// Upper bound, inclusive - /// Thrown if it is impossible to select an odd number satisfying the specified range. - public int Odd(int min = 0, int max = 1) - { - // Ensure that we have a valid range. - if( min > max ) - throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", nameof(max)); - if( ((max & 1) == 0) && (min + 1 > max) ) - throw new ArgumentException("The specified range does not contain any odd numbers.", nameof(max)); - - // Special case where the math below breaks. - if ( max == int.MinValue ) - return int.MinValue | 1; - - // Adjust the range to ensure that we always get the same number of even values as odd values. - // For example, - // if the input is min = 2, max = 4, the new range should be min = 2, max = 3. - // if the input is min = 2, max = 3, the range should remain min = 2, max = 3. - min = min & ~1; - max = (max - 1) | 1; - - if( min > max ) - return min | 1; - - // Ensure that the last bit is set in a random number to make the number odd. - return Number(min, max) | 1; - } + /// + /// Returns a random odd number. If the range does not contain any odd numbers, an is thrown. + /// + /// Lower bound, inclusive + /// Upper bound, inclusive + /// Thrown if it is impossible to select an odd number satisfying the specified range. + public int Odd(int min = 0, int max = 1) + { + // Ensure that we have a valid range. + if( min > max ) + throw new ArgumentException($"The min/max range is invalid. The minimum value '{min}' is greater than the maximum value '{max}'.", nameof(max)); + if( ((max & 1) == 0) && (min + 1 > max) ) + throw new ArgumentException("The specified range does not contain any odd numbers.", nameof(max)); + + // Special case where the math below breaks. + if ( max == int.MinValue ) + return int.MinValue | 1; + + // Adjust the range to ensure that we always get the same number of even values as odd values. + // For example, + // if the input is min = 2, max = 4, the new range should be min = 2, max = 3. + // if the input is min = 2, max = 3, the range should remain min = 2, max = 3. + min = min & ~1; + max = (max - 1) | 1; + + if( min > max ) + return min | 1; + + // Ensure that the last bit is set in a random number to make the number odd. + return Number(min, max) | 1; + } - /// - /// Get a random double, between 0.0 and 1.0. - /// - /// Minimum, default 0.0 - /// Maximum, default 1.0 - public double Double(double min = 0.0d, double max = 1.0d) + /// + /// Get a random double, between 0.0 and 1.0. + /// + /// Minimum, default 0.0 + /// Maximum, default 1.0 + public double Double(double min = 0.0d, double max = 1.0d) + { + //lock any seed access, for thread safety. + lock( Locker.Value ) { - //lock any seed access, for thread safety. - lock( Locker.Value ) + if( min == 0.0d && max == 1.0d ) { - if( min == 0.0d && max == 1.0d ) - { - //use default implementation - return localSeed.NextDouble(); - } - - return localSeed.NextDouble() * (max - min) + min; + //use default implementation + return localSeed.NextDouble(); } - } - /// - /// Get a random decimal, between 0.0 and 1.0. - /// - /// Minimum, default 0.0 - /// Maximum, default 1.0 - public decimal Decimal(decimal min = 0.0m, decimal max = 1.0m) - { - return Convert.ToDecimal(Double()) * (max - min) + min; + return localSeed.NextDouble() * (max - min) + min; } + } - /// - /// Get a random float, between 0.0 and 1.0. - /// - /// Minimum, default 0.0 - /// Maximum, default 1.0 - public float Float(float min = 0.0f, float max = 1.0f) - { - return Convert.ToSingle(Double() * (max - min) + min); - } + /// + /// Get a random decimal, between 0.0 and 1.0. + /// + /// Minimum, default 0.0 + /// Maximum, default 1.0 + public decimal Decimal(decimal min = 0.0m, decimal max = 1.0m) + { + return Convert.ToDecimal(Double()) * (max - min) + min; + } - /// - /// Generate a random byte between 0 and 255. - /// - /// Min value, default byte.MinValue 0 - /// Max value, default byte.MaxValue 255 - public byte Byte(byte min = byte.MinValue, byte max = byte.MaxValue) - { - return Convert.ToByte(Number(min, max)); - } + /// + /// Get a random float, between 0.0 and 1.0. + /// + /// Minimum, default 0.0 + /// Maximum, default 1.0 + public float Float(float min = 0.0f, float max = 1.0f) + { + return Convert.ToSingle(Double() * (max - min) + min); + } - /// - /// Get a random sequence of bytes. - /// - /// The size of the byte array - public byte[] Bytes(int count) - { - var arr = new byte[count]; - lock( Locker.Value ) - { - localSeed.NextBytes(arr); - } - return arr; - } + /// + /// Generate a random byte between 0 and 255. + /// + /// Min value, default byte.MinValue 0 + /// Max value, default byte.MaxValue 255 + public byte Byte(byte min = byte.MinValue, byte max = byte.MaxValue) + { + return Convert.ToByte(Number(min, max)); + } - /// - /// Generate a random sbyte between -128 and 127. - /// - /// Min value, default sbyte.MinValue -128 - /// Max value, default sbyte.MaxValue 127 - public sbyte SByte(sbyte min = sbyte.MinValue, sbyte max = sbyte.MaxValue) + /// + /// Get a random sequence of bytes. + /// + /// The size of the byte array + public byte[] Bytes(int count) + { + var arr = new byte[count]; + lock( Locker.Value ) { - return Convert.ToSByte(Number(min, max)); + localSeed.NextBytes(arr); } + return arr; + } - /// - /// Generate a random int between MinValue and MaxValue. - /// - /// Min value, default int.MinValue - /// Max value, default int.MaxValue - public int Int(int min = int.MinValue, int max = int.MaxValue) - { - return this.Number(min, max); - } + /// + /// Generate a random sbyte between -128 and 127. + /// + /// Min value, default sbyte.MinValue -128 + /// Max value, default sbyte.MaxValue 127 + public sbyte SByte(sbyte min = sbyte.MinValue, sbyte max = sbyte.MaxValue) + { + return Convert.ToSByte(Number(min, max)); + } - /// - /// Generate a random uint between MinValue and MaxValue. - /// - /// Min value, default uint.MinValue - /// Max value, default uint.MaxValue - public uint UInt(uint min = uint.MinValue, uint max = uint.MaxValue) - { - return Convert.ToUInt32(Double() * (max - min) + min); - } + /// + /// Generate a random int between MinValue and MaxValue. + /// + /// Min value, default int.MinValue + /// Max value, default int.MaxValue + public int Int(int min = int.MinValue, int max = int.MaxValue) + { + return this.Number(min, max); + } - /// - /// Generate a random ulong between MinValue and MaxValue. - /// - /// Min value, default ulong.MinValue - /// Max value, default ulong.MaxValue - public ulong ULong(ulong min = ulong.MinValue, ulong max = ulong.MaxValue) - { - return Convert.ToUInt64(Double() * (max - min) + min); - } + /// + /// Generate a random uint between MinValue and MaxValue. + /// + /// Min value, default uint.MinValue + /// Max value, default uint.MaxValue + public uint UInt(uint min = uint.MinValue, uint max = uint.MaxValue) + { + return Convert.ToUInt32(Double() * (max - min) + min); + } - /// - /// Generate a random long between MinValue and MaxValue. - /// - /// Min value, default long.MinValue - /// Max value, default long.MaxValue - public long Long(long min = long.MinValue, long max = long.MaxValue) - { - var range = (decimal)max - min; //use more bits? - return Convert.ToInt64((decimal)Double() * range + min); - } + /// + /// Generate a random ulong between MinValue and MaxValue. + /// + /// Min value, default ulong.MinValue + /// Max value, default ulong.MaxValue + public ulong ULong(ulong min = ulong.MinValue, ulong max = ulong.MaxValue) + { + return Convert.ToUInt64(Double() * (max - min) + min); + } - /// - /// Generate a random short between MinValue and MaxValue. - /// - /// Min value, default short.MinValue -32768 - /// Max value, default short.MaxValue 32767 - public short Short(short min = short.MinValue, short max = short.MaxValue) - { - return Convert.ToInt16(Double() * (max - min) + min); - } + /// + /// Generate a random long between MinValue and MaxValue. + /// + /// Min value, default long.MinValue + /// Max value, default long.MaxValue + public long Long(long min = long.MinValue, long max = long.MaxValue) + { + var range = (decimal)max - min; //use more bits? + return Convert.ToInt64((decimal)Double() * range + min); + } - /// - /// Generate a random ushort between MinValue and MaxValue. - /// - /// Min value, default ushort.MinValue 0 - /// Max value, default ushort.MaxValue 65535 - public ushort UShort(ushort min = ushort.MinValue, ushort max = ushort.MaxValue) - { - return Convert.ToUInt16(Double() * (max - min) + min); - } + /// + /// Generate a random short between MinValue and MaxValue. + /// + /// Min value, default short.MinValue -32768 + /// Max value, default short.MaxValue 32767 + public short Short(short min = short.MinValue, short max = short.MaxValue) + { + return Convert.ToInt16(Double() * (max - min) + min); + } - /// - /// Generate a random char between MinValue and MaxValue. - /// - /// Min value, default char.MinValue - /// Max value, default char.MaxValue - public char Char(char min = char.MinValue, char max = char.MaxValue) - { - return Convert.ToChar(Number(min, max)); - } + /// + /// Generate a random ushort between MinValue and MaxValue. + /// + /// Min value, default ushort.MinValue 0 + /// Max value, default ushort.MaxValue 65535 + public ushort UShort(ushort min = ushort.MinValue, ushort max = ushort.MaxValue) + { + return Convert.ToUInt16(Double() * (max - min) + min); + } - /// - /// Generate a random chars between MinValue and MaxValue. - /// - /// Min value, default char.MinValue - /// Max value, default char.MaxValue - /// The length of chars to return - public char[] Chars(char min = char.MinValue, char max = char.MaxValue, int count = 5) - { - var arr = new char[count]; - for( var i = 0; i < count; i++ ) - arr[i] = Char(min, max); - return arr; - } + /// + /// Generate a random char between MinValue and MaxValue. + /// + /// Min value, default char.MinValue + /// Max value, default char.MaxValue + public char Char(char min = char.MinValue, char max = char.MaxValue) + { + return Convert.ToChar(Number(min, max)); + } - /// - /// Get a string of characters of a specific length. - /// Uses . - /// Note: This method can return ill-formed UTF16 Unicode strings with unpaired surrogates. - /// Use for technically valid Unicode. - /// - /// The exact length of the result string. If null, a random length is chosen between 40 and 80. - /// Min character value, default char.MinValue - /// Max character value, default char.MaxValue - public string String(int? length = null, char minChar = char.MinValue, char maxChar = char.MaxValue) - { - var l = length ?? this.Number(40, 80); + /// + /// Generate a random chars between MinValue and MaxValue. + /// + /// Min value, default char.MinValue + /// Max value, default char.MaxValue + /// The length of chars to return + public char[] Chars(char min = char.MinValue, char max = char.MaxValue, int count = 5) + { + var arr = new char[count]; + for( var i = 0; i < count; i++ ) + arr[i] = Char(min, max); + return arr; + } - return new string(Chars(minChar, maxChar, l)); - } + /// + /// Get a string of characters of a specific length. + /// Uses . + /// Note: This method can return ill-formed UTF16 Unicode strings with unpaired surrogates. + /// Use for technically valid Unicode. + /// + /// The exact length of the result string. If null, a random length is chosen between 40 and 80. + /// Min character value, default char.MinValue + /// Max character value, default char.MaxValue + public string String(int? length = null, char minChar = char.MinValue, char maxChar = char.MaxValue) + { + var l = length ?? this.Number(40, 80); + + return new string(Chars(minChar, maxChar, l)); + } - /// - /// Get a string of characters between and . - /// Uses . - /// Note: This method can return ill-formed UTF16 Unicode strings with unpaired surrogates. - /// Use for technically valid Unicode. - /// - /// Lower-bound string length. Inclusive. - /// Upper-bound string length. Inclusive. - /// Min character value, default char.MinValue - /// Max character value, default char.MaxValue - public string String(int minLength, int maxLength, char minChar = char.MinValue, char maxChar = char.MaxValue) + /// + /// Get a string of characters between and . + /// Uses . + /// Note: This method can return ill-formed UTF16 Unicode strings with unpaired surrogates. + /// Use for technically valid Unicode. + /// + /// Lower-bound string length. Inclusive. + /// Upper-bound string length. Inclusive. + /// Min character value, default char.MinValue + /// Max character value, default char.MaxValue + public string String(int minLength, int maxLength, char minChar = char.MinValue, char maxChar = char.MaxValue) + { + var length = this.Number(minLength, maxLength); + return String(length, minChar, maxChar); + } + + /// + /// Get a string of characters with a specific length drawing characters from . + /// The returned string may contain repeating characters from the string. + /// + /// The length of the string to return. + /// The pool of characters to draw from. The returned string may contain repeat characters from the pool. + public string String2(int length, string chars = "abcdefghijklmnopqrstuvwxyz") + { + var target = new char[length]; + + for (int i = 0; i < length; i++) { - var length = this.Number(minLength, maxLength); - return String(length, minChar, maxChar); + var idx = this.Number(0, chars.Length - 1); + target[i] = chars[idx]; } - /// - /// Get a string of characters with a specific length drawing characters from . - /// The returned string may contain repeating characters from the string. - /// - /// The length of the string to return. - /// The pool of characters to draw from. The returned string may contain repeat characters from the pool. - public string String2(int length, string chars = "abcdefghijklmnopqrstuvwxyz") + return new string(target); + } + + /// + /// Get a string of characters with a specific length drawing characters from . + /// The returned string may contain repeating characters from the string. + /// + /// The minimum length of the string to return. + /// The maximum length of the string to return. + /// The pool of characters to draw from. The returned string may contain repeat characters from the pool. + public string String2(int minLength, int maxLength, string chars = "abcdefghijklmnopqrstuvwxyz") + { + var length = this.Number(minLength, maxLength); + return String2(length, chars); + } + + /// + /// Get a string of valid UTF16 Unicode characters. + /// This method returns a string where each character IsLetterOrDigit() is true. + /// + /// The minimum length of the string to return. + /// The maximum length of the string to return. + /// Excludes surrogate pairs from the returned string. + public string Utf16String(int minLength = 40, int maxLength = 80, bool excludeSurrogates = false) + { + var targetLength = minLength == maxLength ? minLength : this.Number(minLength, maxLength); + + var sb = new StringBuilder(); + + while( sb.Length < targetLength) { - var target = new char[length]; + int spaceLeft = targetLength - sb.Length; + string block = null; + int alignment = 0; - for (int i = 0; i < length; i++) + if (!excludeSurrogates && spaceLeft >= 2 && this.Bool()) + { + block = this.ArrayElement(SafeUnicodeRanges.SurrogatePairs); + alignment = 1; + } + else { - var idx = this.Number(0, chars.Length - 1); - target[i] = chars[idx]; + block = this.ArrayElement(SafeUnicodeRanges.Basic); + alignment = 0; } - return new string(target); - } + char rangeStart = block[alignment]; + char rangeEnd = block[2 + alignment * 2]; - /// - /// Get a string of characters with a specific length drawing characters from . - /// The returned string may contain repeating characters from the string. - /// - /// The minimum length of the string to return. - /// The maximum length of the string to return. - /// The pool of characters to draw from. The returned string may contain repeat characters from the pool. - public string String2(int minLength, int maxLength, string chars = "abcdefghijklmnopqrstuvwxyz") - { - var length = this.Number(minLength, maxLength); - return String2(length, chars); + char pickedChar = (char)this.UShort(rangeStart, rangeEnd); + + if (alignment == 1) + { + sb.Append(block[0]); + sb.Append(pickedChar); + } + else + { + sb.Append(pickedChar); + } } - /// - /// Get a string of valid UTF16 Unicode characters. - /// This method returns a string where each character IsLetterOrDigit() is true. - /// - /// The minimum length of the string to return. - /// The maximum length of the string to return. - /// Excludes surrogate pairs from the returned string. - public string Utf16String(int minLength = 40, int maxLength = 80, bool excludeSurrogates = false) - { - var targetLength = minLength == maxLength ? minLength : this.Number(minLength, maxLength); + return sb.ToString(); + } - var sb = new StringBuilder(); + /// + /// Return a random hex hash. Default 40 characters, aka SHA-1. + /// + /// The length of the hash string. Default, 40 characters, aka SHA-1. + /// Returns the hex string with uppercase characters. + public string Hash(int length = 40, bool upperCase = false) + { + return String2(length, upperCase ? Bogus.Chars.HexUpperCase : Bogus.Chars.HexLowerCase); + } - while( sb.Length < targetLength) - { - int spaceLeft = targetLength - sb.Length; - string block = null; - int alignment = 0; + /// + /// Get a random boolean. + /// + public bool Bool() + { + return Number() == 0; + } - if (!excludeSurrogates && spaceLeft >= 2 && this.Bool()) - { - block = this.ArrayElement(SafeUnicodeRanges.SurrogatePairs); - alignment = 1; - } - else - { - block = this.ArrayElement(SafeUnicodeRanges.Basic); - alignment = 0; - } + /// + /// Get a random boolean. + /// + /// The probability of true. Ranges from 0 to 1. + public bool Bool(float weight) + { + return Float() < weight; + } - char rangeStart = block[alignment]; - char rangeEnd = block[2 + alignment * 2]; + /// + /// Get a random array element. + /// + public T ArrayElement(T[] array) + { + if (array.Length <= 0) + throw new ArgumentException("The array is empty. There are no items to select.", nameof(array)); - char pickedChar = (char)this.UShort(rangeStart, rangeEnd); + var r = Number(max: array.Length - 1); + return array[r]; + } - if (alignment == 1) - { - sb.Append(block[0]); - sb.Append(pickedChar); - } - else - { - sb.Append(pickedChar); - } - } + /// + /// Helper method to get a random element in a BSON array. + /// + public BValue ArrayElement(BArray props, int? min = null, int? max = null) + { + var r = Number(min: min ?? 0, max: max - 1 ?? props.Count - 1); + return props[r]; + } - return sb.ToString(); - } + /// + /// Get a random array element. + /// + public string ArrayElement(Array array) + { + array ??= new[] {"a", "b", "c"}; - /// - /// Return a random hex hash. Default 40 characters, aka SHA-1. - /// - /// The length of the hash string. Default, 40 characters, aka SHA-1. - /// Returns the hex string with uppercase characters. - public string Hash(int length = 40, bool upperCase = false) - { - return String2(length, upperCase ? Bogus.Chars.HexUpperCase : Bogus.Chars.HexLowerCase); - } + var r = Number(max: array.Length - 1); - /// - /// Get a random boolean. - /// - public bool Bool() - { - return Number() == 0; - } + return array.GetValue(r).ToString(); + } - /// - /// Get a random boolean. - /// - /// The probability of true. Ranges from 0 to 1. - public bool Bool(float weight) - { - return Float() < weight; - } + /// + /// Get a random subset of an array. + /// + /// The source of items to pick from. + /// The number of elements to pick; otherwise, a random amount is picked. + public T[] ArrayElements(T[] array, int? count = null) + { + if( count > array.Length ) + throw new ArgumentOutOfRangeException(nameof(count)); + if( count is null ) + count = Number(0, array.Length - 1); - /// - /// Get a random array element. - /// - public T ArrayElement(T[] array) - { - if (array.Length <= 0) - throw new ArgumentException("The array is empty. There are no items to select.", nameof(array)); + return Shuffle(array).Take(count.Value).ToArray(); + } - var r = Number(max: array.Length - 1); - return array[r]; - } + /// + /// Get a random list item. + /// + public T ListItem(List list) + { + return ListItem(list as IList); + } - /// - /// Helper method to get a random element in a BSON array. - /// - public BValue ArrayElement(BArray props, int? min = null, int? max = null) - { - var r = Number(min: min ?? 0, max: max - 1 ?? props.Count - 1); - return props[r]; - } + /// + /// Get a random list item. + /// + public T ListItem(IList list) + { + if (list.Count <= 0) + throw new ArgumentException("The list is empty. There are no items to select.", nameof(list)); - /// - /// Get a random array element. - /// - public string ArrayElement(Array array) - { - array ??= new[] {"a", "b", "c"}; + var r = Number(max: list.Count - 1); + return list[r]; + } - var r = Number(max: array.Length - 1); + /// + /// Get a random subset of a List. + /// + /// The source of items to pick from. + /// The number of items to pick; otherwise, a random amount is picked. + public List ListItems(IList items, int? count = null) + { + if( count > items.Count ) + throw new ArgumentOutOfRangeException(nameof(count)); + if( count is null ) + count = Number(0, items.Count - 1); - return array.GetValue(r).ToString(); - } + return Shuffle(items).Take(count.Value).ToList(); + } - /// - /// Get a random subset of an array. - /// - /// The source of items to pick from. - /// The number of elements to pick; otherwise, a random amount is picked. - public T[] ArrayElements(T[] array, int? count = null) - { - if( count > array.Length ) - throw new ArgumentOutOfRangeException(nameof(count)); - if( count is null ) - count = Number(0, array.Length - 1); + /// + /// Get a random subset of a List. + /// + /// The source of items to pick from. + /// The number of items to pick; otherwise, a random amount is picked. + public IList ListItems(List items, int? count = null) + { + return ListItems(items as IList, count); + } - return Shuffle(array).Take(count.Value).ToArray(); - } + /// + /// Get a random collection item. + /// + public T CollectionItem(ICollection collection) + { + if( collection.Count <= 0 ) + throw new ArgumentException("The collection is empty. There are no items to select.", nameof(collection)); - /// - /// Get a random list item. - /// - public T ListItem(List list) - { - return ListItem(list as IList); - } + var r = Number(max: collection.Count - 1); + return collection.Skip(r).First(); + } - /// - /// Get a random list item. - /// - public T ListItem(IList list) - { - if (list.Count <= 0) - throw new ArgumentException("The list is empty. There are no items to select.", nameof(list)); + /// + /// Replaces symbols with numbers. + /// IE: ### -> 283 + /// + /// The string format + /// The symbol to search for in format that will be replaced with a number + public string ReplaceNumbers(string format, char symbol = '#') + { + return ReplaceSymbols(format, symbol, () => Convert.ToChar('0' + Number(9))); + } - var r = Number(max: list.Count - 1); - return list[r]; - } + /// + /// Replaces each character instance in a string. + /// Func is called each time a symbol is encountered. + /// + /// The string with symbols to replace. + /// The symbol to search for in the string. + /// The function that produces a character for replacement. Invoked each time the replacement symbol is encountered. + public string ReplaceSymbols(string format, char symbol, Func func) + { + var chars = format.Select(c => c == symbol ? func() : c).ToArray(); + return new string(chars); + } - /// - /// Get a random subset of a List. - /// - /// The source of items to pick from. - /// The number of items to pick; otherwise, a random amount is picked. - public List ListItems(IList items, int? count = null) - { - if( count > items.Count ) - throw new ArgumentOutOfRangeException(nameof(count)); - if( count is null ) - count = Number(0, items.Count - 1); + /// + /// Replaces symbols with numbers and letters. # = number, ? = letter, * = number or letter. + /// IE: ###???* -> 283QED4. Letters are uppercase. + /// + public string Replace(string format) + { + var chars = format.Select(c => + { + if( c == '*' ) + { + c = Bool() ? '#' : '?'; + } + if( c == '#' ) + { + return Convert.ToChar('0' + Number(9)); + } + if( c == '?' ) + { + return Convert.ToChar('A' + Number(25)); + } - return Shuffle(items).Take(count.Value).ToList(); - } + return c; + }) + .ToArray(); - /// - /// Get a random subset of a List. - /// - /// The source of items to pick from. - /// The number of items to pick; otherwise, a random amount is picked. - public IList ListItems(List items, int? count = null) + return new string(chars); + } + + /// + /// Clamps the length of a string between min and max characters. + /// If the string is below the minimum, the string is appended with random characters up to the minimum length. + /// If the string is over the maximum, the string is truncated at maximum characters; additionally, if the result string ends with + /// whitespace, it is replaced with a random characters. + /// + public string ClampString(string str, int? min = null, int? max = null) + { + if( max != null && str.Length > max ) { - return ListItems(items as IList, count); + str = str.Substring(0, max.Value).Trim(); } - - /// - /// Get a random collection item. - /// - public T CollectionItem(ICollection collection) + if( min != null && min > str.Length ) { - if( collection.Count <= 0 ) - throw new ArgumentException("The collection is empty. There are no items to select.", nameof(collection)); - - var r = Number(max: collection.Count - 1); - return collection.Skip(r).First(); + var missingChars = min - str.Length; + var fillerChars = this.Replace("".PadRight(missingChars.Value, '?')); + return str + fillerChars; } + return str; + } + + /// + /// Picks a random enum value in T:Enum. + /// + /// Must be an Enum + /// Exclude enum values from being returned + public T Enum(params T[] exclude) where T : struct, Enum + { + var e = typeof(T); + if( !e.IsEnum() ) + throw new ArgumentException("When calling Enum() with no parameters T must be an enum."); - /// - /// Replaces symbols with numbers. - /// IE: ### -> 283 - /// - /// The string format - /// The symbol to search for in format that will be replaced with a number - public string ReplaceNumbers(string format, char symbol = '#') + var selection = System.Enum.GetNames(e); + + if( exclude.Any() ) { - return ReplaceSymbols(format, symbol, () => Convert.ToChar('0' + Number(9))); + var excluded = exclude.Select(ex => System.Enum.GetName(e, ex)); + selection = selection.Except(excluded).ToArray(); } - /// - /// Replaces each character instance in a string. - /// Func is called each time a symbol is encountered. - /// - /// The string with symbols to replace. - /// The symbol to search for in the string. - /// The function that produces a character for replacement. Invoked each time the replacement symbol is encountered. - public string ReplaceSymbols(string format, char symbol, Func func) + if( !selection.Any() ) { - var chars = format.Select(c => c == symbol ? func() : c).ToArray(); - return new string(chars); + throw new ArgumentException("There are no values after exclusion to choose from."); } - /// - /// Replaces symbols with numbers and letters. # = number, ? = letter, * = number or letter. - /// IE: ###???* -> 283QED4. Letters are uppercase. - /// - public string Replace(string format) + var val = this.ArrayElement(selection); + + System.Enum.TryParse(val, out T picked); + return picked; + } + + /// + /// Picks a random subset of enum values in T:Enum. + /// + /// The enum. + /// The number of enums to pick. + /// Any enums that should be excluded before picking. + public T[] EnumValues(int? count = null, params T[] exclude) where T : Enum + { + T[] enums; + if( exclude.Length > 0) { - var chars = format.Select(c => - { - if( c == '*' ) - { - c = Bool() ? '#' : '?'; - } - if( c == '#' ) - { - return Convert.ToChar('0' + Number(9)); - } - if( c == '?' ) - { - return Convert.ToChar('A' + Number(25)); - } - - return c; - }) + enums = System.Enum.GetValues(typeof(T)) + .OfType() + .Except(exclude) .ToArray(); - - return new string(chars); } - - /// - /// Clamps the length of a string between min and max characters. - /// If the string is below the minimum, the string is appended with random characters up to the minimum length. - /// If the string is over the maximum, the string is truncated at maximum characters; additionally, if the result string ends with - /// whitespace, it is replaced with a random characters. - /// - public string ClampString(string str, int? min = null, int? max = null) + else { - if( max != null && str.Length > max ) - { - str = str.Substring(0, max.Value).Trim(); - } - if( min != null && min > str.Length ) - { - var missingChars = min - str.Length; - var fillerChars = this.Replace("".PadRight(missingChars.Value, '?')); - return str + fillerChars; - } - return str; + enums = System.Enum.GetValues(typeof(T)) + .OfType() + .Except(exclude) + .ToArray(); } - /// - /// Picks a random enum value in T:Enum. - /// - /// Must be an Enum - /// Exclude enum values from being returned - public T Enum(params T[] exclude) where T : struct, Enum + if( count > enums.Length || count < 0 ) { - var e = typeof(T); - if( !e.IsEnum() ) - throw new ArgumentException("When calling Enum() with no parameters T must be an enum."); - - var selection = System.Enum.GetNames(e); - - if( exclude.Any() ) - { - var excluded = exclude.Select(ex => System.Enum.GetName(e, ex)); - selection = selection.Except(excluded).ToArray(); - } - - if( !selection.Any() ) - { - throw new ArgumentException("There are no values after exclusion to choose from."); - } - - var val = this.ArrayElement(selection); - - System.Enum.TryParse(val, out T picked); - return picked; + throw new ArgumentOutOfRangeException(nameof(count), count, + $"The {nameof(count)} parameter is {count} and the calculated set of enums has a length of {enums.Length}. It is impossible to pick {count} enums from a list of {enums.Length}."); } - /// - /// Picks a random subset of enum values in T:Enum. - /// - /// The enum. - /// The number of enums to pick. - /// Any enums that should be excluded before picking. - public T[] EnumValues(int? count = null, params T[] exclude) where T : Enum - { - T[] enums; - if( exclude.Length > 0) - { - enums = System.Enum.GetValues(typeof(T)) - .OfType() - .Except(exclude) - .ToArray(); - } - else - { - enums = System.Enum.GetValues(typeof(T)) - .OfType() - .Except(exclude) - .ToArray(); - } + return this.ArrayElements(enums, count); + } - if( count > enums.Length || count < 0 ) + /// + /// Shuffles an IEnumerable source. + /// + public IEnumerable Shuffle(IEnumerable source) + { + List buffer = source.ToList(); + for( var i = 0; i < buffer.Count; i++ ) + { + int j; + //lock any seed access, for thread safety. + lock( Locker.Value ) { - throw new ArgumentOutOfRangeException(nameof(count), count, - $"The {nameof(count)} parameter is {count} and the calculated set of enums has a length of {enums.Length}. It is impossible to pick {count} enums from a list of {enums.Length}."); + j = this.localSeed.Next(i, buffer.Count); } + yield return buffer[j]; - return this.ArrayElements(enums, count); + buffer[j] = buffer[i]; } + } - /// - /// Shuffles an IEnumerable source. - /// - public IEnumerable Shuffle(IEnumerable source) - { - List buffer = source.ToList(); - for( var i = 0; i < buffer.Count; i++ ) - { - int j; - //lock any seed access, for thread safety. - lock( Locker.Value ) - { - j = this.localSeed.Next(i, buffer.Count); - } - yield return buffer[j]; + private WordFunctions wordFunctions; - buffer[j] = buffer[i]; - } - } + /// + /// Returns a single word or phrase in English. + /// + public string Word() + { + this.wordFunctions ??= new WordFunctions(this); + var randomWordMethod = ListItem(this.wordFunctions.Functions); + return randomWordMethod(); + } - private WordFunctions wordFunctions; + /// + /// Gets some random words and phrases in English. + /// + /// Number of times to call Word() + public string Words(int? count = null) + { + if( count == null ) + count = Number(1, 3); - /// - /// Returns a single word or phrase in English. - /// - public string Word() - { - this.wordFunctions ??= new WordFunctions(this); - var randomWordMethod = ListItem(this.wordFunctions.Functions); - return randomWordMethod(); - } + var words = WordsArray(count.Value); - /// - /// Gets some random words and phrases in English. - /// - /// Number of times to call Word() - public string Words(int? count = null) - { - if( count == null ) - count = Number(1, 3); + return string.Join(" ", words); + } - var words = WordsArray(count.Value); + /// + /// Get a range of words in an array (English). + /// + /// Minimum word count. + /// Maximum word count. + public string[] WordsArray(int min, int max) + { + var count = Number(min, max); + return WordsArray(count); + } - return string.Join(" ", words); - } + /// + /// Get a specific number of words in an array (English). + /// + public string[] WordsArray(int count) + { + return Enumerable.Range(1, count) + .Select(f => Word()) + .ToArray(); // lol. + } - /// - /// Get a range of words in an array (English). - /// - /// Minimum word count. - /// Maximum word count. - public string[] WordsArray(int min, int max) - { - var count = Number(min, max); - return WordsArray(count); - } + /// + /// Get a random GUID. + /// + public Guid Guid() + { + var guidBytes = this.Bytes(16); + return new Guid(guidBytes); + } - /// - /// Get a specific number of words in an array (English). - /// - public string[] WordsArray(int count) - { - return Enumerable.Range(1, count) - .Select(f => Word()) - .ToArray(); // lol. - } + /// + /// Get a random GUID. Alias for Randomizer.Guid(). + /// + public Guid Uuid() + { + var guidBytes = this.Bytes(16); + return new Guid(guidBytes); + } - /// - /// Get a random GUID. - /// - public Guid Guid() - { - var guidBytes = this.Bytes(16); - return new Guid(guidBytes); - } + /// + /// Returns a random locale. + /// + public string RandomLocale() + { + return this.ArrayElement(Database.GetAllLocales()); + } - /// - /// Get a random GUID. Alias for Randomizer.Guid(). - /// - public Guid Uuid() - { - var guidBytes = this.Bytes(16); - return new Guid(guidBytes); - } - /// - /// Returns a random locale. - /// - public string RandomLocale() + private static char[] AlphaChars = { - return this.ArrayElement(Database.GetAllLocales()); - } + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z' + }; + /// + /// Returns a random set of alpha numeric characters 0-9, a-z. + /// + public string AlphaNumeric(int length) + { + var sb = new StringBuilder(); + return Enumerable.Range(1, length).Aggregate(sb, (b, i) => b.Append(ArrayElement(AlphaChars)), b => b.ToString()); + } - private static char[] AlphaChars = - { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', - 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z' - }; - - /// - /// Returns a random set of alpha numeric characters 0-9, a-z. - /// - public string AlphaNumeric(int length) + private static char[] HexChars = { - var sb = new StringBuilder(); - return Enumerable.Range(1, length).Aggregate(sb, (b, i) => b.Append(ArrayElement(AlphaChars)), b => b.ToString()); - } + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' + }; - private static char[] HexChars = - { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f' - }; - - /// - /// Generates a random hexadecimal string. - /// - public string Hexadecimal(int length = 1, string prefix = "0x") - { - var sb = new StringBuilder(); - return Enumerable.Range(1, length).Aggregate(sb, (b, i) => b.Append(ArrayElement(HexChars)), b => $"{prefix}{b}"); - } + /// + /// Generates a random hexadecimal string. + /// + public string Hexadecimal(int length = 1, string prefix = "0x") + { + var sb = new StringBuilder(); + return Enumerable.Range(1, length).Aggregate(sb, (b, i) => b.Append(ArrayElement(HexChars)), b => $"{prefix}{b}"); + } - //items are weighted by the decimal probability in their value - /// - /// Returns a selection of T[] based on a weighted distribution of probability. - /// - /// Items to draw the selection from. - /// Weights in decimal form: IE:[.25, .50, .25] for total of 3 items. Should add up to 1. - public T WeightedRandom(T[] items, float[] weights) - { - if( weights.Length != items.Length ) throw new ArgumentOutOfRangeException($"{nameof(items)}.Length and {nameof(weights)}.Length must be the same."); + //items are weighted by the decimal probability in their value + /// + /// Returns a selection of T[] based on a weighted distribution of probability. + /// + /// Items to draw the selection from. + /// Weights in decimal form: IE:[.25, .50, .25] for total of 3 items. Should add up to 1. + public T WeightedRandom(T[] items, float[] weights) + { + if( weights.Length != items.Length ) throw new ArgumentOutOfRangeException($"{nameof(items)}.Length and {nameof(weights)}.Length must be the same."); - var rand = this.Float(); - float max; - float min = 0f; + var rand = this.Float(); + float max; + float min = 0f; - var item = default(T); + var item = default(T); - for( int i = 0; i < weights.Length; i++ ) + for( int i = 0; i < weights.Length; i++ ) + { + max = min + weights[i]; + item = items[i]; + if( rand >= min && rand <= max ) { - max = min + weights[i]; - item = items[i]; - if( rand >= min && rand <= max ) - { - break; - } - min = min + weights[i]; + break; } - - return item; + min = min + weights[i]; } - } + return item; + } +} + +/// +/// General word functions that are available across +/// data sets. The default locale of these word functions is +/// to 'en' and it is intentional. +/// +public class WordFunctions +{ /// - /// General word functions that are available across - /// data sets. The default locale of these word functions is - /// to 'en' and it is intentional. + /// After the class is created, is a list + /// of strings used as a selection list + /// of word functions that generate English words or phrases. /// - public class WordFunctions - { - /// - /// After the class is created, is a list - /// of strings used as a selection list - /// of word functions that generate English words or phrases. - /// - public List> Functions { get; } = new List>(); + public List> Functions { get; } = new List>(); - private Commerce Commerce { get; } - private Company Company { get; } - private Address Address { get; } - private Finance Finance { get; } - private Hacker Hacker { get; } - private Name Name { get; } + private Commerce Commerce { get; } + private Company Company { get; } + private Address Address { get; } + private Finance Finance { get; } + private Hacker Hacker { get; } + private Name Name { get; } - /// - /// Constructor for . - /// - public WordFunctions(Randomizer r) - { - this.Commerce = new Commerce {Random = r}; - this.Company = new Company {Random = r}; - this.Address = new Address {Random = r}; - this.Finance = new Finance {Random = r}; - this.Hacker = new Hacker {Random = r}; - this.Name = new Name {Random = r}; - - Init(); - } + /// + /// Constructor for . + /// + public WordFunctions(Randomizer r) + { + this.Commerce = new Commerce {Random = r}; + this.Company = new Company {Random = r}; + this.Address = new Address {Random = r}; + this.Finance = new Finance {Random = r}; + this.Hacker = new Hacker {Random = r}; + this.Name = new Name {Random = r}; + + Init(); + } - private void Init() - { - this.Functions.Add(() => this.Commerce.Department()); - this.Functions.Add(() => this.Commerce.ProductName()); - this.Functions.Add(() => this.Commerce.ProductAdjective()); - this.Functions.Add(() => this.Commerce.ProductMaterial()); - this.Functions.Add(() => this.Commerce.ProductName()); - this.Functions.Add(() => this.Commerce.Color()); - - this.Functions.Add(() => this.Company.CatchPhraseAdjective()); - this.Functions.Add(() => this.Company.CatchPhraseDescriptor()); - this.Functions.Add(() => this.Company.CatchPhraseNoun()); - this.Functions.Add(() => this.Company.BsAdjective()); - this.Functions.Add(() => this.Company.BsBuzz()); - this.Functions.Add(() => this.Company.BsNoun()); - - this.Functions.Add(() => this.Address.StreetSuffix()); - this.Functions.Add(() => this.Address.County()); - this.Functions.Add(() => this.Address.Country()); - this.Functions.Add(() => this.Address.State()); - - this.Functions.Add(() => this.Address.StreetSuffix()); - - this.Functions.Add(() => this.Finance.AccountName()); - this.Functions.Add(() => this.Finance.TransactionType()); - this.Functions.Add(() => this.Finance.Currency().Description); - - this.Functions.Add(() => this.Hacker.Noun()); - this.Functions.Add(() => this.Hacker.Verb()); - this.Functions.Add(() => this.Hacker.Adjective()); - this.Functions.Add(() => this.Hacker.IngVerb()); - this.Functions.Add(() => this.Hacker.Abbreviation()); - - this.Functions.Add(() => this.Name.JobDescriptor()); - this.Functions.Add(() => this.Name.JobArea()); - this.Functions.Add(() => this.Name.JobType()); - } + private void Init() + { + this.Functions.Add(() => this.Commerce.Department()); + this.Functions.Add(() => this.Commerce.ProductName()); + this.Functions.Add(() => this.Commerce.ProductAdjective()); + this.Functions.Add(() => this.Commerce.ProductMaterial()); + this.Functions.Add(() => this.Commerce.ProductName()); + this.Functions.Add(() => this.Commerce.Color()); + + this.Functions.Add(() => this.Company.CatchPhraseAdjective()); + this.Functions.Add(() => this.Company.CatchPhraseDescriptor()); + this.Functions.Add(() => this.Company.CatchPhraseNoun()); + this.Functions.Add(() => this.Company.BsAdjective()); + this.Functions.Add(() => this.Company.BsBuzz()); + this.Functions.Add(() => this.Company.BsNoun()); + + this.Functions.Add(() => this.Address.StreetSuffix()); + this.Functions.Add(() => this.Address.County()); + this.Functions.Add(() => this.Address.Country()); + this.Functions.Add(() => this.Address.State()); + + this.Functions.Add(() => this.Address.StreetSuffix()); + + this.Functions.Add(() => this.Finance.AccountName()); + this.Functions.Add(() => this.Finance.TransactionType()); + this.Functions.Add(() => this.Finance.Currency().Description); + + this.Functions.Add(() => this.Hacker.Noun()); + this.Functions.Add(() => this.Hacker.Verb()); + this.Functions.Add(() => this.Hacker.Adjective()); + this.Functions.Add(() => this.Hacker.IngVerb()); + this.Functions.Add(() => this.Hacker.Abbreviation()); + + this.Functions.Add(() => this.Name.JobDescriptor()); + this.Functions.Add(() => this.Name.JobArea()); + this.Functions.Add(() => this.Name.JobType()); } } \ No newline at end of file diff --git a/Source/Bogus/ResourceHelper.cs b/Source/Bogus/ResourceHelper.cs index 85d969aa..7e3fb964 100644 --- a/Source/Bogus/ResourceHelper.cs +++ b/Source/Bogus/ResourceHelper.cs @@ -1,67 +1,66 @@ using System.IO; using Bogus.Bson; -namespace Bogus +namespace Bogus; + +/// +/// Helper utility class to read resource manifest streams. +/// +public static class ResourceHelper { /// - /// Helper utility class to read resource manifest streams. + /// Checks to see if a resource exists in an assembly. /// - public static class ResourceHelper + /// The assembly containing the resource. + /// The name of the resource. + /// A boolean indicating if the resource exists in the assembly. + public static bool ResourceExists(System.Reflection.Assembly assembly, string resourceName) { - /// - /// Checks to see if a resource exists in an assembly. - /// - /// The assembly containing the resource. - /// The name of the resource. - /// A boolean indicating if the resource exists in the assembly. - public static bool ResourceExists(System.Reflection.Assembly assembly, string resourceName) - { - return assembly.GetManifestResourceInfo(resourceName) != null; - } + return assembly.GetManifestResourceInfo(resourceName) != null; + } - /// - /// Reads a byte[] resource from an assembly. - /// - /// The assembly containing the resource. - /// The name of the resource. - /// The resource in bytes. - public static byte[] ReadResource(System.Reflection.Assembly assembly, string resourceName) - { - using var s = assembly.GetManifestResourceStream(resourceName); - using var ms = new MemoryStream(); - s.CopyTo(ms); + /// + /// Reads a byte[] resource from an assembly. + /// + /// The assembly containing the resource. + /// The name of the resource. + /// The resource in bytes. + public static byte[] ReadResource(System.Reflection.Assembly assembly, string resourceName) + { + using var s = assembly.GetManifestResourceStream(resourceName); + using var ms = new MemoryStream(); + s.CopyTo(ms); - return ms.ToArray(); - } + return ms.ToArray(); + } - /// - /// Reads a BSON resource from an assembly. - /// - /// The assembly containing the resource. - /// The name of the resource. - /// The value of the resource as a object. - public static BValue ReadBValueResource(System.Reflection.Assembly assembly, string resourceName) - { - using var s = assembly.GetManifestResourceStream(resourceName); - using var ms = new MemoryStream(); - s.CopyTo(ms); + /// + /// Reads a BSON resource from an assembly. + /// + /// The assembly containing the resource. + /// The name of the resource. + /// The value of the resource as a object. + public static BValue ReadBValueResource(System.Reflection.Assembly assembly, string resourceName) + { + using var s = assembly.GetManifestResourceStream(resourceName); + using var ms = new MemoryStream(); + s.CopyTo(ms); - return Bson.Bson.Load(ms.ToArray()); - } + return Bson.Bson.Load(ms.ToArray()); + } - /// - /// Reads a BSON resource from an assembly. - /// - /// The assembly containing the resource. - /// The name of the resource. - /// The value of the resource as a object. - public static BObject ReadBObjectResource(System.Reflection.Assembly assembly, string resourceName) - { - using var s = assembly.GetManifestResourceStream(resourceName); - using var ms = new MemoryStream(); - s.CopyTo(ms); + /// + /// Reads a BSON resource from an assembly. + /// + /// The assembly containing the resource. + /// The name of the resource. + /// The value of the resource as a object. + public static BObject ReadBObjectResource(System.Reflection.Assembly assembly, string resourceName) + { + using var s = assembly.GetManifestResourceStream(resourceName); + using var ms = new MemoryStream(); + s.CopyTo(ms); - return Bson.Bson.Load(ms.ToArray()); - } + return Bson.Bson.Load(ms.ToArray()); } } \ No newline at end of file diff --git a/Source/Bogus/Rule.cs b/Source/Bogus/Rule.cs index 18da7030..b4428019 100644 --- a/Source/Bogus/Rule.cs +++ b/Source/Bogus/Rule.cs @@ -1,75 +1,74 @@ using System; using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Represents a Faker rule +/// +public class Rule { /// - /// Represents a Faker rule + /// Populate action /// - public class Rule - { - /// - /// Populate action - /// - public T Action { get; set; } + public T Action { get; set; } - /// - /// Property name, maybe null for finalize or create. - /// - public string PropertyName { get; set; } + /// + /// Property name, maybe null for finalize or create. + /// + public string PropertyName { get; set; } - /// - /// The rule set this rule belongs to. - /// - public string RuleSet { get; set; } = string.Empty; + /// + /// The rule set this rule belongs to. + /// + public string RuleSet { get; set; } = string.Empty; - /// - /// Prohibits the rule from being applied in strict mode. - /// - public bool ProhibitInStrictMode { get; set; } = false; - } + /// + /// Prohibits the rule from being applied in strict mode. + /// + public bool ProhibitInStrictMode { get; set; } = false; +} - public class PopulateAction : Rule> - { - } +public class PopulateAction : Rule> +{ +} - public class FinalizeAction : Rule> +public class FinalizeAction : Rule> +{ +} + +public class MultiDictionary : Dictionary> +{ + public MultiDictionary(IEqualityComparer comparer) : base(comparer) { } - public class MultiDictionary : Dictionary> + public void Add(Key key, Key2 key2, Value value) { - public MultiDictionary(IEqualityComparer comparer) : base(comparer) - { - } - - public void Add(Key key, Key2 key2, Value value) + if( !this.TryGetValue(key, out var values) ) { - if( !this.TryGetValue(key, out var values) ) - { - values = new Dictionary(); - this.Add(key, values); - } - values[key2] = value; + values = new Dictionary(); + this.Add(key, values); } + values[key2] = value; } +} - public class MultiSetDictionary : Dictionary> +public class MultiSetDictionary : Dictionary> +{ + public MultiSetDictionary(IEqualityComparer comparer) : base(comparer) { - public MultiSetDictionary(IEqualityComparer comparer) : base(comparer) - { - } + } - public void Add(Key key, Value value) + public void Add(Key key, Value value) + { + if( !this.TryGetValue(key, out var values) ) { - if( !this.TryGetValue(key, out var values) ) - { - values = new HashSet(); - this.Add(key, values); - } - if( values.Contains(value) ) - throw new ArgumentException("An item with the same key has already been added."); - values.Add(value); + values = new HashSet(); + this.Add(key, values); } + if( values.Contains(value) ) + throw new ArgumentException("An item with the same key has already been added."); + values.Add(value); } } \ No newline at end of file diff --git a/Source/Bogus/SeedNotifier[T].cs b/Source/Bogus/SeedNotifier[T].cs index e419f5ac..7147c468 100644 --- a/Source/Bogus/SeedNotifier[T].cs +++ b/Source/Bogus/SeedNotifier[T].cs @@ -1,58 +1,57 @@ using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Objects should implement this interface if they use a +/// . +/// +public interface IHasRandomizer { /// - /// Objects should implement this interface if they use a - /// . + /// Access the randomizer on the implementing object. When the property value + /// is set, the object is instructed to use the randomizer as a source of generating + /// random values. Additionally, setting this property also notifies any dependent + /// via . /// - public interface IHasRandomizer - { - /// - /// Access the randomizer on the implementing object. When the property value - /// is set, the object is instructed to use the randomizer as a source of generating - /// random values. Additionally, setting this property also notifies any dependent - /// via . - /// - Randomizer Random { set; } + Randomizer Random { set; } - /// - /// Retrieves the internal notifier registry for this object. - /// - SeedNotifier GetNotifier(); - } + /// + /// Retrieves the internal notifier registry for this object. + /// + SeedNotifier GetNotifier(); +} + +/// +/// The seed notifier's purpose is to keep track of any objects that +/// might need to be notified when a seed/randomizer changes. +/// For example, the Internet dataset depends on the Name dataset +/// to generate data. If the randomizer seed changes in Internet, the +/// Name dependency data set should be notified of this change too. +/// This whole process is important in maintaining determinism in Bogus. +/// +public class SeedNotifier +{ + private List registry = new List(); /// - /// The seed notifier's purpose is to keep track of any objects that - /// might need to be notified when a seed/randomizer changes. - /// For example, the Internet dataset depends on the Name dataset - /// to generate data. If the randomizer seed changes in Internet, the - /// Name dependency data set should be notified of this change too. - /// This whole process is important in maintaining determinism in Bogus. + /// Causes to be remembered and tracked so that the + /// will be notified when is called. /// - public class SeedNotifier + public U Flow(U item) where U : IHasRandomizer { - private List registry = new List(); - - /// - /// Causes to be remembered and tracked so that the - /// will be notified when is called. - /// - public U Flow(U item) where U : IHasRandomizer - { - this.registry.Add(item); - return item; - } + this.registry.Add(item); + return item; + } - /// - /// Pushes/notifies all tracked objects that a new randomizer has been set. - /// - public void Notify(Randomizer r) + /// + /// Pushes/notifies all tracked objects that a new randomizer has been set. + /// + public void Notify(Randomizer r) + { + foreach( var item in registry ) { - foreach( var item in registry ) - { - item.Random = r; - } + item.Random = r; } } } \ No newline at end of file diff --git a/Source/Bogus/Tokenizer.cs b/Source/Bogus/Tokenizer.cs index b81049ac..388c91c0 100644 --- a/Source/Bogus/Tokenizer.cs +++ b/Source/Bogus/Tokenizer.cs @@ -3,185 +3,184 @@ using System.Reflection; using System.Text; -namespace Bogus +namespace Bogus; + +public class MustashMethod +{ + public string Name { get; set; } + public MethodInfo Method { get; set; } + public object[] OptionalArgs { get; set; } +} + +public class Tokenizer { - public class MustashMethod + public static ILookup MustashMethods; + + static Tokenizer() { - public string Name { get; set; } - public MethodInfo Method { get; set; } - public object[] OptionalArgs { get; set; } + RegisterMustashMethods(typeof(Faker)); } - public class Tokenizer + public static void RegisterMustashMethods(Type type) { - public static ILookup MustashMethods; + MustashMethods = type.GetProperties() + .Where(p => p.IsDefined(typeof(RegisterMustasheMethodsAttribute), true)) + .SelectMany(p => + { + return p.PropertyType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) + .Where(mi => mi.GetGenericArguments().Length == 0) + .Select(mi => + { + var category = DataSet.ResolveCategory(p.PropertyType); + var methodName = mi.Name; + var mm = new MustashMethod + { + Name = $"{category}.{methodName}".ToUpperInvariant(), + Method = mi, + OptionalArgs = mi.GetParameters().Where(pi => pi.IsOptional).Select(_ => Type.Missing).ToArray() + }; + return mm; + }); + }) + .ToLookup(mm => mm.Name); + } - static Tokenizer() + public static string Parse(string str, params object[] dataSets) + { + //Recursive base case. If there are no more {{ }} handle bars, + //return. + var start = str.IndexOf("{{", StringComparison.Ordinal); + var end = str.IndexOf("}}", StringComparison.Ordinal); + if( start == -1 && end == -1 ) { - RegisterMustashMethods(typeof(Faker)); + return str; } - public static void RegisterMustashMethods(Type type) + //We have some handlebars to process. Get the method name and arguments. + ParseMustashText(str, start, end, out var methodName, out var arguments); + + if( !MustashMethods.Contains(methodName) ) { - MustashMethods = type.GetProperties() - .Where(p => p.IsDefined(typeof(RegisterMustasheMethodsAttribute), true)) - .SelectMany(p => - { - return p.PropertyType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) - .Where(mi => mi.GetGenericArguments().Length == 0) - .Select(mi => - { - var category = DataSet.ResolveCategory(p.PropertyType); - var methodName = mi.Name; - var mm = new MustashMethod - { - Name = $"{category}.{methodName}".ToUpperInvariant(), - Method = mi, - OptionalArgs = mi.GetParameters().Where(pi => pi.IsOptional).Select(_ => Type.Missing).ToArray() - }; - return mm; - }); - }) - .ToLookup(mm => mm.Name); + throw new ArgumentException($"Unknown method {methodName} can't be found."); } - public static string Parse(string str, params object[] dataSets) + //At this point, we have a methodName like: RANDOMIZER.NUMBER + //and if the dataset was registered with RegisterMustasheMethodsAttribute + //we should be able to extract the dataset given it's methodName. + var dataSet = FindDataSetWithMethod(dataSets, methodName); + + //Considering arguments, lets get the best method overload + //that maps to a registered MustashMethod. + var mm = FindMustashMethod(methodName, arguments); + var providedArgumentList = ConvertStringArgumentsToObjects(arguments, mm); + var optionalArgs = mm.OptionalArgs.Take(mm.Method.GetParameters().Length - providedArgumentList.Length); + var argumentList = providedArgumentList.Concat(optionalArgs).ToArray(); + + //make the actual invocation. + var fakeVal = mm.Method.Invoke(dataSet, argumentList).ToString(); + + var sb = new StringBuilder(); + sb.Append(str, 0, start); + sb.Append(fakeVal); + sb.Append(str.Substring(end + 2)); + + return Parse(sb.ToString(), dataSets); + } + + private static object FindDataSetWithMethod(object[] dataSets, string methodName) + { + var dataSetType = MustashMethods[methodName].First().Method.DeclaringType; + + var ds = dataSets.FirstOrDefault(o => o.GetType() == dataSetType); + + if( ds == null ) { - //Recursive base case. If there are no more {{ }} handle bars, - //return. - var start = str.IndexOf("{{", StringComparison.Ordinal); - var end = str.IndexOf("}}", StringComparison.Ordinal); - if( start == -1 && end == -1 ) - { - return str; - } - - //We have some handlebars to process. Get the method name and arguments. - ParseMustashText(str, start, end, out var methodName, out var arguments); - - if( !MustashMethods.Contains(methodName) ) - { - throw new ArgumentException($"Unknown method {methodName} can't be found."); - } - - //At this point, we have a methodName like: RANDOMIZER.NUMBER - //and if the dataset was registered with RegisterMustasheMethodsAttribute - //we should be able to extract the dataset given it's methodName. - var dataSet = FindDataSetWithMethod(dataSets, methodName); - - //Considering arguments, lets get the best method overload - //that maps to a registered MustashMethod. - var mm = FindMustashMethod(methodName, arguments); - var providedArgumentList = ConvertStringArgumentsToObjects(arguments, mm); - var optionalArgs = mm.OptionalArgs.Take(mm.Method.GetParameters().Length - providedArgumentList.Length); - var argumentList = providedArgumentList.Concat(optionalArgs).ToArray(); - - //make the actual invocation. - var fakeVal = mm.Method.Invoke(dataSet, argumentList).ToString(); - - var sb = new StringBuilder(); - sb.Append(str, 0, start); - sb.Append(fakeVal); - sb.Append(str.Substring(end + 2)); - - return Parse(sb.ToString(), dataSets); + throw new ArgumentException($"Can't parse {methodName} because the dataset was not provided in the {nameof(dataSets)} parameter.", nameof(dataSets)); } + return ds; + } + + private static void ParseMustashText(string str, int start, int end, out string methodName, out string[] arguments) + { + var methodCall = str.Substring(start + 2, end - start - 2) + .Replace("}}", "") + .Replace("{{", ""); - private static object FindDataSetWithMethod(object[] dataSets, string methodName) + var argumentsStart = methodCall.IndexOf("(", StringComparison.Ordinal); + if (argumentsStart != -1) + { + var argumentsString = GetArgumentsString(methodCall, argumentsStart); + methodName = methodCall.Substring(0, argumentsStart).Trim(); + arguments = argumentsString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); + } + else { - var dataSetType = MustashMethods[methodName].First().Method.DeclaringType; + methodName = methodCall; + arguments = new string[0]; + } - var ds = dataSets.FirstOrDefault(o => o.GetType() == dataSetType); + methodName = methodName.ToUpperInvariant(); + } - if( ds == null ) - { - throw new ArgumentException($"Can't parse {methodName} because the dataset was not provided in the {nameof(dataSets)} parameter.", nameof(dataSets)); - } - return ds; - } + private static MustashMethod FindMustashMethod(string methodName, string[] arguments) + { + var selection = + from mm in MustashMethods[methodName] + orderby mm.Method.GetParameters().Count(pi => pi.IsOptional) - arguments.Length + where mm.Method.GetParameters().Length >= arguments.Length + where mm.OptionalArgs.Length + arguments.Length >= mm.Method.GetParameters().Length + select mm; + + var found = selection.FirstOrDefault(); + return found ?? throw new ArgumentException($"Cannot find a method '{methodName}' that could accept {arguments.Length} arguments"); + } - private static void ParseMustashText(string str, int start, int end, out string methodName, out string[] arguments) + private static object[] ConvertStringArgumentsToObjects(string[] parameters, MustashMethod mm) + { + try { - var methodCall = str.Substring(start + 2, end - start - 2) - .Replace("}}", "") - .Replace("{{", ""); - - var argumentsStart = methodCall.IndexOf("(", StringComparison.Ordinal); - if (argumentsStart != -1) - { - var argumentsString = GetArgumentsString(methodCall, argumentsStart); - methodName = methodCall.Substring(0, argumentsStart).Trim(); - arguments = argumentsString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); - } - else - { - methodName = methodCall; - arguments = new string[0]; - } - - methodName = methodName.ToUpperInvariant(); + return mm.Method.GetParameters() + .Zip(parameters, GetValueForParameter) + .ToArray(); } - - private static MustashMethod FindMustashMethod(string methodName, string[] arguments) + catch (OverflowException ex) { - var selection = - from mm in MustashMethods[methodName] - orderby mm.Method.GetParameters().Count(pi => pi.IsOptional) - arguments.Length - where mm.Method.GetParameters().Length >= arguments.Length - where mm.OptionalArgs.Length + arguments.Length >= mm.Method.GetParameters().Length - select mm; - - var found = selection.FirstOrDefault(); - return found ?? throw new ArgumentException($"Cannot find a method '{methodName}' that could accept {arguments.Length} arguments"); + throw new ArgumentOutOfRangeException($"One of the arguments for {mm.Name} is out of supported range. Argument list: {string.Join(",", parameters)}", ex); } - - private static object[] ConvertStringArgumentsToObjects(string[] parameters, MustashMethod mm) + catch (Exception ex) when (ex is InvalidCastException || ex is FormatException) { - try - { - return mm.Method.GetParameters() - .Zip(parameters, GetValueForParameter) - .ToArray(); - } - catch (OverflowException ex) - { - throw new ArgumentOutOfRangeException($"One of the arguments for {mm.Name} is out of supported range. Argument list: {string.Join(",", parameters)}", ex); - } - catch (Exception ex) when (ex is InvalidCastException || ex is FormatException) - { - throw new ArgumentException($"One of the arguments for {mm.Name} cannot be converted to target type. Argument list: {string.Join(",", parameters)}", ex); - } - catch (Exception ex) - { - throw new ArgumentException($"Cannot parse arguments for {mm.Name}. Argument list: {string.Join(",", parameters)}", ex); - } + throw new ArgumentException($"One of the arguments for {mm.Name} cannot be converted to target type. Argument list: {string.Join(",", parameters)}", ex); } - - private static object GetValueForParameter(ParameterInfo parameterInfo, string parameterValue) + catch (Exception ex) { - var type = Nullable.GetUnderlyingType(parameterInfo.ParameterType) ?? parameterInfo.ParameterType; - - if( typeof(Enum).IsAssignableFrom(type)) return Enum.Parse(type, parameterValue); + throw new ArgumentException($"Cannot parse arguments for {mm.Name}. Argument list: {string.Join(",", parameters)}", ex); + } + } - if( typeof(TimeSpan) == type ) return TimeSpan.Parse(parameterValue); + private static object GetValueForParameter(ParameterInfo parameterInfo, string parameterValue) + { + var type = Nullable.GetUnderlyingType(parameterInfo.ParameterType) ?? parameterInfo.ParameterType; - return Convert.ChangeType(parameterValue, type); - } + if( typeof(Enum).IsAssignableFrom(type)) return Enum.Parse(type, parameterValue); - private static string GetArgumentsString(string methodCall, int parametersStart) - { - var parametersEnd = methodCall.IndexOf(')'); - if( parametersEnd == -1 ) - { - throw new ArgumentException($"The method call '{methodCall}' is missing a terminating ')' character."); - } + if( typeof(TimeSpan) == type ) return TimeSpan.Parse(parameterValue); - return methodCall.Substring(parametersStart + 1, parametersEnd - parametersStart - 1); - } + return Convert.ChangeType(parameterValue, type); } - [AttributeUsage(AttributeTargets.Property)] - internal class RegisterMustasheMethodsAttribute : Attribute + private static string GetArgumentsString(string methodCall, int parametersStart) { + var parametersEnd = methodCall.IndexOf(')'); + if( parametersEnd == -1 ) + { + throw new ArgumentException($"The method call '{methodCall}' is missing a terminating ')' character."); + } + + return methodCall.Substring(parametersStart + 1, parametersEnd - parametersStart - 1); } +} + +[AttributeUsage(AttributeTargets.Property)] +internal class RegisterMustasheMethodsAttribute : Attribute +{ } \ No newline at end of file diff --git a/Source/Bogus/Transliterater.cs b/Source/Bogus/Transliterater.cs index a62187ac..14e0f38a 100644 --- a/Source/Bogus/Transliterater.cs +++ b/Source/Bogus/Transliterater.cs @@ -3,187 +3,185 @@ using System.ComponentModel; using System.Text; -namespace Bogus +namespace Bogus; + +/// +/// Best effort utility for transliterating Unicode characters to US-ASCII. +/// +public static partial class Transliterater { /// - /// Best effort utility for transliterating Unicode characters to US-ASCII. + /// Main method for transliteration. /// - public static partial class Transliterater + /// The Unicode text to translate. + /// Optional. If a language specific transliterates are available it will be used. + public static string Translate(string input, string lang = "en") { - /// - /// Main method for transliteration. - /// - /// The Unicode text to translate. - /// Optional. If a language specific transliterates are available it will be used. - public static string Translate(string input, string lang = "en") + //setup defaults. + if( !LangCharMap.TryGetValue(lang, out var langChar) ) + { + langChar = EmptyDictionary; + } + + if( !SymbolMap.TryGetValue(lang, out var symbols) ) + { + symbols = SymbolMap["en"]; + } + + + var sb = new StringBuilder(input.Length); + + //Loop though each character in the input string. + for( var i = 0; i < input.Length; i++) { - //setup defaults. - if( !LangCharMap.TryGetValue(lang, out var langChar) ) + //Try direct langChar translation + var ch = input.Substring(i, 1); + if ( langChar.TryGetValue(ch, out var foundLangChar) ) { - langChar = EmptyDictionary; + sb.Append(foundLangChar); + continue; } - if( !SymbolMap.TryGetValue(lang, out var symbols) ) + //Try using Char Map + var used = 0; + var chCharMap = WalkTrie(i, input, CharMap, ref used); + if( chCharMap is not null ) { - symbols = SymbolMap["en"]; + //After walking the trie, we found a match, + //use what we found instead. + sb.Append(chCharMap); + //then update the number of characters + //we consumed in the input for this + //match to take place + i += used - 1; + continue; } + //Try Diatric Map + used = 0; + var chDiatric = WalkTrie(i, input, DiatricMap, ref used); + if( chDiatric is not null ) + { + sb.Append(chDiatric); + i += used - 1; + continue; + } - var sb = new StringBuilder(input.Length); - - //Loop though each character in the input string. - for( var i = 0; i < input.Length; i++) + //Try symbol map + if( symbols.TryGetValue(ch, out var foundSymbol) ) { - //Try direct langChar translation - var ch = input.Substring(i, 1); - if ( langChar.TryGetValue(ch, out var foundLangChar) ) - { - sb.Append(foundLangChar); - continue; - } - - //Try using Char Map - var used = 0; - var chCharMap = WalkTrie(i, input, CharMap, ref used); - if( chCharMap is not null ) - { - //After walking the trie, we found a match, - //use what we found instead. - sb.Append(chCharMap); - //then update the number of characters - //we consumed in the input for this - //match to take place - i += used - 1; - continue; - } - - //Try Diatric Map - used = 0; - var chDiatric = WalkTrie(i, input, DiatricMap, ref used); - if( chDiatric is not null ) - { - sb.Append(chDiatric); - i += used - 1; - continue; - } - - //Try symbol map - if( symbols.TryGetValue(ch, out var foundSymbol) ) - { - sb.Append(foundSymbol); - continue; - } - - //otherwise, there's no mapping translation from the - //current character to US-ASCII - sb.Append(ch); + sb.Append(foundSymbol); + continue; } - return sb.ToString(); + //otherwise, there's no mapping translation from the + //current character to US-ASCII + sb.Append(ch); } - [EditorBrowsable(EditorBrowsableState.Never)] - public static string WalkTrie(int i, string input, Trie trie, ref int used) - { - if( i >= input.Length ) return trie.Value; + return sb.ToString(); + } - var ch = input.Substring(i, 1); - if( trie.Map.TryGetValue(ch, out var next) ) - { - used++; - return WalkTrie(i + 1, input, next, ref used); - } + [EditorBrowsable(EditorBrowsableState.Never)] + public static string WalkTrie(int i, string input, Trie trie, ref int used) + { + if( i >= input.Length ) return trie.Value; - if( trie.Value?.Length > 0 ) - { - return trie.Value; - } + var ch = input.Substring(i, 1); + if( trie.Map.TryGetValue(ch, out var next) ) + { + used++; + return WalkTrie(i + 1, input, next, ref used); + } - return null; + if( trie.Value?.Length > 0 ) + { + return trie.Value; } - /// - /// Char map for transliteration. - /// - public static Trie CharMap = BuildCharMap(new Trie()); - - /// - /// Diacritic map for transliteration. - /// - public static Trie DiatricMap = BuildDiatricMap(new Trie()); - - /// - /// Language specific map for transliteration. - /// - public static MultiDictionary LangCharMap = BuildLangCharMap(new MultiDictionary(StringComparer.Ordinal)); - - /// - /// Symbol map for transliteration. - /// - public static MultiDictionary SymbolMap = BuildSymbolMap(new MultiDictionary(StringComparer.Ordinal)); - - /// - /// Default empty dictionary to avoid allocations. - /// - public static readonly Dictionary EmptyDictionary = new Dictionary(); + return null; } /// - /// A Trie data-structure mostly used for transliteration. The Trie is used as - /// a fundamental data-structure to traverse and replace characters in a string. - /// https://en.wikipedia.org/wiki/Trie + /// Char map for transliteration. /// - [EditorBrowsable(EditorBrowsableState.Never)] - public class Trie + public static Trie CharMap = BuildCharMap(new Trie()); + + /// + /// Diacritic map for transliteration. + /// + public static Trie DiatricMap = BuildDiatricMap(new Trie()); + + /// + /// Language specific map for transliteration. + /// + public static MultiDictionary LangCharMap = BuildLangCharMap(new MultiDictionary(StringComparer.Ordinal)); + + /// + /// Symbol map for transliteration. + /// + public static MultiDictionary SymbolMap = BuildSymbolMap(new MultiDictionary(StringComparer.Ordinal)); + + /// + /// Default empty dictionary to avoid allocations. + /// + public static readonly Dictionary EmptyDictionary = new Dictionary(); +} + +/// +/// A Trie data-structure mostly used for transliteration. The Trie is used as +/// a fundamental data-structure to traverse and replace characters in a string. +/// https://en.wikipedia.org/wiki/Trie +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public class Trie +{ + public Dictionary Map = new Dictionary(); + public string Value; + + /// + /// Given a Trie, insert the key and value. + /// + /// The Trie node to start the insertion. + /// A key can be any length. Each character in the key is used to traverse the Trie. If a path doesn't exist, a new node in the Trie. + /// The value to use at the end of the trie walk. + public static void Insert(Trie node, string key, string value) { - public Dictionary Map = new Dictionary(); - public string Value; - - /// - /// Given a Trie, insert the key and value. - /// - /// The Trie node to start the insertion. - /// A key can be any length. Each character in the key is used to traverse the Trie. If a path doesn't exist, a new node in the Trie. - /// The value to use at the end of the trie walk. - public static void Insert(Trie node, string key, string value) + for( int i = 0; i < key.Length; i++) { - for( int i = 0; i < key.Length; i++) + var ch = key.Substring(i, 1); + if( !node.Map.TryGetValue(ch, out var trie) ) { - var ch = key.Substring(i, 1); - if( !node.Map.TryGetValue(ch, out var trie) ) - { - trie = new Trie(); - node.Map.Add(ch, trie); - } - node = trie; + trie = new Trie(); + node.Map.Add(ch, trie); } - - node.Value = value; + node = trie; } - /// - /// If a key exists, returns the value at the end of the trie walk. - /// - /// The trie node to begin the walk. - /// The key to lookup. Each character in the key is used to traverse the trie. - public static string Find(Trie node, string key) + node.Value = value; + } + + /// + /// If a key exists, returns the value at the end of the trie walk. + /// + /// The trie node to begin the walk. + /// The key to lookup. Each character in the key is used to traverse the trie. + public static string Find(Trie node, string key) + { + for( int i = 0; i < key.Length; i++ ) { - for( int i = 0; i < key.Length; i++ ) + var ch = key.Substring(i, 1); + if( node.Map.TryGetValue(ch, out var trie) ) { - var ch = key.Substring(i, 1); - if( node.Map.TryGetValue(ch, out var trie) ) - { - node = trie; - } - else - { - return null; - } + node = trie; + } + else + { + return null; } - - return node.Value; } + + return node.Value; } +} - -} \ No newline at end of file diff --git a/Source/Bogus/Utils.cs b/Source/Bogus/Utils.cs index 6631e28d..eb425917 100644 --- a/Source/Bogus/Utils.cs +++ b/Source/Bogus/Utils.cs @@ -2,30 +2,29 @@ using System.Text.RegularExpressions; using Bogus.Extensions; -namespace Bogus +namespace Bogus; + +/// +/// Some utility functions +/// +public static class Utils { /// - /// Some utility functions + /// Slugify's text so that it is URL compatible. IE: "Can make food" -> "Can-make-food". /// - public static class Utils + public static string Slugify(string txt) { - /// - /// Slugify's text so that it is URL compatible. IE: "Can make food" -> "Can-make-food". - /// - public static string Slugify(string txt) - { - var str = txt.Replace(" ", "-").RemoveDiacritics(); - return SlugifyRegex.Replace(str, ""); - } + var str = txt.Replace(" ", "-").RemoveDiacritics(); + return SlugifyRegex.Replace(str, ""); + } - public static Regex SlugifyRegex = new Regex(@"[^a-zA-Z0-9\.\-_]+", RegexOptions.Compiled); + public static Regex SlugifyRegex = new Regex(@"[^a-zA-Z0-9\.\-_]+", RegexOptions.Compiled); - /// - /// Takes string parts and joins them together with a separator. - /// - public static string Slashify(IEnumerable parts, string separator = "/") - { - return string.Join(separator, parts); - } + /// + /// Takes string parts and joins them together with a separator. + /// + public static string Slashify(IEnumerable parts, string separator = "/") + { + return string.Join(separator, parts); } } \ No newline at end of file diff --git a/Source/Bogus/ValidationException.cs b/Source/Bogus/ValidationException.cs index ebf4a7f4..31ba9916 100644 --- a/Source/Bogus/ValidationException.cs +++ b/Source/Bogus/ValidationException.cs @@ -1,14 +1,13 @@ using System; -namespace Bogus +namespace Bogus; + +/// +/// Represents a validation exception. +/// +public class ValidationException : Exception { - /// - /// Represents a validation exception. - /// - public class ValidationException : Exception + public ValidationException(string message) : base(message) { - public ValidationException(string message) : base(message) - { - } } } \ No newline at end of file diff --git a/Source/Bogus/ValidationResult.cs b/Source/Bogus/ValidationResult.cs index 5f8f24ef..1b63e022 100644 --- a/Source/Bogus/ValidationResult.cs +++ b/Source/Bogus/ValidationResult.cs @@ -1,25 +1,24 @@ using System.Collections.Generic; -namespace Bogus +namespace Bogus; + +/// +/// Contains validation results after validation +/// +public class ValidationResult { /// - /// Contains validation results after validation + /// True if is valid /// - public class ValidationResult - { - /// - /// True if is valid - /// - internal bool IsValid { get; set; } + internal bool IsValid { get; set; } - /// - /// A complete list of missing rules - /// - internal List MissingRules { get; } = new List(); + /// + /// A complete list of missing rules + /// + internal List MissingRules { get; } = new List(); - /// - /// Extra validation messages to display - /// - internal List ExtraMessages { get; } = new List(); - } + /// + /// Extra validation messages to display + /// + internal List ExtraMessages { get; } = new List(); } \ No newline at end of file diff --git a/Source/Bogus/Vendor/UserAgentGenerator.cs b/Source/Bogus/Vendor/UserAgentGenerator.cs index 28890928..4de8c35b 100644 --- a/Source/Bogus/Vendor/UserAgentGenerator.cs +++ b/Source/Bogus/Vendor/UserAgentGenerator.cs @@ -1,234 +1,233 @@ using System; using System.Linq; -namespace Bogus.Vendor +namespace Bogus.Vendor; + +internal class UserAgentGenerator { - internal class UserAgentGenerator + private readonly Func random; + + internal UserAgentGenerator(Func random) { - private readonly Func random; + this.random = random; + } - internal UserAgentGenerator(Func random) + private Randomizer Random => random(); + + internal string VersionString(string type, string delim = ".") + { + if( type == "net" ) { - this.random = random; + return $"{this.Random.Number(1, 4)}.{this.Random.Number(0, 9)}.{this.Random.Number(10000, 99999)}.{this.Random.Number(0, 9)}"; } - - private Randomizer Random => random(); - - internal string VersionString(string type, string delim = ".") + if( type == "nt" ) { - if( type == "net" ) - { - return $"{this.Random.Number(1, 4)}.{this.Random.Number(0, 9)}.{this.Random.Number(10000, 99999)}.{this.Random.Number(0, 9)}"; - } - if( type == "nt" ) - { - return $"{this.Random.Number(5, 6)}.{this.Random.Number(0, 3)}"; - } - if( type == "trident" ) - { - return $"{this.Random.Number(3, 7)}.{this.Random.Number(0, 1)}"; - } - if( type == "osx" ) - { - return $"10{delim}{this.Random.Number(5, 10)}{delim}{this.Random.Number(0, 9)}"; - } - if( type == "chrome" ) - { - return $"{this.Random.Number(13, 39)}.0.{this.Random.Number(800, 899)}.0"; - } - if( type == "presto" ) - { - return $"2.9.{this.Random.Number(160, 190)}"; - } - if( type == "presto2" ) - { - return $"{this.Random.Number(10, 12)}.00"; - } - if( type == "safari" ) - { - return $"{this.Random.Number(531, 538)}.{this.Random.Number(0, 2)}.{this.Random.Number(0, 2)}"; - } - throw new ArgumentOutOfRangeException($"{nameof(type)} is not valid."); + return $"{this.Random.Number(5, 6)}.{this.Random.Number(0, 3)}"; } - - internal string RandomRevision(int dots) + if( type == "trident" ) { - var ver = string.Empty; - for( int i = 0; i < dots; i++ ) - { - ver += "." + this.Random.Number(0, 9); - } - return ver; + return $"{this.Random.Number(3, 7)}.{this.Random.Number(0, 1)}"; } - - private string RandomLanguage() + if( type == "osx" ) { - var languages = new[] - { - "AB", "AF", "AN", "AR", "AS", "AZ", "BE", "BG", "BN", "BO", "BR", "BS", "CA", "CE", "CO", "CS", - "CU", "CY", "DA", "DE", "EL", "EN", "EO", "ES", "ET", "EU", "FA", "FI", "FJ", "FO", "FR", "FY", - "GA", "GD", "GL", "GV", "HE", "HI", "HR", "HT", "HU", "HY", "ID", "IS", "IT", "JA", "JV", "KA", - "KG", "KO", "KU", "KW", "KY", "LA", "LB", "LI", "LN", "LT", "LV", "MG", "MK", "MN", "MO", "MS", - "MT", "MY", "NB", "NE", "NL", "NN", "NO", "OC", "PL", "PT", "RM", "RO", "RU", "SC", "SE", "SK", - "SL", "SO", "SQ", "SR", "SV", "SW", "TK", "TR", "TY", "UK", "UR", "UZ", "VI", "VO", "YI", "ZH" - }; - - return this.Random.ArrayElement(languages); + return $"10{delim}{this.Random.Number(5, 10)}{delim}{this.Random.Number(0, 9)}"; } + if( type == "chrome" ) + { + return $"{this.Random.Number(13, 39)}.0.{this.Random.Number(800, 899)}.0"; + } + if( type == "presto" ) + { + return $"2.9.{this.Random.Number(160, 190)}"; + } + if( type == "presto2" ) + { + return $"{this.Random.Number(10, 12)}.00"; + } + if( type == "safari" ) + { + return $"{this.Random.Number(531, 538)}.{this.Random.Number(0, 2)}.{this.Random.Number(0, 2)}"; + } + throw new ArgumentOutOfRangeException($"{nameof(type)} is not valid."); + } - - internal string RandomBrowser() + internal string RandomRevision(int dots) + { + var ver = string.Empty; + for( int i = 0; i < dots; i++ ) { - return this.Random.WeightedRandom(BrowserNames, BrowserWeights); + ver += "." + this.Random.Number(0, 9); } + return ver; + } - private static readonly string[] BrowserNames = - { - "chrome", - "iexplorer", - "firefox", - "safari", - "opera" + private string RandomLanguage() + { + var languages = new[] + { + "AB", "AF", "AN", "AR", "AS", "AZ", "BE", "BG", "BN", "BO", "BR", "BS", "CA", "CE", "CO", "CS", + "CU", "CY", "DA", "DE", "EL", "EN", "EO", "ES", "ET", "EU", "FA", "FI", "FJ", "FO", "FR", "FY", + "GA", "GD", "GL", "GV", "HE", "HI", "HR", "HT", "HU", "HY", "ID", "IS", "IT", "JA", "JV", "KA", + "KG", "KO", "KU", "KW", "KY", "LA", "LB", "LI", "LN", "LT", "LV", "MG", "MK", "MN", "MO", "MS", + "MT", "MY", "NB", "NE", "NL", "NN", "NO", "OC", "PL", "PT", "RM", "RO", "RU", "SC", "SE", "SK", + "SL", "SO", "SQ", "SR", "SV", "SW", "TK", "TR", "TY", "UK", "UR", "UZ", "VI", "VO", "YI", "ZH" }; - private static readonly float[] BrowserWeights = - { - 0.45132810566f, - 0.27477061836f, - 0.19384170608f, - 0.06186781118f, - 0.01574236955f - }; + return this.Random.ArrayElement(languages); + } - private static readonly MultiDictionary BrowserOSUsage = new MultiDictionary(StringComparer.Ordinal) - { - {"chrome", "win", 0.89f}, - {"chrome", "mac", 0.09f}, - {"chrome", "lin", 0.02f}, - {"firefox", "win", 0.83f}, - {"firefox", "mac", 0.16f}, - {"firefox", "lin", 0.01f}, - {"opera", "win", 0.91f}, - {"opera", "mac", 0.03f}, - {"opera", "lin", 0.06f}, - {"safari", "win", 0.04f}, - {"safari", "mac", 0.96f}, - {"iexplorer", "win", 1f}, - }; - internal string RandomOS(string browser) - { - //return random OS weighted by browser. + internal string RandomBrowser() + { + return this.Random.WeightedRandom(BrowserNames, BrowserWeights); + } - var lookup = BrowserOSUsage[browser]; + private static readonly string[] BrowserNames = + { + "chrome", + "iexplorer", + "firefox", + "safari", + "opera" + }; + + private static readonly float[] BrowserWeights = + { + 0.45132810566f, + 0.27477061836f, + 0.19384170608f, + 0.06186781118f, + 0.01574236955f + }; + + private static readonly MultiDictionary BrowserOSUsage = new MultiDictionary(StringComparer.Ordinal) + { + {"chrome", "win", 0.89f}, + {"chrome", "mac", 0.09f}, + {"chrome", "lin", 0.02f}, + {"firefox", "win", 0.83f}, + {"firefox", "mac", 0.16f}, + {"firefox", "lin", 0.01f}, + {"opera", "win", 0.91f}, + {"opera", "mac", 0.03f}, + {"opera", "lin", 0.06f}, + {"safari", "win", 0.04f}, + {"safari", "mac", 0.96f}, + {"iexplorer", "win", 1f}, + }; + + internal string RandomOS(string browser) + { + //return random OS weighted by browser. - var osNames = lookup.Keys.ToArray(); + var lookup = BrowserOSUsage[browser]; - var weights = lookup.Values.ToArray(); + var osNames = lookup.Keys.ToArray(); - return this.Random.WeightedRandom(osNames, weights); - } + var weights = lookup.Values.ToArray(); - private static readonly MultiDictionary Proc = new MultiDictionary(StringComparer.Ordinal) - { - {"lin", "i686", 0.50f}, - {"lin", "x86_64", 0.50f}, - {"mac", "Intel", 0.48f}, - {"mac", "PPC", 0.01f}, - {"mac", "U; Intel", 0.48f}, - {"mac", "U; PPC", 0.01f}, - {"win", "", 0.33f}, - {"win", "WOW64", 0.33f}, - {"win", "Win64", 0.33f} - }; + return this.Random.WeightedRandom(osNames, weights); + } - internal string RandomProc(string os) + private static readonly MultiDictionary Proc = new MultiDictionary(StringComparer.Ordinal) { - var lookup = Proc[os]; + {"lin", "i686", 0.50f}, + {"lin", "x86_64", 0.50f}, + {"mac", "Intel", 0.48f}, + {"mac", "PPC", 0.01f}, + {"mac", "U; Intel", 0.48f}, + {"mac", "U; PPC", 0.01f}, + {"win", "", 0.33f}, + {"win", "WOW64", 0.33f}, + {"win", "Win64", 0.33f} + }; + + internal string RandomProc(string os) + { + var lookup = Proc[os]; - var procNames = lookup.Keys.ToArray(); + var procNames = lookup.Keys.ToArray(); - var procWeights = lookup.Values.ToArray(); + var procWeights = lookup.Values.ToArray(); - return this.Random.WeightedRandom(procNames, procWeights); - } + return this.Random.WeightedRandom(procNames, procWeights); + } - internal string BrowserAgent(string browser, string arch) + internal string BrowserAgent(string browser, string arch) + { + if( browser == "firefox" ) { - if( browser == "firefox" ) - { - var firefox_ver = this.Random.Number(5, 15) + RandomRevision(2); - var gecko_ver = "Gecko/20100101 Firefox/" + firefox_ver; - var proc = RandomProc(arch); - var os_ver = (arch == "win") - ? "(Windows NT " + VersionString("nt") + (proc != "" ? "; " + proc : "") - : (arch == "mac") - ? "(Macintosh; " + proc + " Mac OS X " + VersionString("osx") - : "(X11; Linux " + proc; + var firefox_ver = this.Random.Number(5, 15) + RandomRevision(2); + var gecko_ver = "Gecko/20100101 Firefox/" + firefox_ver; + var proc = RandomProc(arch); + var os_ver = (arch == "win") + ? "(Windows NT " + VersionString("nt") + (proc != "" ? "; " + proc : "") + : (arch == "mac") + ? "(Macintosh; " + proc + " Mac OS X " + VersionString("osx") + : "(X11; Linux " + proc; - return "Mozilla/5.0 " + os_ver + "; rv:" + firefox_ver.Substring(0, firefox_ver.Length - 2) + ") " + gecko_ver; - } - if( browser == "chrome" ) - { - var safari = VersionString("safari"); - var os_ver = (arch == "mac") - ? "(Macintosh; " + RandomProc("mac") + " Mac OS X " + VersionString("osx", "_") + ")" - : (arch == "win") - ? "(Windows; U; Windows NT " + VersionString("nt") + ")" - : "(X11; Linux " + RandomProc(arch) + ")"; - - return "Mozilla/5.0 " + os_ver + " AppleWebKit/" + safari + " (KHTML, like Gecko) Chrome/" + VersionString("chrome") + " Safari/" + safari; - } - if( browser == "opera" ) - { - var os_ver = (arch == "win") - ? "(Windows NT " + VersionString("nt") + "; U; " + RandomLanguage() +")" - : (arch == "lin") - ? "(X11; Linux " + RandomProc(arch) + "; U; " + RandomLanguage()+ ")" - : "(Macintosh; Intel Mac OS X " + VersionString("osx") + " U; " + RandomLanguage() + ")"; + return "Mozilla/5.0 " + os_ver + "; rv:" + firefox_ver.Substring(0, firefox_ver.Length - 2) + ") " + gecko_ver; + } + if( browser == "chrome" ) + { + var safari = VersionString("safari"); + var os_ver = (arch == "mac") + ? "(Macintosh; " + RandomProc("mac") + " Mac OS X " + VersionString("osx", "_") + ")" + : (arch == "win") + ? "(Windows; U; Windows NT " + VersionString("nt") + ")" + : "(X11; Linux " + RandomProc(arch) + ")"; + + return "Mozilla/5.0 " + os_ver + " AppleWebKit/" + safari + " (KHTML, like Gecko) Chrome/" + VersionString("chrome") + " Safari/" + safari; + } + if( browser == "opera" ) + { + var os_ver = (arch == "win") + ? "(Windows NT " + VersionString("nt") + "; U; " + RandomLanguage() +")" + : (arch == "lin") + ? "(X11; Linux " + RandomProc(arch) + "; U; " + RandomLanguage()+ ")" + : "(Macintosh; Intel Mac OS X " + VersionString("osx") + " U; " + RandomLanguage() + ")"; - var majorVersion = this.Random.Number(9, 14) + "." + this.Random.Number(0, 99); + var majorVersion = this.Random.Number(9, 14) + "." + this.Random.Number(0, 99); - var presto_ver = "Presto/" + VersionString("presto") + " Version/" + VersionString("presto2"); + var presto_ver = "Presto/" + VersionString("presto") + " Version/" + VersionString("presto2"); - return "Opera/" + majorVersion + " " + os_ver + " " + presto_ver; - } - if( browser == "safari" ) - { - var safari = VersionString("safari"); - var ver = this.Random.Number(4, 7) + "." + this.Random.Number(0, 1) + "." + this.Random.Number(0, 10); - var os_ver = (arch == "mac") - ? "(Macintosh; " + RandomProc("mac") + " Mac OS X " + VersionString("osx", "_") + " rv:" + this.Random.Number(2, 6) + ".0; " + RandomLanguage() + ")" - : "(Windows; U; Windows NT " + VersionString("nt") + ")"; + return "Opera/" + majorVersion + " " + os_ver + " " + presto_ver; + } + if( browser == "safari" ) + { + var safari = VersionString("safari"); + var ver = this.Random.Number(4, 7) + "." + this.Random.Number(0, 1) + "." + this.Random.Number(0, 10); + var os_ver = (arch == "mac") + ? "(Macintosh; " + RandomProc("mac") + " Mac OS X " + VersionString("osx", "_") + " rv:" + this.Random.Number(2, 6) + ".0; " + RandomLanguage() + ")" + : "(Windows; U; Windows NT " + VersionString("nt") + ")"; - return "Mozilla/5.0 " + os_ver + " AppleWebKit/" + safari + " (KHTML, like Gecko) Version/" + ver + " Safari/" + safari; - } - if( browser == "iexplorer" ) + return "Mozilla/5.0 " + os_ver + " AppleWebKit/" + safari + " (KHTML, like Gecko) Version/" + ver + " Safari/" + safari; + } + if( browser == "iexplorer" ) + { + var ver = this.Random.Number(7, 11); + + if( ver >= 11 ) { - var ver = this.Random.Number(7, 11); - - if( ver >= 11 ) - { - //http://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx - return "Mozilla/5.0 (Windows NT 6." + this.Random.Number(1, 3) + "; Trident/7.0; " + this.Random.ArrayElement(new[] {"Touch; ", ""}) + - "rv:11.0) like Gecko"; - } - - //http://msdn.microsoft.com/en-us/library/ie/ms537503(v=vs.85).aspx - return "Mozilla/5.0 (compatible; MSIE " + ver + ".0; Windows NT " + VersionString("nt") + "; Trident/" + - VersionString("trident") + ((this.Random.Number(0, 1) == 1) ? "; .NET CLR " + VersionString("net") : "") + ")"; + //http://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx + return "Mozilla/5.0 (Windows NT 6." + this.Random.Number(1, 3) + "; Trident/7.0; " + this.Random.ArrayElement(new[] {"Touch; ", ""}) + + "rv:11.0) like Gecko"; } - return string.Empty; + + //http://msdn.microsoft.com/en-us/library/ie/ms537503(v=vs.85).aspx + return "Mozilla/5.0 (compatible; MSIE " + ver + ".0; Windows NT " + VersionString("nt") + "; Trident/" + + VersionString("trident") + ((this.Random.Number(0, 1) == 1) ? "; .NET CLR " + VersionString("net") : "") + ")"; } + return string.Empty; + } - public string Generate() - { - var browser = RandomBrowser(); - var os = RandomOS(browser); + public string Generate() + { + var browser = RandomBrowser(); + var os = RandomOS(browser); - return BrowserAgent(browser, os); - } + return BrowserAgent(browser, os); } } \ No newline at end of file