///|
fn jget(j : Json, key : String) -> Json {
match j {
Object(map) =>
match map.get(key) {
Some(v) => v
None => Json::null()
}
_ => Json::null()
}
}
///|
fn s_at(j : Json, key : String) -> String? {
match jget(j, key) {
String(s) => Some(s)
_ => None
}
}
///|
fn s_at_or(j : Json, key : String, fallback~ : String) -> String {
match s_at(j, key) {
Some(v) => v
None => fallback
}
}
///|
fn arr_at(j : Json, key : String) -> Array[Json] {
match jget(j, key) {
Array(arr) => arr
_ => []
}
}
///|
fn obj_at(j : Json, key : String) -> Json? {
match jget(j, key) {
Object(_) => Some(jget(j, key))
_ => None
}
}
///|
fn categories_from(cats : Array[Json]) -> Array[Category] {
let out : Array[Category] = []
for c in cats {
out.push({
domain: s_at(c, "domain"),
name: s_at_or(c, "name", fallback=""),
})
}
out
}
///|
fn extension_from(j : Json) -> Extension {
let attrs : Map[String, String] = Map([])
match jget(j, "attrs") {
Object(map) =>
for k, v in map {
match v {
String(s) => attrs[k] = s
_ => ()
}
}
_ => ()
}
let children : Map[String, Array[Extension]] = Map([])
match jget(j, "children") {
Object(map) =>
for name, list in map {
match list {
Array(arr) => {
let exts : Array[Extension] = []
for item in arr {
exts.push(extension_from(item))
}
children[name] = exts
}
_ => ()
}
}
_ => ()
}
{
name: s_at_or(j, "name", fallback=""),
value: s_at(j, "value"),
attrs,
children,
}
}
///|
fn extensions_from(j : Json) -> ExtensionMap {
let exts : ExtensionMap = Map([])
match j {
Object(map) =>
for ns, inner in map {
match inner {
Object(mid) =>
for name, list in mid {
match list {
Array(arr) => {
let ext_list = extension_entry(exts, ns, name)
for item in arr {
ext_list.push(extension_from(item))
}
}
_ => ()
}
}
_ => ()
}
}
_ => ()
}
exts
}
///|
fn atom_links_from(j : Json) -> Array[@atom.Link] {
let links : Array[@atom.Link] = []
for l in arr_at(j, "atomLinks") {
links.push({
href: s_at_or(l, "href", fallback=""),
rel: s_at(l, "rel"),
media_type: s_at(l, "type"),
hreflang: s_at(l, "hreflang"),
title: s_at(l, "title"),
length: s_at(l, "length"),
})
}
links
}
///|
fn item_from(j : Json) -> Item raise RssError {
let enclosure : Enclosure? = match obj_at(j, "enclosure") {
Some(e) =>
Some({
url: s_at_or(e, "url", fallback=""),
length: s_at_or(e, "length", fallback=""),
mime_type: s_at_or(e, "mimeType", fallback=""),
})
None => None
}
let guid : Guid? = match obj_at(j, "guid") {
Some(g) =>
Some({
is_permalink: match jget(g, "isPermalink") {
False => false
_ => true
},
value: s_at_or(g, "value", fallback=""),
})
None => None
}
let source : Source? = match obj_at(j, "source") {
Some(src) =>
Some({ url: s_at_or(src, "url", fallback=""), title: s_at(src, "title") })
None => None
}
let itunes : ITunesItemExtension? = match obj_at(j, "itunes") {
Some(e) =>
Some({
author: s_at(e, "author"),
block: s_at(e, "block"),
image: s_at(e, "image"),
duration: s_at(e, "duration"),
explicit: s_at(e, "explicit"),
closed_captioned: s_at(e, "closedCaptioned"),
order: s_at(e, "order"),
subtitle: s_at(e, "subtitle"),
summary: s_at(e, "summary"),
keywords: s_at(e, "keywords"),
episode: s_at(e, "episode"),
season: s_at(e, "season"),
episode_type: s_at(e, "episodeType"),
})
None => None
}
let dc = dublincore_from(jget(j, "dublincore"))
let extensions = extensions_from(jget(j, "extensions"))
let atom_links = atom_links_from(j)
{
title: s_at(j, "title"),
link: s_at(j, "link"),
description: s_at(j, "description"),
author: s_at(j, "author"),
categories: categories_from(arr_at(j, "categories")),
comments: s_at(j, "comments"),
enclosure,
guid,
pub_date: s_at(j, "pubDate"),
source,
content: s_at(j, "content"),
extensions,
atom_links,
itunes_ext: itunes,
dublin_core_ext: dc,
}
}
///|
fn dublincore_from(j : Json) -> DublinCoreExtension? {
fn strs(key : String) -> Array[String] {
let out : Array[String] = []
for v in arr_at(j, key) {
match v {
String(s) => out.push(s)
_ => ()
}
}
out
}
match j {
Object(_) =>
Some({
contributors: strs("contributors"),
coverages: strs("coverages"),
creators: strs("creators"),
dates: strs("dates"),
descriptions: strs("descriptions"),
formats: strs("formats"),
identifiers: strs("identifiers"),
languages: strs("languages"),
publishers: strs("publishers"),
relations: strs("relations"),
rights: strs("rights"),
sources: strs("sources"),
subjects: strs("subjects"),
titles: strs("titles"),
types: strs("types"),
})
_ => None
}
}
///|
fn itunes_channel_from(j : Json) -> ITunesChannelExtension? {
match obj_at(j, "itunes") {
None => None
Some(e) => {
let categories : Array[ITunesCategory] = []
for c in arr_at(e, "categories") {
let subcategory : ITunesCategory? = match obj_at(c, "subcategory") {
Some(sub) =>
Some({ text: s_at_or(sub, "text", fallback=""), subcategory: None })
None => None
}
categories.push({ text: s_at_or(c, "text", fallback=""), subcategory })
}
let owner : ITunesOwner? = match obj_at(e, "owner") {
Some(o) => Some({ name: s_at(o, "name"), email: s_at(o, "email") })
None => None
}
Some({
author: s_at(e, "author"),
block: s_at(e, "block"),
categories,
image: s_at(e, "image"),
explicit: s_at(e, "explicit"),
complete: s_at(e, "complete"),
new_feed_url: s_at(e, "newFeedUrl"),
owner,
subtitle: s_at(e, "subtitle"),
summary: s_at(e, "summary"),
keywords: s_at(e, "keywords"),
itunes_type: s_at(e, "type"),
})
}
}
}
///|
fn syndication_from(j : Json) -> SyndicationExtension? {
match j {
Object(_) => {
let period = match s_at_or(j, "period", fallback="daily") {
"hourly" => Hourly
"weekly" => Weekly
"monthly" => Monthly
"yearly" => Yearly
_ => Daily
}
let frequency = match jget(j, "frequency") {
Number(n, ..) => {
let n = n.to_int()
if n > 0 {
n
} else {
1
}
}
_ => 1
}
Some({
period,
frequency,
base: s_at_or(j, "base", fallback="1970-01-01T00:00:00Z"),
})
}
_ => None
}
}
///|
/// Reconstruct a channel from canonical JSON.
pub fn Channel::from_canon(json : Json) -> Channel raise RssError {
let cloud : Cloud? = match obj_at(json, "cloud") {
Some(c) =>
Some({
domain: s_at_or(c, "domain", fallback=""),
port: s_at_or(c, "port", fallback=""),
path: s_at_or(c, "path", fallback=""),
register_procedure: s_at_or(c, "registerProcedure", fallback=""),
protocol: s_at_or(c, "protocol", fallback=""),
})
None => None
}
let image : Image? = match obj_at(json, "image") {
Some(i) =>
Some({
url: s_at_or(i, "url", fallback=""),
title: s_at_or(i, "title", fallback=""),
link: s_at_or(i, "link", fallback=""),
width: s_at(i, "width"),
height: s_at(i, "height"),
description: s_at(i, "description"),
})
None => None
}
let text_input : TextInput? = match obj_at(json, "textInput") {
Some(t) =>
Some({
title: s_at_or(t, "title", fallback=""),
description: s_at_or(t, "description", fallback=""),
name: s_at_or(t, "name", fallback=""),
link: s_at_or(t, "link", fallback=""),
})
None => None
}
let extensions = extensions_from(jget(json, "extensions"))
let atom_links = atom_links_from(json)
let (itunes_ext, dublin_core_ext, syndication_ext) = (
itunes_channel_from(json),
dublincore_from(jget(json, "dublincore")),
syndication_from(jget(json, "syndication")),
)
let items : Array[Item] = []
for ij in arr_at(json, "items") {
items.push(item_from(ij))
}
{
title: s_at_or(json, "title", fallback=""),
link: s_at_or(json, "link", fallback=""),
description: s_at_or(json, "description", fallback=""),
language: s_at(json, "language"),
copyright: s_at(json, "copyright"),
managing_editor: s_at(json, "managingEditor"),
webmaster: s_at(json, "webMaster"),
pub_date: s_at(json, "pubDate"),
last_build_date: s_at(json, "lastBuildDate"),
categories: categories_from(arr_at(json, "categories")),
generator: s_at(json, "generator"),
docs: s_at(json, "docs"),
cloud,
rating: s_at(json, "rating"),
ttl: s_at(json, "ttl"),
image,
text_input,
skip_hours: str_array(arr_at(json, "skipHours")),
skip_days: str_array(arr_at(json, "skipDays")),
items,
extensions,
atom_links,
itunes_ext,
dublin_core_ext,
syndication_ext,
namespaces: namespaces_from(jget(json, "namespaces")),
}
}
///|
fn str_array(arr : Array[Json]) -> Array[String] {
let out : Array[String] = []
for v in arr {
match v {
String(s) => out.push(s)
_ => ()
}
}
out
}
///|
fn namespaces_from(j : Json) -> Map[String, String] {
let out : Map[String, String] = Map([])
match j {
Object(map) =>
for k, v in map {
match v {
String(s) => out[k] = s
_ => ()
}
}
_ => ()
}
out
}