// Copyright Inrupt Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the // Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import type { Literal, Quad_Object, NamedNode } from "@rdfjs/types"; import type { Time } from "../datatypes"; import type { Thing, ThingLocal, ThingPersisted, Url, UrlString, } from "../interfaces"; import type { AddOfType } from "./add"; import { addBoolean, addDate, addDatetime, addTime, addDecimal, addInteger, addIri, addLiteral, addNamedNode, addStringNoLocale, addStringWithLocale, addTerm, addUrl, } from "./add"; import type { RemoveOfType } from "./remove"; import { removeAll, removeBoolean, removeDate, removeDatetime, removeTime, removeDecimal, removeInteger, removeIri, removeLiteral, removeNamedNode, removeStringNoLocale, removeStringWithLocale, removeUrl, } from "./remove"; import type { SetOfType } from "./set"; import { setBoolean, setDate, setDatetime, setTime, setDecimal, setInteger, setIri, setLiteral, setNamedNode, setStringNoLocale, setStringWithLocale, setTerm, setUrl, } from "./set"; import type { CreateThingLocalOptions, CreateThingOptions, CreateThingPersistedOptions, } from "./thing"; import { createThing, isThing } from "./thing"; type Adder = ( property: Parameters>[1], value: Parameters>[2], ) => ThingBuilder; type Setter = ( property: Parameters>[1], value: Parameters>[2], ) => ThingBuilder; type Remover = ( property: Parameters>[1], value: Parameters>[2], ) => ThingBuilder; // Unfortunately this interface has too many properties for TypeScript to infer, // hence the duplication between the interface and the implementation method names. /** * A Fluent interface to build a [[Thing]]. * * Add, replace or remove property values using consecutive calls to `.add*()`, * `.set*()` and `.remove*()`, then finally generate a [[Thing]] with the given * properties using `.build()`. * @since 1.9.0 */ export type ThingBuilder = { build: () => T; addUrl: Adder; addIri: Adder; addBoolean: Adder; addDatetime: Adder; addDate: Adder; addTime: Adder; addDecimal: Adder; addInteger: Adder; addStringNoLocale: Adder; addStringEnglish: Adder; addStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => ThingBuilder; addNamedNode: Adder; addLiteral: Adder; addTerm: Adder; setUrl: Setter; setIri: Setter; setBoolean: Setter; setDatetime: Setter; setDate: Setter; setTime: Setter; setDecimal: Setter; setInteger: Setter; setStringNoLocale: Setter; setStringEnglish: Setter; setStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => ThingBuilder; setNamedNode: Setter; setLiteral: Setter; setTerm: Setter; removeAll: (property: Parameters[1]) => ThingBuilder; removeUrl: Remover; removeIri: Remover; removeBoolean: Remover; removeDatetime: Remover; removeDate: Remover; removeTime: Remover; removeDecimal: Remover; removeInteger: Remover; removeStringNoLocale: Remover; removeStringEnglish: Remover; removeStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => ThingBuilder; removeNamedNode: Remover; removeLiteral: Remover; }; /** * Modify a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can initialise several properties of a given Thing as follows: * * const me = buildThing(createThing({ name: "profile-vincent" })) * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @param init A Thing to modify. * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing(init: ThingLocal): ThingBuilder; /** * Modify a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can initialise several properties of a given Thing as follows: * * const me = buildThing(createThing({ url: "https://example.pod/profile#vincent" })) * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @param init A Thing to modify. * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing(init: ThingPersisted): ThingBuilder; /** * Create a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can create a new Thing and initialise several properties as follows: * * const me = buildThing({ name: "profile-vincent" }) * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @param init Options used to initialise a new Thing. * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing( init: CreateThingLocalOptions, ): ThingBuilder; /** * Create a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can create a new Thing and initialise several properties as follows: * * const me = buildThing({ url: "https://example.pod/profile#vincent" }) * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @param init Optionally pass an existing [[Thing]] to modify the properties of. If left empty, `buildThing` will initialise a new Thing. * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing( init: CreateThingPersistedOptions, ): ThingBuilder; /** * Create a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can create a new Thing and initialise several properties as follows: * * const me = buildThing() * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing(): ThingBuilder; /** * Create or modify a [[Thing]], setting multiple properties in a single expresssion. * * For example, you can create a new Thing and initialise several properties as follows: * * const me = buildThing() * .addUrl(rdf.type, schema.Person) * .addStringNoLocale(schema.givenName, "Vincent") * .build(); * * Take note of the final call to `.build()` to obtain the actual Thing. * * @param init Optionally pass an existing [[Thing]] to modify the properties of. If left empty, `buildThing` will initialise a new Thing. * @returns a [[ThingBuilder]], a Fluent API that allows you to set multiple properties in a single expression. * @since 1.9.0 */ export function buildThing( init: Thing | CreateThingOptions = createThing(), ): ThingBuilder { let thing = isThing(init) ? init : createThing(init); function getAdder(adder: AddOfType) { return ( property: Parameters[1], value: Parameters[2], ) => { thing = adder(thing, property, value); return builder; }; } function getSetter(setter: SetOfType) { return ( property: Parameters[1], value: Parameters[2], ) => { thing = setter(thing, property, value); return builder; }; } function getRemover(remover: RemoveOfType) { return ( property: Parameters[1], value: Parameters[2], ) => { thing = remover(thing, property, value); return builder; }; } const builder: ThingBuilder = { build: () => thing, addUrl: getAdder(addUrl), addIri: getAdder(addIri), addBoolean: getAdder(addBoolean), addDatetime: getAdder(addDatetime), addDate: getAdder(addDate), addTime: getAdder(addTime), addDecimal: getAdder(addDecimal), addInteger: getAdder(addInteger), addStringNoLocale: getAdder(addStringNoLocale), addStringEnglish: ( property: Parameters[1], value: Parameters[2], ) => { thing = addStringWithLocale(thing, property, value, "en"); return builder; }, addStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => { thing = addStringWithLocale(thing, property, value, locale); return builder; }, addNamedNode: getAdder(addNamedNode), addLiteral: getAdder(addLiteral), addTerm: getAdder(addTerm), setUrl: getSetter(setUrl), setIri: getSetter(setIri), setBoolean: getSetter(setBoolean), setDatetime: getSetter(setDatetime), setDate: getSetter(setDate), setTime: getSetter(setTime), setDecimal: getSetter(setDecimal), setInteger: getSetter(setInteger), setStringNoLocale: getSetter(setStringNoLocale), setStringEnglish: ( property: Parameters[1], value: Parameters[2], ) => { thing = setStringWithLocale(thing, property, value, "en"); return builder; }, setStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => { thing = setStringWithLocale(thing, property, value, locale); return builder; }, setNamedNode: getSetter(setNamedNode), setLiteral: getSetter(setLiteral), setTerm: getSetter(setTerm), removeAll: (property: Parameters[1]) => { thing = removeAll(thing, property); return builder; }, removeUrl: getRemover(removeUrl), removeIri: getRemover(removeIri), removeBoolean: getRemover(removeBoolean), removeDatetime: getRemover(removeDatetime), removeDate: getRemover(removeDate), removeTime: getRemover(removeTime), removeDecimal: getRemover(removeDecimal), removeInteger: getRemover(removeInteger), removeStringNoLocale: getRemover(removeStringNoLocale), removeStringEnglish: ( property: Parameters[1], value: Parameters[2], ) => buildThing(removeStringWithLocale(thing, property, value, "en")), removeStringWithLocale: ( property: Parameters[1], value: Parameters[2], locale: Parameters[3], ) => buildThing(removeStringWithLocale(thing, property, value, locale)), removeNamedNode: getRemover(removeNamedNode), removeLiteral: getRemover(removeLiteral), }; return builder; }