Skip to content

Commit 1b373de

Browse files
committed
Made FeedReader Async
1 parent 235748b commit 1b373de

File tree

9 files changed

+154
-62
lines changed

9 files changed

+154
-62
lines changed

.github/publishnuget.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Publish Nuget Package
2+
3+
on:
4+
release:
5+
types:
6+
- created
7+
8+
jobs:
9+
build:
10+
11+
runs-on: windows-latest
12+
strategy:
13+
matrix:
14+
dotnet-version: [ '9.0.x' ]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup .NET ${{ matrix.dotnet-version }}
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: ${{ matrix.dotnet-version }}
23+
24+
- name: Setup NuGet
25+
uses: NuGet/setup-nuget@v2
26+
27+
- name: Restore dependencies
28+
run: dotnet restore
29+
30+
- name: Build
31+
run: dotnet build -c Release --no-restore /p:Version="${{ github.event.release.tag_name }}"
32+
33+
- name: Run tests
34+
run: dotnet test -c Release --no-restore --no-build
35+
36+
- name: Create packages
37+
run: |
38+
dotnet pack ${{ github.event.repository.name }} -c Release --no-restore --no-build -p:Version="${{ github.event.release.tag_name }}"
39+
dotnet pack ${{ github.event.repository.name }}.Configuration -c Release --no-restore --no-build -p:Version="${{ github.event.release.tag_name }}"
40+
dotnet pack ${{ github.event.repository.name }}.DependencyInjection -c Release --no-restore --no-build -p:Version="${{ github.event.release.tag_name }}"
41+
42+
- name: Publish
43+
run: dotnet nuget push **\*.nupkg -s 'https://api.nuget.org/v3/index.json' -k ${{secrets.NUGET_API_KEY}}

.github/test.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Test
2+
3+
on:
4+
push
5+
6+
jobs:
7+
build:
8+
9+
runs-on: windows-latest
10+
strategy:
11+
matrix:
12+
dotnet-version: [ '9.0.x' ]
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Setup .NET ${{ matrix.dotnet-version }}
18+
uses: actions/setup-dotnet@v4
19+
with:
20+
dotnet-version: ${{ matrix.dotnet-version }}
21+
22+
- name: Setup NuGet
23+
uses: NuGet/setup-nuget@v2
24+
25+
- name: Restore dependencies
26+
run: dotnet restore
27+
28+
- name: Run tests
29+
run: dotnet test --no-restore

Gfx/icon.psd

-39.8 KB
Binary file not shown.

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# ![Logo](https://raw.githubusercontent.com/RobThree/SimpleFeedReader/master/Gfx/icon.png) SimpleFeedReader
1+
# ![Logo](https://raw.githubusercontent.com/RobThree/SimpleFeedReader/master/SimpleFeedReader/icon.png) SimpleFeedReader
22

33
Easy to use, simple, Syndication feed reader (Atom / RSS). Available as [Nuget Package](https://www.nuget.org/packages/SimpleFeedReader/).
44

55
## Usage
66

77
```c#
88
var reader = new FeedReader();
9-
var items = reader.RetrieveFeed("http://www.nytimes.com/services/xml/rss/nyt/International.xml");
9+
var items = await reader.RetrieveFeedAsync("http://www.nytimes.com/services/xml/rss/nyt/International.xml");
1010

1111
foreach (var i in items)
1212
Console.WriteLine($"{i.Date.ToString("g")}\t{i.Title}");
@@ -31,8 +31,6 @@ The `FeedReader` also accepts an optional `FeedNormalizer` (needs to implement t
3131
3232
You can implement your own `IFeedItemNormalizer` (see the [UnitTest project](https://github.com/RobThree/SimpleFeedReader/tree/master/SimpleFeedReaderTests) for examples) to handle 'normalization' differently to your desire. The `FeedReader` has some convienience methods like `RetrieveFeeds()` that retrieve more than one feed.
3333
34-
Included in the project is a [Sandcastle Helpfile Builder project](https://github.com/RobThree/SimpleFeedReader/tree/master/Help) that creates a helpfile for easy to use documentation.
35-
3634
The project is aimed at easy, don't-make-me-think, retrieval of syndication feeds' entries. It is by no means intended as full-fledged feedreader. It is, however, easily extensible for your purposes (again, see the [UnitTest project](https://github.com/RobThree/SimpleFeedReader/tree/master/SimpleFeedReaderTests) for examples; the `ExtendedFeedItem` and `ExtendedFeedItemNormalizer` are nice concrete examples of this idea).
3735
3836
[![Build status](https://ci.appveyor.com/api/projects/status/wy2swaddwhukotg8)](https://ci.appveyor.com/project/RobIII/simplefeedreader) <a href="https://www.nuget.org/packages/SimpleFeedReader/"><img src="http://img.shields.io/nuget/v/SimpleFeedReader.svg?style=flat-square" alt="NuGet version" height="18"></a>

SimpleFeedReader/DefaultFeedItemNormalizer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ namespace SimpleFeedReader;
99
/// The <see cref="DefaultFeedItemNormalizer"/> normalizes <see cref="FeedItem.Title"/>,
1010
/// <see cref="FeedItem.Content"/> and <see cref="FeedItem.Summary"/> of <see cref="FeedItem"/>s to the point where
1111
/// they no longer contain any HTML, redundant whitespace, un-normalized unicode chars and other control chars like
12-
/// tabs, newlines or backspaces. The <see cref="FeedItem"/>'s <see cref="FeedItem.Date"/> property will contain
13-
/// whichever date is latest; the <see cref="FeedItem.PublishDate"/> or <see cref="FeedItem.LastUpdatedDate"/>.
12+
/// tabs, newlines or backspaces.
1413
/// </summary>
1514
/// <remarks>
1615
/// You can implement a normalizer yourself by implementing the <see cref="IFeedItemNormalizer"/> interface.

SimpleFeedReader/FeedReader.cs

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.ServiceModel.Syndication;
1+
using System.Net.Http;
2+
using System.ServiceModel.Syndication;
23
using System.Xml;
34

45
namespace SimpleFeedReader;
@@ -18,6 +19,8 @@ namespace SimpleFeedReader;
1819
/// </param>
1920
public class FeedReader(IFeedItemNormalizer defaultFeedItemNormalizer, bool throwOnError)
2021
{
22+
private static readonly HttpClient _httpclient = new();
23+
2124
/// <summary>
2225
/// Gets the default FeedItemNormalizer the <see cref="FeedReader"/> will use when normalizing
2326
/// <see cref="SyndicationItem"/>s.
@@ -60,10 +63,11 @@ public FeedReader(IFeedItemNormalizer defaultFeedItemNormalizer)
6063
/// </summary>
6164
/// <param name="uris">The uri's of the feeds to retrieve.</param>
6265
/// <returns>
63-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
66+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
6467
/// </returns>
6568
/// <remarks>This is a convenience method.</remarks>
66-
public IEnumerable<FeedItem> RetrieveFeeds(IEnumerable<string> uris) => RetrieveFeeds(uris, DefaultNormalizer);
69+
public Task<IEnumerable<FeedItem>> RetrieveFeedsAsync(IEnumerable<string> uris)
70+
=> RetrieveFeedsAsync(uris, DefaultNormalizer);
6771

6872
/// <summary>
6973
/// Retrieves the specified feeds.
@@ -73,29 +77,26 @@ public FeedReader(IFeedItemNormalizer defaultFeedItemNormalizer)
7377
/// The <see cref="IFeedItemNormalizer"/> to use when normalizing <see cref="FeedItem"/>s.
7478
/// </param>
7579
/// <returns>
76-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
80+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
7781
/// </returns>
7882
/// <remarks>This is a convenience method.</remarks>
79-
public IEnumerable<FeedItem> RetrieveFeeds(IEnumerable<string> uris, IFeedItemNormalizer normalizer)
83+
public async Task<IEnumerable<FeedItem>> RetrieveFeedsAsync(IEnumerable<string> uris, IFeedItemNormalizer normalizer)
8084
{
81-
var items = new List<FeedItem>();
82-
foreach (var u in uris)
83-
{
84-
items.AddRange(RetrieveFeed(u, normalizer));
85-
}
85+
var tasks = uris.Select(uri => RetrieveFeedAsync(uri, normalizer));
86+
await Task.WhenAll(tasks);
8687

87-
return items;
88+
return tasks.SelectMany(tasks => tasks.Result);
8889
}
8990

9091
/// <summary>
9192
/// Retrieves the specified feed.
9293
/// </summary>
9394
/// <param name="uri">The uri of the feed to retrieve.</param>
9495
/// <returns>
95-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
96+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
9697
/// </returns>
97-
public IEnumerable<FeedItem> RetrieveFeed(string uri)
98-
=> RetrieveFeed(uri, DefaultNormalizer);
98+
public Task<IEnumerable<FeedItem>> RetrieveFeedAsync(string uri)
99+
=> RetrieveFeedAsync(uri, DefaultNormalizer);
99100

100101
/// <summary>
101102
/// Retrieves the specified feed.
@@ -105,13 +106,14 @@ public IEnumerable<FeedItem> RetrieveFeed(string uri)
105106
/// The <see cref="IFeedItemNormalizer"/> to use when normalizing <see cref="FeedItem"/>s.
106107
/// </param>
107108
/// <returns>
108-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
109+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
109110
/// </returns>
110-
public IEnumerable<FeedItem> RetrieveFeed(string uri, IFeedItemNormalizer normalizer)
111+
public async Task<IEnumerable<FeedItem>> RetrieveFeedAsync(string uri, IFeedItemNormalizer normalizer)
111112
{
112113
try
113114
{
114-
return RetrieveFeed(XmlReader.Create(uri), normalizer);
115+
using var reader = await GetXmlReaderAsync(uri);
116+
return await RetrieveFeedAsync(reader, normalizer);
115117
}
116118
catch
117119
{
@@ -128,9 +130,10 @@ public IEnumerable<FeedItem> RetrieveFeed(string uri, IFeedItemNormalizer normal
128130
/// </summary>
129131
/// <param name="xmlReader">The <see cref="XmlReader"/> to use to read the items from.</param>
130132
/// <returns>
131-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
133+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
132134
/// </returns>
133-
public IEnumerable<FeedItem> RetrieveFeed(XmlReader xmlReader) => RetrieveFeed(xmlReader, DefaultNormalizer);
135+
public Task<IEnumerable<FeedItem>> RetrieveFeedAsync(XmlReader xmlReader) =>
136+
RetrieveFeedAsync(xmlReader, DefaultNormalizer);
134137

135138
/// <summary>
136139
/// Retrieves the specified feed.
@@ -140,9 +143,9 @@ public IEnumerable<FeedItem> RetrieveFeed(string uri, IFeedItemNormalizer normal
140143
/// The <see cref="IFeedItemNormalizer"/> to use when normalizing <see cref="FeedItem"/>s.
141144
/// </param>
142145
/// <returns>
143-
/// Returns an <see cref="IEnumerable&lt;FeedItem&gt;"/> of retrieved <see cref="FeedItem"/>s.
146+
/// Returns a task that resolves to an <see cref="IEnumerable{FeedItem}"/> of retrieved <see cref="FeedItem"/>s.
144147
/// </returns>
145-
public IEnumerable<FeedItem> RetrieveFeed(XmlReader xmlReader, IFeedItemNormalizer normalizer)
148+
public Task<IEnumerable<FeedItem>> RetrieveFeedAsync(XmlReader xmlReader, IFeedItemNormalizer normalizer)
146149
{
147150
if (xmlReader == null)
148151
{
@@ -154,14 +157,10 @@ public IEnumerable<FeedItem> RetrieveFeed(XmlReader xmlReader, IFeedItemNormaliz
154157
throw new ArgumentNullException(nameof(normalizer));
155158
}
156159

157-
var items = new List<FeedItem>();
158160
try
159161
{
160162
var feed = SyndicationFeed.Load(xmlReader);
161-
foreach (var item in feed.Items)
162-
{
163-
items.Add(normalizer.Normalize(feed, item));
164-
}
163+
return Task.FromResult(feed.Items.Select(item => normalizer.Normalize(feed, item)));
165164
}
166165
catch
167166
{
@@ -170,6 +169,23 @@ public IEnumerable<FeedItem> RetrieveFeed(XmlReader xmlReader, IFeedItemNormaliz
170169
throw;
171170
}
172171
}
173-
return items;
172+
return Task.FromResult(Enumerable.Empty<FeedItem>());
173+
}
174+
175+
private static async Task<XmlReader> GetXmlReaderAsync(string uri)
176+
{
177+
if (Uri.IsWellFormedUriString(uri, UriKind.Absolute))
178+
{
179+
var response = await _httpclient.GetAsync(uri);
180+
response.EnsureSuccessStatusCode();
181+
var stream = await response.Content.ReadAsStreamAsync();
182+
return XmlReader.Create(stream);
183+
}
184+
else if (File.Exists(uri))
185+
{
186+
var stream = File.OpenRead(uri);
187+
return XmlReader.Create(stream);
188+
}
189+
throw new FileNotFoundException($"The file '{uri}' was not found.");
174190
}
175191
}

SimpleFeedReader/SimpleFeedReader.csproj

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,24 @@
2222
<PackageIcon>icon.png</PackageIcon>
2323
<Copyright>(c) 2014 - 2025 Devcorner.nl</Copyright>
2424
<PackageLicenseExpression>MIT</PackageLicenseExpression>
25+
<GenerateDocumentationFile>True</GenerateDocumentationFile>
26+
<PackageReadmeFile>README.md</PackageReadmeFile>
2527
</PropertyGroup>
2628

2729
<ItemGroup>
28-
<None Include="..\Gfx\icon.png" Pack="true" PackagePath="" />
30+
<None Include="icon.png" Pack="true" PackagePath="" />
31+
<None Include="..\README.md">
32+
<Pack>True</Pack>
33+
<PackagePath>\</PackagePath>
34+
</None>
2935
</ItemGroup>
3036

3137
<ItemGroup>
3238
<PackageReference Include="PolySharp" Version="1.15.0">
3339
<PrivateAssets>all</PrivateAssets>
3440
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3541
</PackageReference>
42+
<PackageReference Include="System.Net.Http" Version="4.3.4" />
3643
<PackageReference Include="System.ServiceModel.Syndication" Version="9.0.2" />
3744
</ItemGroup>
3845
</Project>
File renamed without changes.

0 commit comments

Comments
 (0)