Project updates from Template #1
@@ -46,11 +46,17 @@ public extension StructuredData {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - name: the name of the organization and the site.
|
/// - name: the name of the organization and the site.
|
||||||
/// - url: the absolute URL the site is served at.
|
/// - url: the absolute URL the site is served at.
|
||||||
|
/// - alternateName: the name the organization is also known by, or `nil` (the default) to omit its property.
|
||||||
|
/// - description: what the organization does, or `nil` (the default) to omit its property.
|
||||||
|
/// - areaServed: the area the organization serves, or `nil` (the default) to omit its property.
|
||||||
/// - logo: the absolute URL of the organization's logo, or `nil` (the default) to omit its property.
|
/// - logo: the absolute URL of the organization's logo, or `nil` (the default) to omit its property.
|
||||||
/// - profiles: the absolute URLs of the organization's public profiles, or empty (the default) to omit their property.
|
/// - profiles: the absolute URLs of the organization's public profiles, or empty (the default) to omit their property.
|
||||||
init(
|
init(
|
||||||
name: String,
|
name: String,
|
||||||
url: String,
|
url: String,
|
||||||
|
alternateName: String? = nil,
|
||||||
|
description: String? = nil,
|
||||||
|
areaServed: String? = nil,
|
||||||
logo: String? = nil,
|
logo: String? = nil,
|
||||||
profiles: [String] = []
|
profiles: [String] = []
|
||||||
) {
|
) {
|
||||||
@@ -61,6 +67,18 @@ public extension StructuredData {
|
|||||||
.init(.url, value: .string(url)),
|
.init(.url, value: .string(url)),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if let alternateName {
|
||||||
|
organization.append(.init(.alternateName, value: .string(alternateName)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let description {
|
||||||
|
organization.append(.init(.description, value: .string(description)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let areaServed {
|
||||||
|
organization.append(.init(.areaServed, value: .string(areaServed)))
|
||||||
|
}
|
||||||
|
|
||||||
if let logo {
|
if let logo {
|
||||||
organization.append(.init(.logo, value:.string(logo)))
|
organization.append(.init(.logo, value:.string(logo)))
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -69,6 +69,12 @@ extension StructuredData.Property {
|
|||||||
// MARK: - Constants
|
// MARK: - Constants
|
||||||
|
|
||||||
public extension StructuredData.Property.Name {
|
public extension StructuredData.Property.Name {
|
||||||
|
/// The name the thing a node describes is also known by.
|
||||||
|
static let alternateName: Self = "alternateName"
|
||||||
|
/// The area an organization serves.
|
||||||
|
static let areaServed: Self = "areaServed"
|
||||||
|
/// What the thing a node describes is or does.
|
||||||
|
static let description: Self = "description"
|
||||||
/// The absolute URL of an organization's logo.
|
/// The absolute URL of an organization's logo.
|
||||||
static let logo: Self = "logo"
|
static let logo: Self = "logo"
|
||||||
/// The name of the thing a node describes.
|
/// The name of the thing a node describes.
|
||||||
|
|||||||
+5
-1
@@ -1,5 +1,5 @@
|
|||||||
extension StructuredData {
|
extension StructuredData {
|
||||||
/// A value of a ``Property``: a string, a list, a nested node, or a reference to another node.
|
/// A value of a ``Property``: a string, a number, a list, a nested node, or a reference to another node.
|
||||||
///
|
///
|
||||||
/// Every string a value renders is escaped as a JSON literal with `<` escaped as well, so a value can never close the `script`
|
/// Every string a value renders is escaped as a JSON literal with `<` escaped as well, so a value can never close the `script`
|
||||||
/// tag embedding the payload it renders into.
|
/// tag embedding the payload it renders into.
|
||||||
@@ -8,6 +8,8 @@ extension StructuredData {
|
|||||||
case array([Value])
|
case array([Value])
|
||||||
/// A nested node, e.g. the place a schema.org event is located at.
|
/// A nested node, e.g. the place a schema.org event is located at.
|
||||||
case node(Node)
|
case node(Node)
|
||||||
|
/// A whole number, e.g. a list item's position, rendered unquoted so it reads as a number rather than as text.
|
||||||
|
case number(Int)
|
||||||
/// A reference to the ``Node/id`` of another node in the graph, rendered as an `@id` object.
|
/// A reference to the ``Node/id`` of another node in the graph, rendered as an `@id` object.
|
||||||
case reference(String)
|
case reference(String)
|
||||||
/// A string value.
|
/// A string value.
|
||||||
@@ -28,6 +30,8 @@ extension StructuredData.Value {
|
|||||||
"[\(values.map(\.fragment).joined(separator: .Separator.comma))]"
|
"[\(values.map(\.fragment).joined(separator: .Separator.comma))]"
|
||||||
case .node(let node):
|
case .node(let node):
|
||||||
node.fragment
|
node.fragment
|
||||||
|
case .number(let number):
|
||||||
|
String(number)
|
||||||
case .reference(let id):
|
case .reference(let id):
|
||||||
#"{"@id":\#(Self.literal(id))}"#
|
#"{"@id":\#(Self.literal(id))}"#
|
||||||
case .string(let string):
|
case .string(let string):
|
||||||
|
|||||||
@@ -30,6 +30,21 @@ struct StructuredDataTests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func `carries the organization's alternate name, description, and area in that order`() {
|
||||||
|
let data = StructuredData(
|
||||||
|
name: "A Site",
|
||||||
|
url: "https://site.example/",
|
||||||
|
alternateName: "The Site",
|
||||||
|
description: "What the site is.",
|
||||||
|
areaServed: "A City"
|
||||||
|
)
|
||||||
|
|
||||||
|
#expect(data.payload.contains(
|
||||||
|
#""name":"A Site","url":"https://site.example/","alternateName":"The Site","description":"What the site is.","areaServed":"A City""#
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func `omits the properties of the facts minimal data does not carry`() {
|
func `omits the properties of the facts minimal data does not carry`() {
|
||||||
let data = StructuredData(
|
let data = StructuredData(
|
||||||
@@ -120,6 +135,9 @@ struct StructuredDataTests {
|
|||||||
#expect(StructuredData.Value.array([.string("A"), .string("B")]).fragment == #"["A","B"]"#)
|
#expect(StructuredData.Value.array([.string("A"), .string("B")]).fragment == #"["A","B"]"#)
|
||||||
#expect(StructuredData.Value.reference("https://site.example/#organization").fragment == #"{"@id":"https://site.example/#organization"}"#)
|
#expect(StructuredData.Value.reference("https://site.example/#organization").fragment == #"{"@id":"https://site.example/#organization"}"#)
|
||||||
#expect(StructuredData.Value.node(.init(type: "Place", properties: [])).fragment == #"{"@type":"Place"}"#)
|
#expect(StructuredData.Value.node(.init(type: "Place", properties: [])).fragment == #"{"@type":"Place"}"#)
|
||||||
|
// Unquoted, or a consumer reads the position of a list item as text and cannot order by it.
|
||||||
|
#expect(StructuredData.Value.number(1).fragment == "1")
|
||||||
|
#expect(StructuredData.Value.number(-3).fragment == "-3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user