Skip to content

Commit 8c7aa70

Browse files
committed
Make calling TryResolveXX easy.
1 parent 985f14f commit 8c7aa70

4 files changed

Lines changed: 145 additions & 119 deletions

File tree

converter/generator/DocToStaticPagesTransformer.cs

Lines changed: 110 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Buffers;
2+
using System.Diagnostics;
23
using System.Net;
34
using System.Xml.Linq;
45
using AngleSharp.Dom;
@@ -120,41 +121,46 @@ protected internal override bool TryResolveHref(string href, string sourceDir, o
120121
{
121122
titleEn = null;
122123

123-
if (!href.StartsWith('/') && Uri.IsWellFormedUriString(href, UriKind.Relative))
124+
var parts = new UrlParts(href);
125+
126+
if (parts.IsAbosolute || href.StartsWith('/') || href.StartsWith('#'))
124127
{
125-
var fullPath = Path.GetFullPath(href, sourceDir);
126-
if (fullPath.StartsWith(SourceFolder)
127-
&& Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(fullPath.AsSpan()))) is { IsEmpty: false } targetBookDirContainer)
128-
{
129-
var targetFile = WebUtility.UrlDecode(fullPath[(targetBookDirContainer.Length + 1)..].Replace('\\', '/'));
128+
result = href;
129+
return true;
130+
}
130131

131-
if (PageLinks.TryGetValue(targetFile, out var link)
132-
|| (MovedPages.TryGetValue(targetFile, out var movedToFile) && PageLinks.TryGetValue(movedToFile, out link)))
132+
var path = parts is { Query.Length: 0, Hash.Length: 0 } ? href : parts.Path.ToString();
133+
Debug.Assert(!path.IsEmpty);
134+
135+
var fullPath = Path.GetFullPath(path, sourceDir);
136+
if (fullPath.StartsWith(SourceFolder)
137+
&& Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(fullPath.AsSpan()))) is { IsEmpty: false } targetBookDirContainer)
138+
{
139+
var targetFile = WebUtility.UrlDecode(fullPath[(targetBookDirContainer.Length + 1)..].Replace('\\', '/'));
140+
141+
if (PageLinks.TryGetValue(targetFile, out var link)
142+
|| (MovedPages.TryGetValue(targetFile, out var movedToFile) && PageLinks.TryGetValue(movedToFile, out link)))
143+
{
144+
if (Language == "en")
133145
{
134-
if (Language == "en")
135-
{
136-
result = '/'.TrySurroundEach(link.book, link.url);
137-
}
138-
else
139-
{
140-
result = '/'.TrySurroundEach(link.book, link.url, Language);
141-
}
146+
result = '/'.TrySurroundEach(link.book, link.url);
147+
}
148+
else
149+
{
150+
result = '/'.TrySurroundEach(link.book, link.url, Language);
151+
}
142152

143-
titleEn = link.titleEn;
144-
return true;
153+
if (!parts.Query.IsEmpty || !parts.Hash.IsEmpty)
154+
{
155+
result = $"{result}{parts.Query}{parts.Hash}";
145156
}
146-
}
147157

148-
result = "Unknown href mapping";
149-
return false;
150-
}
151-
else if (href.StartsWith("mailto:") || href.StartsWith("javascript:") || Uri.IsWellFormedUriString(href, UriKind.Absolute))
152-
{
153-
result = href;
154-
return true;
158+
titleEn = link.titleEn;
159+
return true;
160+
}
155161
}
156162

157-
result = "Unrecognized href pattern";
163+
result = "Unknown href mapping";
158164
return false;
159165
}
160166

@@ -163,93 +169,106 @@ protected override string GetSharedImageSrc(string path, string fileName)
163169

164170
protected internal override bool TryResolveSrc(string src, string sourceDir, out string result, out (string src, string dst)? copy)
165171
{
166-
if (src.StartsWith("../images/"))
172+
var parts = new UrlParts(src);
173+
174+
if (parts.IsAbosolute || src.StartsWith('/'))
167175
{
168-
var srcImg = new FileInfo(Path.GetFullPath(src, sourceDir));
169-
var needsCopy = true;
176+
result = src;
177+
copy = null;
178+
return true;
179+
}
170180

171-
var fileName = Path.GetFileName(src);
172-
if (SharedImages.TryGetValue(fileName, out result!))
173-
{
174-
needsCopy = false;
175-
}
176-
else if (srcImg.Exists)
177-
{
178-
result = '/'.TryPrefixEach(BookUrlName, Language, src["../".Length..]);
181+
var path = parts is { Query.Length: 0, Hash.Length: 0 } ? src : parts.Path.ToString();
182+
Debug.Assert(!path.IsEmpty);
179183

180-
if (Language == "en")
184+
var indexOfImages = path.IndexOf("images/");
185+
Debug.Assert(indexOfImages > -1);
186+
187+
var srcImg = new FileInfo(Path.GetFullPath(path, sourceDir));
188+
var needsCopy = true;
189+
190+
var fileName = Path.GetFileName(path);
191+
if (SharedImages.TryGetValue(fileName, out result!))
192+
{
193+
needsCopy = false;
194+
}
195+
else if (srcImg.Exists)
196+
{
197+
result = '/'.TryPrefixEach(BookUrlName, Language, path[indexOfImages..]);
198+
199+
if (Language == "en")
200+
{
201+
if (!EnglishImages.TryGetValue(path, out var visited))
181202
{
182-
if (!EnglishImages.TryGetValue(src, out var visited))
183-
{
184-
var size = srcImg.Length;
185-
var hash = FileHash.UInt64FromFile(srcImg.FullName);
203+
var size = srcImg.Length;
204+
var hash = FileHash.UInt64FromFile(srcImg.FullName);
186205

187-
EnglishImages.Add(src, (size, hash, result));
188-
}
189-
else
190-
{
191-
result = visited.url;
192-
needsCopy = false;
193-
}
206+
EnglishImages.Add(path, (size, hash, result));
194207
}
195208
else
196209
{
197-
if (VisitedImages.TryGetValue(src, out var prevUrl))
198-
{
199-
result = prevUrl;
200-
needsCopy = false;
201-
}
202-
else
203-
{
204-
VisitedImages.Add(src, result);
205-
206-
if (EnglishImages.TryGetValue(src, out var visited) && srcImg.Length == visited.size && FileHash.UInt64FromFile(srcImg.FullName) == visited.hash)
207-
{
208-
result = VisitedImages[src] = visited.url;
209-
needsCopy = false;
210-
}
211-
}
210+
result = visited.url;
211+
needsCopy = false;
212212
}
213213
}
214214
else
215215
{
216-
var srcImgEn = $"{SourceFolderEn}{srcImg.FullName.AsSpan(SourceFolderEn.Length)}";
217-
218-
if (!File.Exists(srcImgEn))
216+
if (VisitedImages.TryGetValue(path, out var prevUrl))
219217
{
220-
result = "Image src not found";
221-
copy = null;
222-
return false;
218+
result = prevUrl;
219+
needsCopy = false;
223220
}
221+
else
222+
{
223+
VisitedImages.Add(path, result);
224224

225-
result = '/'.TryPrefixEach(BookUrlName, "en", src["../".Length..]);
226-
needsCopy = false;
227-
}
228-
229-
if (UseWebp)
230-
{
231-
var resultDir = Path.GetDirectoryName(result.AsSpan());
232-
var resultFileName = Path.GetFileNameWithoutExtension(result.AsSpan());
233-
234-
result = $"{resultDir}/{resultFileName}.webp";
225+
if (EnglishImages.TryGetValue(path, out var visited) && srcImg.Length == visited.size && FileHash.UInt64FromFile(srcImg.FullName) == visited.hash)
226+
{
227+
result = VisitedImages[path] = visited.url;
228+
needsCopy = false;
229+
}
230+
}
235231
}
232+
}
233+
else
234+
{
235+
var srcImgEn = $"{SourceFolderEn}{srcImg.FullName.AsSpan(SourceFolderEn.Length)}";
236236

237-
if (!needsCopy)
237+
if (!File.Exists(srcImgEn))
238238
{
239+
result = "Image src not found";
239240
copy = null;
240-
}
241-
else
242-
{
243-
var dstImg = Path.Combine(OutputFolder, Language, src["../".Length..]);
244-
copy = (srcImg.FullName, dstImg);
241+
return false;
245242
}
246243

247-
return true;
244+
result = '/'.TryPrefixEach(BookUrlName, "en", path[indexOfImages..]);
245+
needsCopy = false;
248246
}
249247

250-
result = "Unrecognized src";
251-
copy = null;
252-
return false;
248+
if (UseWebp)
249+
{
250+
var resultDir = Path.GetDirectoryName(result.AsSpan());
251+
var resultFileName = Path.GetFileNameWithoutExtension(result.AsSpan());
252+
253+
result = $"{resultDir}/{resultFileName}.webp";
254+
}
255+
256+
if (!needsCopy)
257+
{
258+
copy = null;
259+
}
260+
else
261+
{
262+
var dstImg = Path.Combine(OutputFolder, Language, path[indexOfImages..]);
263+
copy = (srcImg.FullName, dstImg);
264+
}
265+
266+
if (!parts.Query.IsEmpty)
267+
{
268+
result = $"{result}{parts.Query}";
269+
}
270+
271+
return true;
253272
}
254273

255274
protected override IHtmlElement? TransformImage(IHtmlDocument document, IHtmlImageElement img, string sourceFile, string sourceDir)

converter/generator/DocTransformer.cs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -134,30 +134,26 @@ internal protected virtual void Transform(IHtmlDocument document, IHtmlHeadEleme
134134

135135
protected virtual IHtmlElement? TransformAnchor(IHtmlDocument document, IHtmlAnchorElement a, string sourceFile, string sourceDir)
136136
{
137-
if (a.GetAttribute("href") is string href && !href.IsBlank)
137+
if (a.GetAttribute("href") is not string href || href.IsBlank)
138138
{
139-
if (href.StartsWith('#'))
140-
{
141-
return null;
142-
}
143-
144-
var parts = new UrlParts(href);
145-
146-
if (TryResolveHref(parts.File.Length == href.Length ? href : parts.File.ToString(), sourceDir, out var result, out var title))
147-
{
148-
a.SetAttribute("href", $"{result}{parts.Query}{parts.Hash}");
139+
return null;
140+
}
149141

150-
if (a.Title.IsBlank && !title.IsEmpty)
151-
{
152-
a.Title = title;
153-
}
142+
if (TryResolveHref(href, sourceDir, out var result, out var title))
143+
{
144+
a.SetAttribute("href", result);
154145

155-
return a;
156-
}
157-
else
146+
if ((a.Title.IsBlank || a.Title.Contains(':')) && !title.IsEmpty)
158147
{
159-
ReportProblem(sourceFile, result, parts.File.ToString(), a.SourceReference?.Position);
148+
a.Title = title;
160149
}
150+
151+
return a;
152+
}
153+
else
154+
{
155+
var parts = new UrlParts(href);
156+
ReportProblem(sourceFile, result, parts.Path.ToString(), a.SourceReference?.Position);
161157
}
162158

163159
return null;
@@ -167,16 +163,14 @@ internal protected virtual void Transform(IHtmlDocument document, IHtmlHeadEleme
167163

168164
protected virtual IHtmlElement? TransformImage(IHtmlDocument document, IHtmlImageElement img, string sourceFile, string sourceDir)
169165
{
170-
if (img.GetAttribute("src") is not string src)
166+
if (img.GetAttribute("src") is not string src || src.IsBlank)
171167
{
172168
return null;
173169
}
174170

175-
var parts = new UrlParts(src);
176-
177-
if (TryResolveSrc(parts.File.ToString(), sourceDir, out var result, out var copy))
171+
if (TryResolveSrc(src, sourceDir, out var result, out var copy))
178172
{
179-
img.SetAttribute("src", $"{result}{parts.Query}{parts.Hash}");
173+
img.SetAttribute("src", result);
180174

181175
if (copy is (string srcImg, string dstImg))
182176
{
@@ -188,7 +182,8 @@ internal protected virtual void Transform(IHtmlDocument document, IHtmlHeadEleme
188182
}
189183
else
190184
{
191-
ReportProblem(sourceFile, result, parts.File.ToString(), img.SourceReference?.Position);
185+
var parts = new UrlParts(src);
186+
ReportProblem(sourceFile, result, parts.Path.ToString(), img.SourceReference?.Position);
192187
}
193188

194189
return null;

converter/generator/UrlParts.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
internal readonly ref struct UrlParts
44
{
5-
public ReadOnlySpan<char> File { get; }
5+
public bool IsAbosolute { get; }
6+
public ReadOnlySpan<char> Path { get; }
67
public ReadOnlySpan<char> Query { get; }
78
public ReadOnlySpan<char> Hash { get; }
89

@@ -25,6 +26,14 @@ public UrlParts(string url)
2526
span = span[..sep];
2627
}
2728

28-
File = sep > -1 ? span[..sep] : span;
29+
if (Uri.TryCreate(url, UriKind.Absolute, out var uri))
30+
{
31+
Path = uri.AbsolutePath;
32+
IsAbosolute = true;
33+
}
34+
else
35+
{
36+
Path = sep > -1 ? span[..sep] : span;
37+
}
2938
}
3039
}

converter/tests/DocToStaticPagesTransformerTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ public class DocToStaticPagesTransformerTests
88
[InlineData("./A/App/A.html", "app", "en", "/app/a/", "App A")]
99
[InlineData("./A/App/B.html", "app", "de", "/app/b/de/", "App B")]
1010
[InlineData("./A/App/B_Script.html", "app", "de", "/build/b-scripts/de/", "B Scripts (Moved from App)")]
11-
public async Task HrefShouldResolveCorrectly(string href, string book, string language, string expectedUrl, string expectedTitle)
11+
[InlineData("/link.aspx?a=b#h", "app", "de", "/link.aspx?a=b#h", null)]
12+
[InlineData("http://localhost/link.aspx?a=b", "app", "de", "http://localhost/link.aspx?a=b", null)]
13+
[InlineData("mailto:a@b.lan", "app", "de", "mailto:a@b.lan", null)]
14+
public async Task HrefShouldResolveCorrectly(string href, string book, string language, string expectedUrl, string? expectedTitle)
1215
{
1316
var bookDir = Path.GetFullPath("../../../../converter/tests/books/" + book, AppContext.BaseDirectory);
1417
var args = new DocToStaticPagesTransformerArgs

0 commit comments

Comments
 (0)