Packages

A sitemap XML builder for Gleam

Current section

Files

Jump to
sitemapb src sitemapb.gleam
Raw

src/sitemapb.gleam

//// Sitemap XML!
//// <https://www.sitemaps.org/protocol.html>
import gleam/float
import gleam/list
import gleam/option.{type Option}
import gleam/string_tree
import gleam/time/calendar
import gleam/time/timestamp.{type Timestamp}
import xmb.{type Xml, text, x}
pub type Url {
Url(
/// URL of the page. This URL must begin with the protocol (such as http)
/// and end with a trailing slash, if your web server requires it. This
/// value must be less than 2,048 characters.
///
location: String,
/// The date of last modification of the page.
///
/// Note that the date must be set to the date the linked page was last
/// modified, not when the sitemap is generated.
///
/// Note also that this tag is separate from the If-Modified-Since (304)
/// header the server can return, and search engines may use the
/// information from both sources differently.
///
last_modified: option.Option(Timestamp),
/// How frequently the page is likely to change. This value provides
/// general information to search engines and may not correlate exactly to
/// how often they crawl the page.
///
/// Please note that the value of this tag is considered a hint and not a
/// command. Even though search engine crawlers may consider this information
/// when making decisions, they may crawl pages marked "hourly" less
/// frequently than that, and they may crawl pages marked "yearly" more
/// frequently than that. Crawlers may periodically crawl pages marked
/// "never" so that they can handle unexpected changes to those pages.
///
change_frequency: option.Option(ChangeFrequency),
/// The priority of this URL relative to other URLs on your site. Valid
/// values range from 0.0 to 1.0. This value does not affect how your pages
/// are compared to pages on other sites—it only lets the search engines
/// know which pages you deem most important for the crawlers.
///
/// Values outside of the range of 0.0 to 1.0 will be replaced with 0.0 or
/// 1.0.
///
/// If not specified then the priority of a page is considered to be 0.5.
///
/// Please note that the priority you assign to a page is not likely to
/// influence the position of your URLs in a search engine's result pages.
/// Search engines may use this information when selecting between URLs on
/// the same site, so you can use this tag to increase the likelihood that
/// your most important pages are present in a search index.
///
/// Also, please note that assigning a high priority to all of the URLs on
/// your site is not likely to help you. Since the priority is relative, it
/// is only used to select between URLs on your site.
///
priority: option.Option(Float),
)
}
/// How frequently a page is likely to change. This value provides general
/// information to search engines and may not correlate exactly to how often
/// they crawl the page.
///
/// The value "always" should be used to describe documents that change each
/// time they are accessed. The value "never" should be used to describe
/// archived URLs.
///
pub type ChangeFrequency {
Always
Hourly
Daily
Weekly
Monthly
Yearly
Never
}
const namespace = #("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9")
/// Render an XML sitemap.
///
/// This function lets you specify all the properties of each page.
///
pub fn render(site: List(Url)) -> string_tree.StringTree {
let url = fn(url) {
let Url(location:, last_modified:, change_frequency:, priority:) = url
x("url", [], [
x("loc", [], [text(location)]),
maybe("lastmod", last_modified, render_timestamp),
maybe("changefreq", change_frequency, render_change_frequency),
maybe("priority", priority, render_priority),
])
}
x("urlset", [namespace], list.map(site, url))
|> list.wrap
|> xmb.render
}
/// Render an XML sitemap.
///
/// This function takes only URLs. Use the `render` function if you want to
/// specify the other properties for eaech page.
///
pub fn render_basic(site: List(String)) -> string_tree.StringTree {
let url = fn(url) { x("url", [], [x("loc", [], [text(url)])]) }
x("urlset", [namespace], list.map(site, url))
|> list.wrap
|> xmb.render
}
fn render_priority(priority: Float) -> Xml {
priority
|> float.clamp(min: 0.0, max: 1.0)
|> float.to_string
|> text
}
fn render_timestamp(timestamp: Timestamp) -> Xml {
text(timestamp.to_rfc3339(timestamp, calendar.utc_offset))
}
fn render_change_frequency(frequency: ChangeFrequency) -> Xml {
text(case frequency {
Always -> "always"
Hourly -> "hourly"
Daily -> "daily"
Weekly -> "weekly"
Monthly -> "monthly"
Yearly -> "yearly"
Never -> "never"
})
}
fn maybe(tag: String, item: Option(a), render: fn(a) -> Xml) -> Xml {
case item {
option.Some(value) -> x(tag, [], [render(value)])
option.None -> xmb.nothing()
}
}