Skip to content

Commit 27f9b40

Browse files
committed
phone util added
1 parent cfa7da7 commit 27f9b40

File tree

3 files changed

+178
-2
lines changed

3 files changed

+178
-2
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using System.Text;
2+
3+
namespace SharedKernel.Helpers;
4+
5+
public static class PhoneUtil
6+
{
7+
public static bool TryFormatArmenianMsisdn(string? input, out string? formatted)
8+
{
9+
formatted = null;
10+
if (string.IsNullOrWhiteSpace(input))
11+
{
12+
return false;
13+
}
14+
15+
var s = input.Trim();
16+
17+
// Special-case exact "(374)" prefix
18+
if (s.StartsWith("(374)", StringComparison.Ordinal))
19+
{
20+
var rest = DigitsOnly(s.AsSpan(5));
21+
if (rest.Length == 8)
22+
{
23+
formatted = "+374" + rest;
24+
return true;
25+
}
26+
27+
formatted = input;
28+
return false;
29+
}
30+
31+
// General tolerant parse: allow spaces, dashes, dots, parentheses; '+' only at start
32+
var sb = new StringBuilder(s.Length);
33+
for (var i = 0; i < s.Length; i++)
34+
{
35+
var c = s[i];
36+
if (c is >= '0' and <= '9')
37+
{
38+
sb.Append(c);
39+
continue;
40+
}
41+
42+
if (i == 0 && c == '+')
43+
{
44+
continue;
45+
}
46+
47+
if (c == ' ' || c == '-' || c == '.' || c == '(' || c == ')' || char.IsWhiteSpace(c))
48+
{
49+
continue;
50+
}
51+
52+
formatted = input;
53+
return false;
54+
}
55+
56+
var digits = sb.ToString();
57+
var span = digits.AsSpan();
58+
ReadOnlySpan<char> last8;
59+
60+
var plusAtStart = s[0] == '+';
61+
if (plusAtStart)
62+
{
63+
if (span.Length == 11 && span.StartsWith("374".AsSpan()))
64+
{
65+
last8 = span[3..];
66+
}
67+
else
68+
{
69+
formatted = input;
70+
return false;
71+
}
72+
}
73+
else
74+
{
75+
switch (span.Length)
76+
{
77+
case 11 when span.StartsWith("374".AsSpan()):
78+
last8 = span[3..];
79+
break;
80+
case 9 when span[0] == '0':
81+
last8 = span[1..];
82+
break;
83+
case 8:
84+
last8 = span;
85+
break;
86+
default:
87+
formatted = input;
88+
return false;
89+
}
90+
}
91+
92+
if (last8.Length != 8)
93+
{
94+
formatted = input;
95+
return false;
96+
}
97+
98+
formatted = $"+374{last8.ToString()}";
99+
return true;
100+
}
101+
102+
private static string DigitsOnly(ReadOnlySpan<char> span)
103+
{
104+
var sb = new StringBuilder(span.Length);
105+
foreach (var c in span)
106+
{
107+
if (c is >= '0' and <= '9')
108+
{
109+
sb.Append(c);
110+
}
111+
}
112+
113+
return sb.ToString();
114+
}
115+
}

src/SharedKernel/SharedKernel.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>1.6.4</Version>
11+
<Version>1.6.5</Version>
1212
<PackageId>Pandatech.SharedKernel</PackageId>
1313
<Title>Pandatech Shared Kernel Library</Title>
1414
<PackageTags>Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar</PackageTags>
1515
<Description>Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-sharedkernel</RepositoryUrl>
17-
<PackageReleaseNotes>Nuget updates</PackageReleaseNotes>
17+
<PackageReleaseNotes>Phone util added</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using SharedKernel.Helpers;
2+
3+
namespace SharedKernel.Tests;
4+
5+
public class PhoneUtilTests
6+
{
7+
[Theory]
8+
[InlineData("+37493910593", "+37493910593")]
9+
[InlineData("37493910593", "+37493910593")]
10+
[InlineData("093910593", "+37493910593")]
11+
[InlineData("93910593", "+37493910593")]
12+
[InlineData("(374)93910593", "+37493910593")]
13+
// tolerant separators
14+
[InlineData("+374 93 910 593", "+37493910593")]
15+
[InlineData("374-93-910-593", "+37493910593")]
16+
[InlineData("(374) 93 910 593", "+37493910593")]
17+
public void Formats_Armenian_Numbers_When_Valid(string input, string expected)
18+
{
19+
var ok = PhoneUtil.TryFormatArmenianMsisdn(input, out var formatted);
20+
Assert.True(ok);
21+
Assert.Equal(expected, formatted);
22+
}
23+
24+
[Theory]
25+
[InlineData("+12025550199")] // foreign
26+
[InlineData("441234567890")] // foreign
27+
[InlineData("+++37493910593")] // invalid
28+
[InlineData("37493A10593")] // invalid char
29+
[InlineData("(374)1234567")] // only 7 digits after prefix
30+
[InlineData("123456789")] // 9 digits but not starting with 0
31+
public void Returns_Original_When_Not_Armenian(string input)
32+
{
33+
var ok = PhoneUtil.TryFormatArmenianMsisdn(input, out var formatted);
34+
Assert.False(ok);
35+
Assert.Equal(input, formatted);
36+
}
37+
38+
[Fact]
39+
public void Empty_String_Returns_False_And_Null()
40+
{
41+
var ok = PhoneUtil.TryFormatArmenianMsisdn("", out var formatted);
42+
Assert.False(ok);
43+
Assert.Null(formatted);
44+
}
45+
46+
[Fact]
47+
public void Whitespace_String_Returns_False_And_Null()
48+
{
49+
var ok = PhoneUtil.TryFormatArmenianMsisdn(" ", out var formatted);
50+
Assert.False(ok);
51+
Assert.Null(formatted);
52+
}
53+
54+
[Fact]
55+
public void Null_Returns_False_And_Null()
56+
{
57+
var ok = PhoneUtil.TryFormatArmenianMsisdn(null, out var formatted);
58+
Assert.False(ok);
59+
Assert.Null(formatted);
60+
}
61+
}

0 commit comments

Comments
 (0)