- Add Blogex.LinkValidator module to validate /blog/{id}/{slug} semantics
- Add Blogex.LinkError exception with actionable error messages
- Integrate validation into Blogex.Blog via @before_compile callback
- Add unit tests (34) and integration tests (4) for link validation
- Add test fixtures (valid/invalid posts) in blogex/priv/blog/test/
Closes: validate-internal-link-semantics-in-blog-post-markdown-bodies-at-compile-time-h3hb
Closes: define-link-semantic-validation-logic-in-blogex-7syv
Closes: write-tests-for-link-semantic-validation-y30h
Closes: integrate-link-validation-into-blogexblog-compile-time-macro-1205
54 lines
1.3 KiB
Elixir
54 lines
1.3 KiB
Elixir
defmodule Blogex.LinkError do
|
|
@moduledoc """
|
|
Exception raised when a blog post contains invalid internal links.
|
|
|
|
Raised at compile time by `Blogex.Blog` when `LinkValidator` finds
|
|
semantic errors in post body links.
|
|
|
|
## Fields
|
|
|
|
* `:blog` — the blog identifier atom (e.g., `:engineering`)
|
|
* `:post_id` — the post slug/id that contains the invalid link
|
|
* `:errors` — list of `{line, link, reason, post_id}` tuples
|
|
|
|
## Example
|
|
|
|
raise Blogex.LinkError,
|
|
blog: :engineering,
|
|
post_id: "my-post",
|
|
errors: [
|
|
{1, "/blog/unknown/broken", "unknown blog ID: unknown", post_id: "my-post"}
|
|
]
|
|
"""
|
|
|
|
defexception blog: nil, post_id: nil, errors: []
|
|
|
|
@type t :: %__MODULE__{
|
|
blog: atom(),
|
|
post_id: String.t() | nil,
|
|
errors: [{integer(), String.t(), String.t(), keyword()}]
|
|
}
|
|
|
|
@impl true
|
|
def message(%__MODULE__{blog: blog, post_id: post_id, errors: errors}) do
|
|
post_label =
|
|
case post_id do
|
|
nil -> ""
|
|
id -> " (post: #{id})"
|
|
end
|
|
|
|
errors_list =
|
|
errors
|
|
|> Enum.map(fn {line, link, reason, _meta} ->
|
|
" line #{line}: #{link} — #{reason}"
|
|
end)
|
|
|> Enum.join("\n")
|
|
|
|
"""
|
|
invalid internal blog links in #{blog}#{post_label}
|
|
|
|
#{errors_list}
|
|
"""
|
|
end
|
|
end
|