Skip to content

Commit

Permalink
feat: multiline turtle literals
Browse files Browse the repository at this point in the history
  • Loading branch information
tpluscode committed May 15, 2023
1 parent 4af562f commit c8f0724
Show file tree
Hide file tree
Showing 9 changed files with 770 additions and 207 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-mangos-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rdfjs-elements/formats-pretty': patch
---

Preserve line breaks when serialising Turtle and Trig (fixes #139)
2 changes: 2 additions & 0 deletions packages/formats/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@rdfjs/term-map": "^2.0.0",
"@tpluscode/rdf-ns-builders": ">=3.0.2",
"@zazuko/formats-lazy": "^1.0.1",
"@zazuko/prefixes": "^2.0.0",
"readable-stream": ">=3.6.0"
},
"devDependencies": {
Expand All @@ -45,6 +46,7 @@
"clownface": "^1.2.0",
"get-stream": "^6.0.1",
"mocha": "^10.0.0",
"mocha-chai-jest-snapshot": "^1.1.4",
"rdf-ext": "^2.2.0",
"rdf-utils-fs": "^2.2.0",
"string-to-stream": "^3.0.1"
Expand Down
5 changes: 5 additions & 0 deletions packages/formats/serializers/TransformToConciseHash.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import stream from 'readable-stream'
import { rdf, xsd } from '@tpluscode/rdf-ns-builders'
import TermMap from '@rdfjs/term-map'
import graphy from '@graphy/core.data.factory'
import { LongLiteral } from './graphy/coercions.js'

function isListNode(predicates) {
return predicates.has(rdf.first) && predicates.has(rdf.rest)
Expand Down Expand Up @@ -169,6 +170,10 @@ export class TransformToConciseHash extends stream.Transform {
}
}

if (term.value.includes('\n')) {
return new LongLiteral(term)
}

return graphy.fromTerm(term).concise(this.prefixes)
}

Expand Down
10 changes: 6 additions & 4 deletions packages/formats/serializers/graphy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { lazySink } from '@zazuko/formats-lazy/LazySink.js'
import { TransformToConciseHash } from './TransformToConciseHash.js'
import { coercions } from './graphy/coercions.js'

async function serializer(importScribe, { strict } = {}) {
async function serializer(importScribe, writerOptions = {}) {
const create = (await importScribe).default

return class {
Expand All @@ -18,13 +19,14 @@ async function serializer(importScribe, { strict } = {}) {

const writer = create({
prefixes,
coercions: writerOptions.coercions,
})

quadStream
.pipe(
new TransformToConciseHash({
prefixes,
strict,
strict: writerOptions.strict,
preserveListNodeProperties,
})
)
Expand All @@ -36,11 +38,11 @@ async function serializer(importScribe, { strict } = {}) {
}

export const TurtleSerializer = lazySink(() =>
serializer(import('@graphy/content.ttl.write'))
serializer(import('@graphy/content.ttl.write'), { coercions })
)
export const RdfXmlSerializer = lazySink(() =>
serializer(import('@graphy/content.xml.scribe'), { strict: true })
)
export const TrigSerializer = lazySink(() =>
serializer(import('@graphy/content.trig.write'), { strict: true })
serializer(import('@graphy/content.trig.write'), { strict: true, coercions })
)
36 changes: 36 additions & 0 deletions packages/formats/serializers/graphy/coercions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { xsd } from '@tpluscode/rdf-ns-builders'
import { shrink } from '@zazuko/prefixes'

export class LongLiteral {
constructor(term) {
this.term = term
}

toTerm() {
const raw = `"""${this.term.value.replace(/"$/, '\\"')}"""`

return {
terse: prefixes => raw + this.langOrDatatype(prefixes),
verbose: prefixes => raw + this.langOrDatatype(prefixes),
}
}

langOrDatatype(prefixes) {
if (this.term.language) {
return `@${this.term.language}`
}

if (this.term.datatype.equals(xsd.string)) {
return ''
}

const shrunk = shrink(this.term.datatype.value, prefixes)
if (!shrunk) {
return `^^<${this.term.datatype.value}>`
}

return `^^${shrunk}`
}
}

export const coercions = new Map([[LongLiteral, literal => literal.toTerm()]])
11 changes: 11 additions & 0 deletions packages/formats/test/graphs/multiline-literals.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
PREFIX ex: <http://example.org/>

ex:a ex:b "The first line\nThe second line\n more" .

ex:x ex:y "Long\ndescription\nwith datatype"^^ex:multiline .

ex:m ex:n "multiline\nwith language"@en-US .

ex:f ex:g "The first line\n\"The quoted line\"\n more" .

ex:r ex:q "Quote\nat end\"" .
48 changes: 48 additions & 0 deletions packages/formats/test/serializers/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`@rdfjs-elements/formats-pretty/serializers turtle preserves line breaks in literals 1`] = `
"
<http://example.org/a> <http://example.org/b> \\"\\"\\"The first line
The second line
more\\"\\"\\" .
<http://example.org/x> <http://example.org/y> \\"\\"\\"Long
description
with datatype\\"\\"\\"^^<http://example.org/multiline> .
<http://example.org/m> <http://example.org/n> \\"\\"\\"multiline
with language\\"\\"\\"@en-US .
<http://example.org/f> <http://example.org/g> \\"\\"\\"The first line
\\"The quoted line\\"
more\\"\\"\\" .
<http://example.org/r> <http://example.org/q> \\"\\"\\"Quote
at end\\\\\\"\\"\\"\\" .
"
`;

exports[`@rdfjs-elements/formats-pretty/serializers turtle preserves line breaks in literals when using prefixes 1`] = `
"@prefix ex: <http://example.org/> .
ex:a ex:b \\"\\"\\"The first line
The second line
more\\"\\"\\" .
ex:x ex:y \\"\\"\\"Long
description
with datatype\\"\\"\\"^^ex:multiline .
ex:m ex:n \\"\\"\\"multiline
with language\\"\\"\\"@en-US .
ex:f ex:g \\"\\"\\"The first line
\\"The quoted line\\"
more\\"\\"\\" .
ex:r ex:q \\"\\"\\"Quote
at end\\\\\\"\\"\\"\\" .
"
`;
37 changes: 36 additions & 1 deletion packages/formats/test/serializers/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'chai'
import chai, { expect } from 'chai'
import { jestSnapshotPlugin } from 'mocha-chai-jest-snapshot'
import prefixes from '@zazuko/prefixes'
import * as ns from '@tpluscode/rdf-ns-builders'
import $rdf from 'rdf-ext'
Expand All @@ -13,6 +14,8 @@ import { TurtleSerializer } from '../../serializers/graphy.js'
const __dirname = dirname(fileURLToPath(import.meta.url))

describe('@rdfjs-elements/formats-pretty/serializers', () => {
chai.use(jestSnapshotPlugin())

describe('turtle', () => {
it('combines default and import prefixes', async () => {
// given
Expand Down Expand Up @@ -93,6 +96,38 @@ describe('@rdfjs-elements/formats-pretty/serializers', () => {
// then
expect(serialized).to.be.ok
})

it('preserves line breaks in literals', async () => {
// given
const graph = rdfUtil.fromFile(
join(__dirname, `../graphs/multiline-literals.ttl`)
)
const sink = new TurtleSerializer()

// when
const serialized = await getStream(sink.import(graph))

// then
expect(serialized).toMatchSnapshot()
})

it('preserves line breaks in literals when using prefixes', async () => {
// given
const graph = rdfUtil.fromFile(
join(__dirname, `../graphs/multiline-literals.ttl`)
)
const sink = new TurtleSerializer({
prefixes: {
ex: 'http://example.org/',
},
})

// when
const serialized = await getStream(sink.import(graph))

// then
expect(serialized).toMatchSnapshot()
})
})

describe('jsonld', () => {
Expand Down
Loading

0 comments on commit c8f0724

Please sign in to comment.