feat: initial commit
This commit is contained in:
commit
a161b86c9a
705 changed files with 288162 additions and 0 deletions
13
vendor/sourcery.dny.nu/longdistance/.gitattributes
vendored
Normal file
13
vendor/sourcery.dny.nu/longdistance/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
*.go diff=golang
|
||||
|
||||
go.sum linguist-generated merge=ours
|
||||
/vendor/ linguist-vendored
|
||||
|
||||
*.md linguist-documentation text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
|
||||
|
||||
/.github export-ignore
|
||||
/.woodpecker linguist-vendored export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
4
vendor/sourcery.dny.nu/longdistance/.gitignore
vendored
Normal file
4
vendor/sourcery.dny.nu/longdistance/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
*.test
|
||||
*.out
|
||||
vendor/
|
||||
.vscode/
|
||||
373
vendor/sourcery.dny.nu/longdistance/LICENSE
vendored
Normal file
373
vendor/sourcery.dny.nu/longdistance/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
37
vendor/sourcery.dny.nu/longdistance/README.md
vendored
Normal file
37
vendor/sourcery.dny.nu/longdistance/README.md
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# longdistance
|
||||
|
||||
A Go library for folks whose relationship status with Linked Data is "It's Complicated".
|
||||
|
||||
This library implements parts of the [JSON-LD 1.1][jld] specification. It does not currently implement features from the JSON-LD 1.1 Processing Algorithms and API specification that are not needed for handling [ActivityStreams][as].
|
||||
|
||||
[jld]: https://www.w3.org/TR/json-ld/
|
||||
[as]: https://www.w3.org/TR/activitystreams-core/
|
||||
|
||||
For each implemented functionality, it passes the associated [JSON-LD test suite][jldtest] provided by the W3C.
|
||||
|
||||
[jldtest]: https://w3c.github.io/json-ld-api/tests/
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [godoc](https://pkg.go.dev/sourcery.dny.nu/longdistance).
|
||||
|
||||
## Supported features
|
||||
|
||||
* Context processing.
|
||||
* Remote context retrieval is supported, but requires a loader to be provided.
|
||||
* Document expansion.
|
||||
* Document compaction.
|
||||
* Except `@preserve`.
|
||||
|
||||
## Unsupported features
|
||||
|
||||
* Document flattening.
|
||||
* Framing.
|
||||
* RDF serialisation/deserialisation.
|
||||
* Remote document retrieval.
|
||||
|
||||
If you're able and willing to contribute one of these features, please start by opening an issue so we can discuss how to appraoch it.
|
||||
|
||||
## License
|
||||
|
||||
This library is licensed under the Mozilla Public License Version 2.0.
|
||||
1410
vendor/sourcery.dny.nu/longdistance/compact.go
vendored
Normal file
1410
vendor/sourcery.dny.nu/longdistance/compact.go
vendored
Normal file
File diff suppressed because it is too large
Load diff
25
vendor/sourcery.dny.nu/longdistance/consts.go
vendored
Normal file
25
vendor/sourcery.dny.nu/longdistance/consts.go
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package longdistance
|
||||
|
||||
const (
|
||||
// BlankNode is the blank node prefix.
|
||||
BlankNode = "_:"
|
||||
)
|
||||
|
||||
// Values for @direction.
|
||||
const (
|
||||
DirectionLTR = "ltr"
|
||||
DirectionRTL = "rtl"
|
||||
)
|
||||
|
||||
// JSON-LD MIME types and profiles.
|
||||
const (
|
||||
ApplicationLDJSON = "application/ld+json"
|
||||
ApplicationJSON = "application/json"
|
||||
|
||||
ProfileExpanded = "http://www.w3.org/ns/json-ld#expanded"
|
||||
ProfileCompacted = "http://www.w3.org/ns/json-ld#compacted"
|
||||
ProfileContext = "http://www.w3.org/ns/json-ld#context"
|
||||
ProfileFlattened = "http://www.w3.org/ns/json-ld#flattened"
|
||||
ProfileFrame = "http://www.w3.org/ns/json-ld#frame"
|
||||
ProfileFramed = "http://www.w3.org/ns/json-ld#framed"
|
||||
)
|
||||
677
vendor/sourcery.dny.nu/longdistance/context.go
vendored
Normal file
677
vendor/sourcery.dny.nu/longdistance/context.go
vendored
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"iter"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
"sourcery.dny.nu/longdistance/internal/url"
|
||||
)
|
||||
|
||||
// RemoteContextLimit is the recursion limit for resolving remote contexts.
|
||||
const RemoteContextLimit = 10
|
||||
|
||||
// Context represents a processed JSON-LD context.
|
||||
type Context struct {
|
||||
defs map[string]Term
|
||||
protected map[string]struct{}
|
||||
currentBaseIRI string
|
||||
originalBaseIRI string
|
||||
|
||||
vocabMapping string
|
||||
defaultLang string
|
||||
defaultDirection string
|
||||
previousContext *Context
|
||||
inverse inverseContext
|
||||
}
|
||||
|
||||
// newContext initialises a new context with the specified documentURL set as
|
||||
// the current and original base IRI.
|
||||
func newContext(documentURL string) *Context {
|
||||
return &Context{
|
||||
defs: make(map[string]Term),
|
||||
protected: make(map[string]struct{}),
|
||||
defaultLang: "",
|
||||
defaultDirection: "",
|
||||
previousContext: nil,
|
||||
inverse: nil,
|
||||
currentBaseIRI: documentURL,
|
||||
originalBaseIRI: documentURL,
|
||||
}
|
||||
}
|
||||
|
||||
// Terms returns an iterator over context term definitions.
|
||||
func (c *Context) Terms() iter.Seq2[string, Term] {
|
||||
return func(yield func(string, Term) bool) {
|
||||
for k, v := range c.defs {
|
||||
if !yield(k, v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) initInverse() {
|
||||
if c.inverse == nil {
|
||||
c.inverse = workIt(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) clone() *Context {
|
||||
return &Context{
|
||||
defs: maps.Clone(c.defs),
|
||||
protected: maps.Clone(c.protected),
|
||||
currentBaseIRI: c.currentBaseIRI,
|
||||
originalBaseIRI: c.originalBaseIRI,
|
||||
vocabMapping: c.vocabMapping,
|
||||
defaultLang: c.defaultLang,
|
||||
defaultDirection: c.defaultDirection,
|
||||
previousContext: c.previousContext,
|
||||
inverse: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Context takes in JSON and parses it into a [Context].
|
||||
func (p *Processor) Context(localContext json.RawMessage, baseURL string) (*Context, error) {
|
||||
if len(localContext) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if json.IsNull(localContext) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.context(nil, localContext, baseURL, newCtxProcessingOpts())
|
||||
}
|
||||
|
||||
type ctxProcessingOpts struct {
|
||||
remotes []string
|
||||
override bool
|
||||
propagate bool
|
||||
validate bool
|
||||
}
|
||||
|
||||
func newCtxProcessingOpts() ctxProcessingOpts {
|
||||
return ctxProcessingOpts{
|
||||
propagate: true,
|
||||
validate: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Processor) context(
|
||||
activeContext *Context,
|
||||
localContext json.RawMessage,
|
||||
baseURL string,
|
||||
opts ctxProcessingOpts,
|
||||
) (*Context, error) {
|
||||
if activeContext == nil {
|
||||
activeContext = newContext(baseURL)
|
||||
}
|
||||
|
||||
if p.baseIRI != "" {
|
||||
activeContext.currentBaseIRI = p.baseIRI
|
||||
}
|
||||
|
||||
// 1)
|
||||
result := activeContext.clone()
|
||||
|
||||
// 2)
|
||||
if json.IsMap(localContext) {
|
||||
var propcheck struct {
|
||||
Propagate *bool `json:"@propagate,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(localContext, &propcheck); err != nil {
|
||||
return nil, ErrInvalidPropagateValue
|
||||
}
|
||||
if propcheck.Propagate != nil {
|
||||
opts.propagate = *propcheck.Propagate
|
||||
}
|
||||
}
|
||||
|
||||
// 3)
|
||||
if !opts.propagate {
|
||||
if result.previousContext == nil {
|
||||
result.previousContext = activeContext.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// 4)
|
||||
localContext = json.MakeArray(localContext)
|
||||
|
||||
var contexts []json.RawMessage
|
||||
if err := json.Unmarshal(localContext, &contexts); err != nil {
|
||||
return nil, fmt.Errorf("invalid context document")
|
||||
}
|
||||
|
||||
if len(contexts) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 5)
|
||||
for _, context := range contexts {
|
||||
// 5.1)
|
||||
switch context[0] {
|
||||
case '[':
|
||||
return nil, ErrInvalidLocalContext
|
||||
case '{':
|
||||
// goes on after the switch
|
||||
default:
|
||||
// 5.1)
|
||||
if json.IsNull(context) {
|
||||
// 5.1.1)
|
||||
if !opts.override && len(result.protected) != 0 {
|
||||
return nil, ErrInvalidContextNullificaton
|
||||
}
|
||||
|
||||
// 5.1.2)
|
||||
previous := result.clone()
|
||||
result = newContext(activeContext.originalBaseIRI)
|
||||
if !opts.propagate {
|
||||
result.previousContext = previous
|
||||
}
|
||||
|
||||
// 5.1.3)
|
||||
continue
|
||||
}
|
||||
|
||||
var s string
|
||||
if err := json.Unmarshal(context, &s); err != nil {
|
||||
return nil, ErrInvalidLocalContext
|
||||
}
|
||||
|
||||
// 5.2)
|
||||
// 5.2.1)
|
||||
if !url.IsIRI(baseURL) && !url.IsIRI(s) {
|
||||
return nil, ErrLoadingDocument
|
||||
}
|
||||
|
||||
iri, err := url.Resolve(baseURL, s)
|
||||
if err != nil {
|
||||
return nil, ErrLoadingDocument
|
||||
}
|
||||
|
||||
// 5.2.2)
|
||||
if !opts.validate && slices.Contains(opts.remotes, iri) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 5.2.3)
|
||||
if len(opts.remotes) > RemoteContextLimit {
|
||||
if p.modeLD10 {
|
||||
return nil, ErrRecursiveContextInclusion
|
||||
}
|
||||
return nil, ErrContextOverflow
|
||||
}
|
||||
opts.remotes = append(opts.remotes, iri)
|
||||
|
||||
// 5.2.4) 5.2.5)
|
||||
doc, err := p.retrieveRemoteContext(iri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 5.2.6)
|
||||
newOpts := newCtxProcessingOpts()
|
||||
newOpts.remotes = slices.Clone(opts.remotes)
|
||||
newOpts.validate = opts.validate
|
||||
res, err := p.context(
|
||||
result,
|
||||
doc.Context,
|
||||
doc.URL,
|
||||
newOpts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = res
|
||||
continue
|
||||
}
|
||||
|
||||
// 5.3)
|
||||
var ctxObj map[string]json.RawMessage
|
||||
if err := json.Unmarshal(context, &ctxObj); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal context: %s %w", err, ErrInvalidLocalContext)
|
||||
}
|
||||
|
||||
// 5.5)
|
||||
if version, ok := ctxObj[KeywordVersion]; ok {
|
||||
if err := p.handleVersion(version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 5.6)
|
||||
if imp, ok := ctxObj[KeywordImport]; ok {
|
||||
res, err := p.handleImport(baseURL, imp, ctxObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctxObj = res
|
||||
}
|
||||
|
||||
// 5.7)
|
||||
if base, ok := ctxObj[KeywordBase]; ok && len(opts.remotes) == 0 {
|
||||
if err := p.handleBase(result, base); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 5.8)
|
||||
if vocab, ok := ctxObj[KeywordVocab]; ok {
|
||||
if err := p.handleVocab(result, vocab); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 5.9)
|
||||
if lang, ok := ctxObj[KeywordLanguage]; ok {
|
||||
if err := p.handleLanguage(result, lang); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 5.10)
|
||||
if dir, ok := ctxObj[KeywordDirection]; ok {
|
||||
if err := p.handleDirection(result, dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 5.11)
|
||||
if prop, ok := ctxObj[KeywordPropagate]; ok {
|
||||
if err := p.handlePropagate(prop); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
protected := false
|
||||
if prot, ok := ctxObj[KeywordProtected]; ok && !json.IsNull(prot) {
|
||||
if err := json.Unmarshal(prot, &protected); err != nil {
|
||||
return nil, ErrInvalidProtectedValue
|
||||
}
|
||||
}
|
||||
|
||||
// 5.12)
|
||||
defined := map[string]*bool{}
|
||||
|
||||
// 5.13)
|
||||
for k := range ctxObj {
|
||||
switch k {
|
||||
case KeywordBase, KeywordDirection, KeywordImport,
|
||||
KeywordLanguage, KeywordPropagate, KeywordProtected,
|
||||
KeywordVersion, KeywordVocab:
|
||||
default:
|
||||
newOpts := newCreateTermOptions()
|
||||
newOpts.baseURL = baseURL
|
||||
newOpts.protected = protected
|
||||
newOpts.override = opts.override
|
||||
newOpts.remotes = slices.Clone(opts.remotes)
|
||||
if err := p.createTerm(
|
||||
result,
|
||||
ctxObj,
|
||||
k,
|
||||
defined,
|
||||
newOpts,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (p *Processor) handlePropagate(prop json.RawMessage) error {
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidContextEntry
|
||||
}
|
||||
|
||||
if json.IsNull(prop) {
|
||||
return ErrInvalidPropagateValue
|
||||
}
|
||||
|
||||
var b bool
|
||||
if err := json.Unmarshal(prop, &b); err != nil {
|
||||
return ErrInvalidPropagateValue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) handleDirection(result *Context, dir json.RawMessage) error {
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidContextEntry
|
||||
}
|
||||
|
||||
if json.IsNull(dir) {
|
||||
result.defaultDirection = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
var d string
|
||||
if err := json.Unmarshal(dir, &d); err != nil {
|
||||
return ErrInvalidBaseDirection
|
||||
}
|
||||
|
||||
switch d {
|
||||
case DirectionLTR, DirectionRTL:
|
||||
default:
|
||||
return ErrInvalidBaseDirection
|
||||
}
|
||||
|
||||
result.defaultDirection = d
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) handleLanguage(result *Context, lang json.RawMessage) error {
|
||||
if json.IsNull(lang) {
|
||||
result.defaultLang = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
var l string
|
||||
if err := json.Unmarshal(lang, &l); err != nil {
|
||||
return ErrInvalidDefaultLanguage
|
||||
}
|
||||
|
||||
result.defaultLang = strings.ToLower(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) handleVocab(result *Context, vocab json.RawMessage) error {
|
||||
// 5.8.2)
|
||||
if json.IsNull(vocab) {
|
||||
result.vocabMapping = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
var s string
|
||||
if err := json.Unmarshal(vocab, &s); err != nil {
|
||||
return ErrInvalidVocabMapping
|
||||
}
|
||||
|
||||
// 5.8.3)
|
||||
if !(url.IsIRI(s) || url.IsRelative(s) || s == BlankNode) {
|
||||
return ErrInvalidVocabMapping
|
||||
}
|
||||
|
||||
u, err := p.expandIRI(result, s, true, true, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.vocabMapping = u
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) handleBase(result *Context, base json.RawMessage) error {
|
||||
// 5.7.2)
|
||||
if json.IsNull(base) {
|
||||
result.currentBaseIRI = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
var iri string
|
||||
if err := json.Unmarshal(base, &iri); err != nil {
|
||||
return ErrInvalidBaseIRI
|
||||
}
|
||||
|
||||
// 5.7.3)
|
||||
if url.IsIRI(iri) {
|
||||
result.currentBaseIRI = iri
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5.7.4)
|
||||
if url.IsRelative(iri) {
|
||||
u, err := url.Resolve(result.currentBaseIRI, iri)
|
||||
if err != nil {
|
||||
return ErrInvalidBaseIRI
|
||||
}
|
||||
result.currentBaseIRI = u
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5.7.5)
|
||||
return ErrInvalidBaseIRI
|
||||
}
|
||||
|
||||
func (p *Processor) handleImport(baseURL string, data json.RawMessage, context map[string]json.RawMessage) (map[string]json.RawMessage, error) {
|
||||
// 5.6.1)
|
||||
if p.modeLD10 {
|
||||
return nil, ErrInvalidContextEntry
|
||||
}
|
||||
|
||||
// 5.6.2)
|
||||
var val string
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return nil, ErrInvalidImportValue
|
||||
}
|
||||
|
||||
// 5.6.3)
|
||||
iri, err := url.Resolve(baseURL, val)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidRemoteContext
|
||||
}
|
||||
|
||||
// 5.6.4) 5.6.5)
|
||||
res, err := p.retrieveRemoteContext(iri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 5.6.6)
|
||||
var ctxObj map[string]json.RawMessage
|
||||
if err := json.Unmarshal(res.Context, &ctxObj); err != nil {
|
||||
return nil, ErrInvalidRemoteContext
|
||||
}
|
||||
|
||||
// 5.6.7)
|
||||
if _, ok := ctxObj[KeywordImport]; ok {
|
||||
return nil, ErrInvalidContextEntry
|
||||
}
|
||||
|
||||
maps.Copy(ctxObj, context)
|
||||
return ctxObj, nil
|
||||
}
|
||||
|
||||
func (p *Processor) handleVersion(data json.RawMessage) error {
|
||||
var ver float64
|
||||
if err := json.Unmarshal(data, &ver); err != nil {
|
||||
return ErrInvalidVersionValue
|
||||
}
|
||||
if ver != 1.1 {
|
||||
return ErrInvalidVersionValue
|
||||
}
|
||||
if p.modeLD10 {
|
||||
return ErrProcessingMode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Processor) retrieveRemoteContext(
|
||||
iri string,
|
||||
) (Document, error) {
|
||||
// 5.2.4) 5.2.5) the document loader is expected to do the caching
|
||||
if p.loader == nil {
|
||||
return Document{}, fmt.Errorf("no loader %w", ErrLoadingRemoteContext)
|
||||
}
|
||||
doc, err := p.loader(context.TODO(), iri)
|
||||
if err != nil {
|
||||
return Document{}, err
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
type inverseContext map[string]map[string]mapping
|
||||
|
||||
type mapping struct {
|
||||
Language map[string]string
|
||||
Type map[string]string
|
||||
Any map[string]string
|
||||
}
|
||||
|
||||
// workIt flips a context and reverses it
|
||||
//
|
||||
// ti esrever dna ti pilf ,nwod gniht ym tuP
|
||||
func workIt(activeContext *Context) inverseContext {
|
||||
// 1)
|
||||
result := inverseContext{}
|
||||
|
||||
// 2)
|
||||
defaultLang := KeywordNone
|
||||
if activeContext.defaultLang != "" {
|
||||
defaultLang = strings.ToLower(activeContext.defaultLang)
|
||||
}
|
||||
|
||||
// 3)
|
||||
terms := slices.Collect(maps.Keys(activeContext.defs))
|
||||
slices.SortFunc(terms, sortedLeast)
|
||||
|
||||
for _, key := range terms {
|
||||
def := activeContext.defs[key]
|
||||
// 3.1)
|
||||
if def.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 3.2)
|
||||
container := KeywordNone
|
||||
if def.Container != nil {
|
||||
dc := slices.Clone(def.Container)
|
||||
slices.Sort(dc)
|
||||
container = strings.Join(dc, "")
|
||||
}
|
||||
|
||||
// 3.3)
|
||||
vvar := def.IRI
|
||||
|
||||
// 3.4)
|
||||
if _, ok := result[vvar]; !ok {
|
||||
result[vvar] = map[string]mapping{}
|
||||
}
|
||||
|
||||
// 3.5)
|
||||
containerMap := result[vvar]
|
||||
|
||||
// 3.6)
|
||||
if _, ok := containerMap[container]; !ok {
|
||||
containerMap[container] = mapping{
|
||||
Language: map[string]string{},
|
||||
Type: map[string]string{},
|
||||
Any: map[string]string{
|
||||
KeywordNone: key,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 3.7)
|
||||
typeLanguage := containerMap[container]
|
||||
|
||||
// 3.8)
|
||||
typeMap := typeLanguage.Type
|
||||
|
||||
// 3.9)
|
||||
langMap := typeLanguage.Language
|
||||
|
||||
if def.Reverse {
|
||||
// 3.10)
|
||||
if _, ok := typeMap[KeywordReverse]; !ok {
|
||||
typeMap[KeywordReverse] = key
|
||||
}
|
||||
} else if def.Type != "" {
|
||||
if def.Type == KeywordNone {
|
||||
// 3.11)
|
||||
if _, ok := langMap[KeywordAny]; !ok {
|
||||
// 3.11.1)
|
||||
langMap[KeywordAny] = key
|
||||
}
|
||||
if _, ok := typeMap[KeywordAny]; !ok {
|
||||
// 3.11.2)
|
||||
typeMap[KeywordAny] = key
|
||||
}
|
||||
} else {
|
||||
// 3.12)
|
||||
if _, ok := typeMap[def.Type]; !ok {
|
||||
// 3.12.1
|
||||
typeMap[def.Type] = key
|
||||
}
|
||||
}
|
||||
} else if def.Language != "" || def.Direction != "" {
|
||||
if def.Language != "" && def.Direction != "" {
|
||||
// 3.13)
|
||||
// 3.13.1) + 3.13.5)
|
||||
langDir := KeywordNone
|
||||
if def.Language != KeywordNull && def.Direction != KeywordNull {
|
||||
// 3.13.2)
|
||||
langDir = strings.ToLower(def.Language) + "_" + def.Direction
|
||||
} else if def.Language != KeywordNull {
|
||||
// 3.13.3)
|
||||
langDir = strings.ToLower(def.Language)
|
||||
} else if def.Direction != KeywordNull {
|
||||
// 3.13.4)
|
||||
langDir = "_" + def.Direction
|
||||
}
|
||||
// 3.13.6)
|
||||
if _, ok := langMap[langDir]; !ok {
|
||||
langMap[langDir] = key
|
||||
}
|
||||
} else if def.Language != "" {
|
||||
// 3.14)
|
||||
lang := KeywordNull
|
||||
if def.Language != KeywordNull {
|
||||
lang = strings.ToLower(def.Language)
|
||||
}
|
||||
if _, ok := langMap[lang]; !ok {
|
||||
langMap[lang] = key
|
||||
}
|
||||
} else if def.Direction != "" {
|
||||
// 3.15)
|
||||
dir := KeywordNone
|
||||
if def.Direction != KeywordNull {
|
||||
dir = "_" + def.Direction
|
||||
}
|
||||
if _, ok := langMap[dir]; !ok {
|
||||
langMap[dir] = key
|
||||
}
|
||||
}
|
||||
} else if activeContext.defaultDirection != "" {
|
||||
// 3.16)
|
||||
langDir := strings.ToLower(defaultLang) + "_" + activeContext.defaultDirection
|
||||
if _, ok := langMap[langDir]; !ok {
|
||||
langMap[langDir] = key
|
||||
}
|
||||
if _, ok := langMap[KeywordNone]; !ok {
|
||||
langMap[KeywordNone] = key
|
||||
}
|
||||
if _, ok := typeMap[KeywordNone]; !ok {
|
||||
typeMap[KeywordNone] = key
|
||||
}
|
||||
} else {
|
||||
// 3.17)
|
||||
|
||||
// 3.17.1)
|
||||
if _, ok := langMap[defaultLang]; !ok {
|
||||
langMap[defaultLang] = key
|
||||
}
|
||||
|
||||
// 3.17.2)
|
||||
if _, ok := langMap[KeywordNone]; !ok {
|
||||
langMap[KeywordNone] = key
|
||||
}
|
||||
|
||||
// 3.17.3)
|
||||
if _, ok := typeMap[KeywordNone]; !ok {
|
||||
typeMap[KeywordNone] = key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
44
vendor/sourcery.dny.nu/longdistance/doc.go
vendored
Normal file
44
vendor/sourcery.dny.nu/longdistance/doc.go
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Package longdistance can be used to process JSON-LD.
|
||||
//
|
||||
// You can turn incoming JSON into fully expanded JSON-LD using
|
||||
// [Processor.Expand]. This will transform the document into a list of [Node].
|
||||
// Each node has dedicated fields for each JSON-LD keyword, and the catch-all
|
||||
// [Node.Properties] for everything else. If you serialise this document to JSON
|
||||
// you'll get JSON-LD Expanded Document form.
|
||||
//
|
||||
// By calling [Processor.Compact] you can compact a list of [Node] to what looks
|
||||
// like regular JSON, based on the provided compaction context. The result is
|
||||
// serialised JSON that you can send out.
|
||||
//
|
||||
// By default a [Processor] cannot load remote contexts. You can install a
|
||||
// [RemoteContextLoaderFunc] using [WithRemoteContextLoader] when creating the
|
||||
// processor. You will need to provide your own. In order to not have
|
||||
// dependencies on the network when processing documents, it's strongly
|
||||
// recommended to create your own implementation of [RemoteContextLoaderFunc]
|
||||
// with the necessary contexts built-in. You can take a look at the FileLoader
|
||||
// in helpers_test.go.
|
||||
//
|
||||
// # JSON typing
|
||||
//
|
||||
// In order to provide a type-safe implementation, JSON scalars (numbers,
|
||||
// strings, booleans) are not decoded and stored as [json.RawMessage] instead.
|
||||
// You can use the optionally specified type to decide how to decode the value.
|
||||
// When the type is unspecified, the following rules can be used:
|
||||
// - Numbers with a zero fraction and smaller than 10^21 are int64.
|
||||
// - Numbers with a decimal point or a value greater than 10^21 are float64.
|
||||
// - Booleans are booleans.
|
||||
// - Anything else is a string.
|
||||
//
|
||||
// Certain numbers might be encoded as strings to avoid size or precision issues
|
||||
// with JSON number representation. They should have an accompanying type
|
||||
// definition to explain how to interpret them. Certain strings might also hold
|
||||
// a different value, like a timestamp or a duration. Those too should have a
|
||||
// type specifying how to interpret them.
|
||||
//
|
||||
// # Constraints
|
||||
//
|
||||
// For JSON-LD, there are a few extra constraints on top of JSON:
|
||||
// - Do not use keys that look like a JSON-LD keyword: @+alpha characters.
|
||||
// - Do not use the empty string for a key.
|
||||
// - Keys must be unique.
|
||||
package longdistance
|
||||
59
vendor/sourcery.dny.nu/longdistance/errors.go
vendored
Normal file
59
vendor/sourcery.dny.nu/longdistance/errors.go
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package longdistance
|
||||
|
||||
import "errors"
|
||||
|
||||
// Error types from the JSON-LD specification.
|
||||
var (
|
||||
ErrCollidingKeywords = errors.New("colliding keywords")
|
||||
ErrContextOverflow = errors.New("context overflow")
|
||||
ErrCyclicIRIMapping = errors.New("cyclic IRI mapping")
|
||||
ErrInvalidBaseDirection = errors.New("invalid base direction")
|
||||
ErrInvalidBaseIRI = errors.New("invalid base IRI")
|
||||
ErrInvalidContainerMapping = errors.New("invalid container mapping")
|
||||
ErrInvalidContextEntry = errors.New("invalid context entry")
|
||||
ErrInvalidContextNullificaton = errors.New("invalid context nullification")
|
||||
ErrInvalidDefaultLanguage = errors.New("invalid default language")
|
||||
ErrInvalidIDValue = errors.New("invalid @id value")
|
||||
ErrInvalidImportValue = errors.New("invalid @import value")
|
||||
ErrInvalidIncludedValue = errors.New("invalid @included value")
|
||||
ErrInvalidIndexValue = errors.New("invalid @index value")
|
||||
ErrInvalidIRIMapping = errors.New("invalid IRI mapping")
|
||||
ErrInvalidKeywordAlias = errors.New("invalid keyword alias")
|
||||
ErrInvalidLanguageMapping = errors.New("invalid language mapping")
|
||||
ErrInvalidLanguageMapValue = errors.New("invalid language map value")
|
||||
ErrInvalidLanguageTaggedString = errors.New("invalid language-tagged string")
|
||||
ErrInvalidLanguageTaggedValue = errors.New("invalid language-tagged value")
|
||||
ErrInvalidLocalContext = errors.New("invalid local context")
|
||||
ErrInvalidNestValue = errors.New("invalid @nest value")
|
||||
ErrInvalidPrefixValue = errors.New("invalid @prefix value")
|
||||
ErrInvalidPropagateValue = errors.New("invalid @propagate value")
|
||||
ErrInvalidProtectedValue = errors.New("invalid @protected value")
|
||||
ErrInvalidRemoteContext = errors.New("invalid remote context")
|
||||
ErrInvalidReverseProperty = errors.New("invalid reverse property")
|
||||
ErrInvalidReversePropertyMap = errors.New("invalid reverse property map")
|
||||
ErrInvalidReversePropertyValue = errors.New("invalid reverse property value")
|
||||
ErrInvalidReverseValue = errors.New("invalid @reverse value")
|
||||
ErrInvalidScopedContext = errors.New("invalid scoped context")
|
||||
ErrInvalidSetOrListObject = errors.New("invalid set or list object")
|
||||
ErrInvalidTermDefinition = errors.New("invalid term definition")
|
||||
ErrInvalidTypedValue = errors.New("invalid typed value")
|
||||
ErrInvalidTypeMapping = errors.New("invalid type mapping")
|
||||
ErrInvalidTypeValue = errors.New("invalid type value")
|
||||
ErrInvalidValueObject = errors.New("invalid value object")
|
||||
ErrInvalidValueObjectValue = errors.New("invalid value object value")
|
||||
ErrInvalidVersionValue = errors.New("invalid @version value")
|
||||
ErrInvalidVocabMapping = errors.New("invalid vocab mapping")
|
||||
ErrIRIConfusedWithPrefix = errors.New("IRI confused with prefix")
|
||||
ErrKeywordRedefinition = errors.New("keyword redefinition")
|
||||
ErrLoadingDocument = errors.New("loading document failed")
|
||||
ErrLoadingRemoteContext = errors.New("loading remote context failed")
|
||||
ErrProcessingMode = errors.New("processing mode conflict")
|
||||
ErrProtectedTermRedefinition = errors.New("protected term redefinition")
|
||||
ErrRecursiveContextInclusion = errors.New("recursive context inclusion")
|
||||
)
|
||||
|
||||
// Library-specific errors.
|
||||
var (
|
||||
ErrFrameExpansionUnsupported = errors.New("frame expansion is not supported")
|
||||
ErrPreserveUnsupported = errors.New("@preserve is not supported")
|
||||
)
|
||||
1270
vendor/sourcery.dny.nu/longdistance/expand.go
vendored
Normal file
1270
vendor/sourcery.dny.nu/longdistance/expand.go
vendored
Normal file
File diff suppressed because it is too large
Load diff
68
vendor/sourcery.dny.nu/longdistance/internal/json/json.go
vendored
Normal file
68
vendor/sourcery.dny.nu/longdistance/internal/json/json.go
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type RawMessage = json.RawMessage
|
||||
type Object map[string]RawMessage
|
||||
type Array []RawMessage
|
||||
|
||||
var Compact = json.Compact
|
||||
var Marshal = json.Marshal
|
||||
var MarshalIndent = json.MarshalIndent
|
||||
var Valid = json.Valid
|
||||
var Unmarshal = json.Unmarshal
|
||||
|
||||
var (
|
||||
beginArray = byte('[')
|
||||
beginObject = byte('{')
|
||||
beginString = byte('"')
|
||||
null = RawMessage(`null`)
|
||||
)
|
||||
|
||||
func IsNull(in RawMessage) bool {
|
||||
return bytes.Equal(in, null)
|
||||
}
|
||||
|
||||
func IsArray(in RawMessage) bool {
|
||||
if len(in) == 0 {
|
||||
return false
|
||||
}
|
||||
return in[0] == beginArray
|
||||
}
|
||||
|
||||
func IsMap(in RawMessage) bool {
|
||||
if len(in) == 0 {
|
||||
return false
|
||||
}
|
||||
return in[0] == beginObject
|
||||
}
|
||||
|
||||
func IsString(in RawMessage) bool {
|
||||
if len(in) == 0 {
|
||||
return false
|
||||
}
|
||||
return in[0] == beginString
|
||||
}
|
||||
|
||||
func IsScalar(in RawMessage) bool {
|
||||
return !IsArray(in) && !IsMap(in) && !IsNull(in)
|
||||
}
|
||||
|
||||
func MakeArray(in RawMessage) RawMessage {
|
||||
if len(in) == 0 {
|
||||
return json.RawMessage(`[]`)
|
||||
}
|
||||
|
||||
if IsArray(in) {
|
||||
return in
|
||||
}
|
||||
|
||||
return bytes.Join([][]byte{
|
||||
[]byte(`[`),
|
||||
in,
|
||||
[]byte(`]`),
|
||||
}, nil)
|
||||
}
|
||||
116
vendor/sourcery.dny.nu/longdistance/internal/url/url.go
vendored
Normal file
116
vendor/sourcery.dny.nu/longdistance/internal/url/url.go
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
package url
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Parse = url.Parse
|
||||
|
||||
func Relative(base string, iri string) (string, error) {
|
||||
baseURL, err := Parse(base)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse base URL: %w", err)
|
||||
}
|
||||
|
||||
absURL, err := Parse(iri)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse absolute URL: %w", err)
|
||||
}
|
||||
|
||||
if baseURL.Scheme != absURL.Scheme || baseURL.Host != absURL.Host {
|
||||
return "", fmt.Errorf("cannot create relative URL when host or scheme differ")
|
||||
}
|
||||
|
||||
basePath := baseURL.EscapedPath()
|
||||
absPath := absURL.EscapedPath()
|
||||
if basePath == absPath {
|
||||
if absURL.Fragment != "" || absURL.RawQuery != "" {
|
||||
return (&url.URL{
|
||||
RawQuery: absURL.RawQuery,
|
||||
Fragment: absURL.Fragment,
|
||||
}).String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
last := strings.LastIndex(basePath, "/")
|
||||
basePath = basePath[:last+1]
|
||||
baseParts := strings.Split(basePath, "/")
|
||||
absParts := strings.Split(absPath, "/")
|
||||
|
||||
prefix := 0
|
||||
lap := len(absParts)
|
||||
count := min(len(baseParts), lap)
|
||||
for i, elem := range baseParts[:count] {
|
||||
if elem == absParts[i] {
|
||||
prefix++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
relpaths := make([]string, 0, len(baseParts)-prefix)
|
||||
for range baseParts[prefix+1:] {
|
||||
relpaths = append(relpaths, "..")
|
||||
}
|
||||
|
||||
relpaths = append(relpaths, absParts[prefix:]...)
|
||||
final := path.Join(relpaths...)
|
||||
|
||||
// Include query and fragment if present
|
||||
relURL := &url.URL{
|
||||
Path: final,
|
||||
RawQuery: absURL.RawQuery,
|
||||
Fragment: absURL.Fragment,
|
||||
}
|
||||
|
||||
res := relURL.String()
|
||||
if strings.HasSuffix(res, "..") {
|
||||
res = res + "/"
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func EndsInGenDelim(s string) bool {
|
||||
delims := []string{":", "/", "?", "#", "[", "]", "@"}
|
||||
last := s[len(s)-1:]
|
||||
return slices.Contains(delims, last)
|
||||
}
|
||||
|
||||
func IsRelative(s string) bool {
|
||||
_, err := Parse(s)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func IsIRI(s string) bool {
|
||||
u, err := Parse(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ns := u.String()
|
||||
if strings.HasSuffix(s, "#") {
|
||||
// preserve the empty fragment
|
||||
ns = ns + "#"
|
||||
}
|
||||
|
||||
return u.IsAbs() && s == ns
|
||||
}
|
||||
|
||||
func Resolve(base string, val string) (string, error) {
|
||||
r, err := Parse(val)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
u, err := Parse(base)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return u.ResolveReference(r).String(), nil
|
||||
}
|
||||
131
vendor/sourcery.dny.nu/longdistance/iri.go
vendored
Normal file
131
vendor/sourcery.dny.nu/longdistance/iri.go
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
"sourcery.dny.nu/longdistance/internal/url"
|
||||
)
|
||||
|
||||
func (p *Processor) expandIRI(
|
||||
activeContext *Context,
|
||||
value string,
|
||||
relative bool,
|
||||
vocab bool,
|
||||
localContext map[string]json.RawMessage,
|
||||
defined map[string]*bool,
|
||||
) (string, error) {
|
||||
// 1)
|
||||
if isKeyword(value) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// 2)
|
||||
if looksLikeKeyword(value) {
|
||||
p.logger.Warn("keyword lookalike value encountered",
|
||||
slog.String("value", value))
|
||||
// we can't generate a warning, so return nil
|
||||
// any empty values will be dropped
|
||||
return "", nil
|
||||
}
|
||||
|
||||
hasLocal := len(localContext) > 0
|
||||
|
||||
// 3)
|
||||
if hasLocal {
|
||||
if _, ok := localContext[value]; ok {
|
||||
if v := defined[value]; v == nil || !*v {
|
||||
if err := p.createTerm(
|
||||
activeContext,
|
||||
localContext,
|
||||
value,
|
||||
defined,
|
||||
newCreateTermOptions(),
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4)
|
||||
if activeContext != nil {
|
||||
if t, ok := activeContext.defs[value]; ok {
|
||||
if isKeyword(t.IRI) {
|
||||
return t.IRI, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5)
|
||||
if vocab {
|
||||
if activeContext != nil {
|
||||
if t, ok := activeContext.defs[value]; ok {
|
||||
return t.IRI, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6)
|
||||
if strings.Index(value, ":") >= 1 {
|
||||
// 6.1)
|
||||
prefix, suffix, found := strings.Cut(value, ":")
|
||||
if found {
|
||||
// 6.2)
|
||||
if prefix == "_" || strings.HasPrefix(suffix, "//") {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// 6.3)
|
||||
if hasLocal {
|
||||
if _, ok := localContext[prefix]; ok {
|
||||
if v := defined[prefix]; v == nil || !*v {
|
||||
if err := p.createTerm(
|
||||
activeContext,
|
||||
localContext,
|
||||
prefix,
|
||||
defined,
|
||||
newCreateTermOptions(),
|
||||
); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6.4)
|
||||
if activeContext != nil {
|
||||
if t, ok := activeContext.defs[prefix]; ok && t.IRI != "" && t.Prefix {
|
||||
return t.IRI + suffix, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 6.5)
|
||||
if url.IsIRI(value) {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7)
|
||||
if vocab {
|
||||
if activeContext.vocabMapping != "" {
|
||||
return activeContext.vocabMapping + value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 8)
|
||||
if relative {
|
||||
if activeContext.currentBaseIRI == "" {
|
||||
return value, nil
|
||||
}
|
||||
u, err := url.Resolve(activeContext.currentBaseIRI, value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
96
vendor/sourcery.dny.nu/longdistance/keywords.go
vendored
Normal file
96
vendor/sourcery.dny.nu/longdistance/keywords.go
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package longdistance
|
||||
|
||||
// JSON-LD keywords.
|
||||
const (
|
||||
KeywordAny = "@any"
|
||||
KeywordBase = "@base"
|
||||
KeywordContainer = "@container"
|
||||
KeywordContext = "@context"
|
||||
KeywordDefault = "@default"
|
||||
KeywordDirection = "@direction"
|
||||
KeywordGraph = "@graph"
|
||||
KeywordID = "@id"
|
||||
KeywordImport = "@import"
|
||||
KeywordIncluded = "@included"
|
||||
KeywordIndex = "@index"
|
||||
KeywordJSON = "@json"
|
||||
KeywordLanguage = "@language"
|
||||
KeywordList = "@list"
|
||||
KeywordNest = "@nest"
|
||||
KeywordNone = "@none"
|
||||
KeywordNull = "@null"
|
||||
KeywordPrefix = "@prefix"
|
||||
KeywordPreserve = "@preserve"
|
||||
KeywordPropagate = "@propagate"
|
||||
KeywordProtected = "@protected"
|
||||
KeywordReverse = "@reverse"
|
||||
KeywordSet = "@set"
|
||||
KeywordType = "@type"
|
||||
KeywordValue = "@value"
|
||||
KeywordVersion = "@version"
|
||||
KeywordVocab = "@vocab"
|
||||
)
|
||||
|
||||
// isKeyword returns if the string matches a known JSON-LD keyword.
|
||||
func isKeyword(s string) bool {
|
||||
switch s {
|
||||
case KeywordBase,
|
||||
KeywordContainer,
|
||||
KeywordContext,
|
||||
KeywordDefault,
|
||||
KeywordDirection,
|
||||
KeywordGraph,
|
||||
KeywordID,
|
||||
KeywordImport,
|
||||
KeywordIncluded,
|
||||
KeywordIndex,
|
||||
KeywordJSON,
|
||||
KeywordLanguage,
|
||||
KeywordList,
|
||||
KeywordNest,
|
||||
KeywordNone,
|
||||
KeywordPrefix,
|
||||
KeywordPreserve,
|
||||
KeywordPropagate,
|
||||
KeywordProtected,
|
||||
KeywordReverse,
|
||||
KeywordSet,
|
||||
KeywordType,
|
||||
KeywordValue,
|
||||
KeywordVersion,
|
||||
KeywordVocab:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// looksLikeKeyword determines if a string has the general shape of a JSON-LD
|
||||
// keyword.
|
||||
//
|
||||
// It returns true for strings of the form: "@[alpha]".
|
||||
//
|
||||
// This means that a string like @blabla1 will return false, but it's still
|
||||
// strongly recommended against using those for keys just to avoid confusion.
|
||||
func looksLikeKeyword(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if s == "@" {
|
||||
return false
|
||||
}
|
||||
|
||||
if s[0] != '@' {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, char := range s[1:] {
|
||||
if (char < 'a' || char > 'z') &&
|
||||
(char < 'A' || char > 'Z') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
34
vendor/sourcery.dny.nu/longdistance/loader.go
vendored
Normal file
34
vendor/sourcery.dny.nu/longdistance/loader.go
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
)
|
||||
|
||||
// RemoteContextLoaderFunc is called to retrieve a remote context.
|
||||
//
|
||||
// It returns a Document, and an error in case retrieval failed.
|
||||
//
|
||||
// When building your own loader, please remember that:
|
||||
// - [Document.URL] is the URL the context was retrieved from after having
|
||||
// followed any redirects.
|
||||
// - [Document.Context] is the value of the [KeywordContext] in the returned
|
||||
// document, or the empty JSON map if the context was absent.
|
||||
// - Request a context with [ApplicationLDJSON] and profile [ProfileContext].
|
||||
// You can use [mime.FormatMediaType] to build the value for the Accept
|
||||
// header.
|
||||
// - Have proper timeouts, retry handling and request deduplication.
|
||||
// - Make sure to cache the resulting [Document] to avoid unnecessary future
|
||||
// requests. Contexts should not change for the lifetime of the application.
|
||||
type RemoteContextLoaderFunc func(context.Context, string) (Document, error)
|
||||
|
||||
// Document holds a retrieved context.
|
||||
//
|
||||
// - URL holds the final URL a context was retrieved from, after following
|
||||
// redirects.
|
||||
// - Context holds the value of the @context element, or the empty map.
|
||||
type Document struct {
|
||||
URL string
|
||||
Context json.RawMessage
|
||||
}
|
||||
368
vendor/sourcery.dny.nu/longdistance/node.go
vendored
Normal file
368
vendor/sourcery.dny.nu/longdistance/node.go
vendored
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
)
|
||||
|
||||
// Properties is a key-to-array-of-[Node] map.
|
||||
//
|
||||
// It's used to hold any property that's not a JSON-LD keyword.
|
||||
type Properties map[string][]Node
|
||||
|
||||
// Node represents a node in a JSON-LD graph.
|
||||
//
|
||||
// Every supported JSON-LD keyword has a field of its own. All remaining
|
||||
// properties are tracked on the Properties field.
|
||||
type Node struct {
|
||||
Direction string // @direction / KeywordDirection
|
||||
Graph []Node // @graph / KeywordGraph
|
||||
ID string // @id / KeywordID
|
||||
Included []Node // @included / KeywordIncluded
|
||||
Index string // @index / KeywordIndex
|
||||
Language string // @language / KeywordLanguage
|
||||
List []Node // @list / KeywordList
|
||||
Reverse Properties // @reverse / KeywordReverse
|
||||
Set []Node // @set / KeywordSet
|
||||
Type []string // @type / KeywordType
|
||||
Value json.RawMessage // @value / KeywordValue
|
||||
|
||||
Properties Properties // everything else
|
||||
}
|
||||
|
||||
// Internal is a generic type that matches the internals of [Node].
|
||||
//
|
||||
// This can be used to convert to a [Node] from any type outside this package
|
||||
// that happens to be a [Node] underneath.
|
||||
type Internal interface {
|
||||
~struct {
|
||||
Direction string
|
||||
Graph []Node
|
||||
ID string
|
||||
Included []Node
|
||||
Index string
|
||||
Language string
|
||||
List []Node
|
||||
Reverse Properties
|
||||
Set []Node
|
||||
Type []string
|
||||
Value json.RawMessage
|
||||
Properties Properties
|
||||
}
|
||||
}
|
||||
|
||||
// PropertySet returns a [Set] with an entry for each property that is set on
|
||||
// the [Node].
|
||||
func (n *Node) PropertySet() map[string]struct{} {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
res := make(map[string]struct{}, len(n.Properties)+2)
|
||||
if n.Has(KeywordDirection) {
|
||||
res[KeywordDirection] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordGraph) {
|
||||
res[KeywordGraph] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordID) {
|
||||
res[KeywordID] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordIncluded) {
|
||||
res[KeywordIncluded] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordIndex) {
|
||||
res[KeywordIndex] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordLanguage) {
|
||||
res[KeywordLanguage] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordList) {
|
||||
res[KeywordList] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordReverse) {
|
||||
res[KeywordReverse] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordSet) {
|
||||
res[KeywordSet] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordType) {
|
||||
res[KeywordType] = struct{}{}
|
||||
}
|
||||
if n.Has(KeywordValue) {
|
||||
res[KeywordValue] = struct{}{}
|
||||
}
|
||||
|
||||
for p := range n.Properties {
|
||||
res[p] = struct{}{}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (n *Node) propsWithout(props ...string) map[string]struct{} {
|
||||
nprops := n.PropertySet()
|
||||
for _, prop := range props {
|
||||
delete(nprops, prop)
|
||||
}
|
||||
return nprops
|
||||
}
|
||||
|
||||
func (n *Node) isNode() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return !n.Has(KeywordList) && !n.Has(KeywordValue) && !n.Has(KeywordSet)
|
||||
}
|
||||
|
||||
// Has returns if a node has the requested property.
|
||||
//
|
||||
// Properties must either be a JSON-LD keyword, or an expanded IRI.
|
||||
func (n *Node) Has(prop string) bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch prop {
|
||||
case KeywordID:
|
||||
return n.ID != ""
|
||||
case KeywordValue:
|
||||
return n.Value != nil
|
||||
case KeywordLanguage:
|
||||
return n.Language != ""
|
||||
case KeywordDirection:
|
||||
return n.Direction != ""
|
||||
case KeywordType:
|
||||
return n.Type != nil
|
||||
case KeywordList:
|
||||
return n.List != nil
|
||||
case KeywordSet:
|
||||
return n.Set != nil
|
||||
case KeywordGraph:
|
||||
return n.Graph != nil
|
||||
case KeywordIncluded:
|
||||
return n.Included != nil
|
||||
case KeywordIndex:
|
||||
return n.Index != ""
|
||||
case KeywordReverse:
|
||||
return n.Reverse != nil
|
||||
default:
|
||||
for key := range n.Properties {
|
||||
if prop == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsZero returns if this is the zero value of a [Node].
|
||||
func (n *Node) IsZero() bool {
|
||||
if n == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return len(n.PropertySet()) == 0
|
||||
}
|
||||
|
||||
// IsSubject checks if this node is a subject.
|
||||
//
|
||||
// This means:
|
||||
// - It has an @id.
|
||||
// - It may have an @type.
|
||||
// - It has at least one other property.
|
||||
func (n *Node) IsSubject() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordID) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(KeywordID, KeywordIndex)) != 0
|
||||
}
|
||||
|
||||
// IsSubjectReference checks if this node is a subject reference.
|
||||
//
|
||||
// This means:
|
||||
// - It has an @id.
|
||||
// - It may have an @type.
|
||||
// - It has no other properties.
|
||||
func (n *Node) IsSubjectReference() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordID) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(KeywordID, KeywordType)) == 0
|
||||
}
|
||||
|
||||
// IsList checks if this node is a list.
|
||||
//
|
||||
// This means:
|
||||
// - It has an @list.
|
||||
// - It has no other properties.
|
||||
func (n *Node) IsList() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordList) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(KeywordList, KeywordIndex)) == 0
|
||||
}
|
||||
|
||||
// IsValue checks if this is a value node.
|
||||
//
|
||||
// This means:
|
||||
// - It has an @value.
|
||||
// - It may have an @direction, @index, @langauge and @type.
|
||||
// - It has no other properties.
|
||||
//
|
||||
// Additionally, it's invalid to have @type together with @language or
|
||||
// @direction.
|
||||
func (n *Node) IsValue() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordValue) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(
|
||||
KeywordValue,
|
||||
KeywordDirection,
|
||||
KeywordIndex,
|
||||
KeywordLanguage,
|
||||
KeywordType,
|
||||
)) == 0
|
||||
}
|
||||
|
||||
// IsGraph returns if the object is a graph.
|
||||
//
|
||||
// This requires:
|
||||
// - It must have an @graph.
|
||||
// - It may have @id and @index.
|
||||
// - It has no other properties.
|
||||
func (n *Node) IsGraph() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordGraph) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(KeywordID, KeywordIndex, KeywordGraph)) == 0
|
||||
}
|
||||
|
||||
// IsSimpleGraph returns if the object is a simple graph.
|
||||
//
|
||||
// This requires:
|
||||
// - It must have an @graph.
|
||||
// - It may have @index.
|
||||
// - It has no other properties.
|
||||
func (n *Node) IsSimpleGraph() bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !n.Has(KeywordGraph) {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(n.propsWithout(KeywordIndex, KeywordGraph)) == 0
|
||||
}
|
||||
|
||||
// MarshalJSON encodes to Expanded Document Form.
|
||||
func (n *Node) MarshalJSON() ([]byte, error) {
|
||||
result := map[string]any{}
|
||||
|
||||
if n.Has(KeywordID) {
|
||||
result[KeywordID] = n.ID
|
||||
}
|
||||
|
||||
if n.Has(KeywordIndex) {
|
||||
result[KeywordIndex] = n.Index
|
||||
}
|
||||
|
||||
if n.Has(KeywordType) {
|
||||
var data any
|
||||
if n.Value != nil && len(n.Type) == 1 {
|
||||
data = n.Type[0]
|
||||
} else {
|
||||
data = n.Type
|
||||
}
|
||||
result[KeywordType] = data
|
||||
}
|
||||
|
||||
if n.Has(KeywordValue) {
|
||||
result[KeywordValue] = n.Value
|
||||
}
|
||||
|
||||
if n.Has(KeywordLanguage) {
|
||||
result[KeywordLanguage] = n.Language
|
||||
}
|
||||
|
||||
if n.Has(KeywordDirection) {
|
||||
result[KeywordDirection] = n.Direction
|
||||
}
|
||||
|
||||
if n.Has(KeywordList) {
|
||||
result[KeywordList] = n.List
|
||||
}
|
||||
|
||||
if n.Has(KeywordGraph) {
|
||||
result[KeywordGraph] = n.Graph
|
||||
}
|
||||
|
||||
if n.Has(KeywordIncluded) {
|
||||
result[KeywordIncluded] = n.Included
|
||||
}
|
||||
|
||||
if n.Has(KeywordReverse) {
|
||||
result[KeywordReverse] = n.Reverse
|
||||
}
|
||||
|
||||
for k, v := range n.Properties {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
return json.Marshal(result)
|
||||
}
|
||||
|
||||
// GetNodes returns the nodes stored in property.
|
||||
func (n *Node) GetNodes(property string) []Node {
|
||||
switch property {
|
||||
case KeywordGraph:
|
||||
return n.Graph
|
||||
case KeywordIncluded:
|
||||
return n.Included
|
||||
case KeywordList:
|
||||
return n.List
|
||||
case KeywordSet:
|
||||
return n.Set
|
||||
default:
|
||||
if !n.Has(property) {
|
||||
return nil
|
||||
}
|
||||
return n.Properties[property]
|
||||
}
|
||||
}
|
||||
|
||||
// AddNodes appends the nodes stored in property.
|
||||
func (n *Node) AddNodes(property string, nodes ...Node) {
|
||||
n.Properties[property] = append(n.Properties[property], nodes...)
|
||||
}
|
||||
|
||||
// SetNodes overrides the nodes stored in property.
|
||||
func (n *Node) SetNodes(property string, nodes ...Node) {
|
||||
n.Properties[property] = nodes
|
||||
}
|
||||
142
vendor/sourcery.dny.nu/longdistance/processor.go
vendored
Normal file
142
vendor/sourcery.dny.nu/longdistance/processor.go
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
)
|
||||
|
||||
// ProcessorOption can be used to customise the behaviour of a [Processor].
|
||||
type ProcessorOption func(*Processor)
|
||||
|
||||
// Processor represents a JSON-LD processor.
|
||||
//
|
||||
// Your application should only ever need one of them. Do not create a new one
|
||||
// for each request you're handling.
|
||||
//
|
||||
// Create one with [NewProcessor] and pass any [ProcessorOption] to configure
|
||||
// the processor.
|
||||
type Processor struct {
|
||||
modeLD10 bool
|
||||
ordered bool
|
||||
baseIRI string
|
||||
compactArrays bool
|
||||
compactToRelative bool
|
||||
loader RemoteContextLoaderFunc
|
||||
logger *slog.Logger
|
||||
expandContext json.RawMessage
|
||||
excludeIRIsFromCompaction []string
|
||||
remapPrefixIRIs map[string]string
|
||||
}
|
||||
|
||||
// NewProcessor creates a new JSON-LD processor.
|
||||
//
|
||||
// By default:
|
||||
// - Processing mode is JSON-LD 1.1. This can handle both JSON-LD 1.0 and
|
||||
// JSON-LD 1.1 documents. To switch to JSON-LD 1.0 only, configure it with
|
||||
// [With10Processing].
|
||||
// - No loader is configured. Without one, remote contexts as well as @import
|
||||
// contexts cannot be processed. Set it with [WithRemoteContextLoader].
|
||||
// - Arrays are compacted. Change it with [WithCompactArrays].
|
||||
// - IRIs can compact to relative IRIs. Change it with
|
||||
// [WithCompactToRelative].
|
||||
// - Logger is [slog.DiscardHandler]. Set it with [WithLogger]. The logger is
|
||||
// only used to emit warnings.
|
||||
func NewProcessor(options ...ProcessorOption) *Processor {
|
||||
p := &Processor{
|
||||
compactArrays: true,
|
||||
compactToRelative: true,
|
||||
logger: slog.New(slog.DiscardHandler),
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// With10Processing sets the processing mode to json-ld-1.0.
|
||||
func With10Processing(b bool) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.modeLD10 = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithRemoteContextLoader sets the context loader function.
|
||||
func WithRemoteContextLoader(l RemoteContextLoaderFunc) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.loader = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets the logger that'll be used to emit warnings during
|
||||
// processing.
|
||||
//
|
||||
// Without a logger no warnings will be emitted when keyword lookalikes are
|
||||
// encountered that are ignored.
|
||||
func WithLogger(l *slog.Logger) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// WithOrdered ensures that object elements and language maps are processed in
|
||||
// lexicographical order.
|
||||
//
|
||||
// This is typically not needed, but helps to stabilise the test suite.
|
||||
func WithOrdered(b bool) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.ordered = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaseIRI sets an explicit base IRI to use.
|
||||
func WithBaseIRI(iri string) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.baseIRI = iri
|
||||
}
|
||||
}
|
||||
|
||||
// WithCompactArrays sets whether single-valued arrays should
|
||||
// be reduced to their value where possible.
|
||||
func WithCompactArrays(b bool) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.compactArrays = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithCompactToRelative sets whether IRIs can be transformed into
|
||||
// relative IRIs during IRI compaction.
|
||||
func WithCompactToRelative(b bool) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.compactToRelative = b
|
||||
}
|
||||
}
|
||||
|
||||
// WithExpandContext provides an additional out-of-band context
|
||||
// that's used during expansion.
|
||||
func WithExpandContext(ctx json.RawMessage) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.expandContext = ctx
|
||||
}
|
||||
}
|
||||
|
||||
// WithExcludeIRIsFromCompaction disables IRI compaction for the specified IRIs.
|
||||
func WithExcludeIRIsFromCompaction(iri ...string) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
p.excludeIRIsFromCompaction = iri
|
||||
}
|
||||
}
|
||||
|
||||
// WithRemapPrefixIRIs can remap a prefix IRI during context processing.
|
||||
//
|
||||
// Prefixes are only remapped for an exact match.
|
||||
//
|
||||
// This is useful to remap the incorrect schema.org# to schema.org/.
|
||||
func WithRemapPrefixIRIs(old, new string) ProcessorOption {
|
||||
return func(p *Processor) {
|
||||
if p.remapPrefixIRIs == nil {
|
||||
p.remapPrefixIRIs = make(map[string]string, 2)
|
||||
}
|
||||
p.remapPrefixIRIs[old] = new
|
||||
}
|
||||
}
|
||||
771
vendor/sourcery.dny.nu/longdistance/term.go
vendored
Normal file
771
vendor/sourcery.dny.nu/longdistance/term.go
vendored
Normal file
|
|
@ -0,0 +1,771 @@
|
|||
package longdistance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"sourcery.dny.nu/longdistance/internal/json"
|
||||
"sourcery.dny.nu/longdistance/internal/url"
|
||||
)
|
||||
|
||||
// Term represents a term definition in a JSON-LD context.
|
||||
type Term struct {
|
||||
IRI string
|
||||
Prefix bool
|
||||
Protected bool
|
||||
Reverse bool
|
||||
|
||||
BaseIRI string
|
||||
Context json.RawMessage
|
||||
Container []string
|
||||
Direction string
|
||||
Index string
|
||||
Language string
|
||||
Nest string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (t *Term) equalWithoutProtected(ot *Term) bool {
|
||||
if t == nil && ot == nil {
|
||||
return true
|
||||
}
|
||||
if t == nil || ot == nil {
|
||||
return false
|
||||
}
|
||||
if t.IRI != ot.IRI {
|
||||
return false
|
||||
}
|
||||
if t.Prefix != ot.Prefix {
|
||||
return false
|
||||
}
|
||||
if t.Reverse != ot.Reverse {
|
||||
return false
|
||||
}
|
||||
if t.BaseIRI != ot.BaseIRI {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(t.Context, ot.Context) {
|
||||
return false
|
||||
}
|
||||
if !slices.Equal(t.Container, ot.Container) {
|
||||
return false
|
||||
}
|
||||
if t.Direction != ot.Direction {
|
||||
return false
|
||||
}
|
||||
if t.Index != ot.Index {
|
||||
return false
|
||||
}
|
||||
if t.Language != ot.Language {
|
||||
return false
|
||||
}
|
||||
if t.Nest != ot.Nest {
|
||||
return false
|
||||
}
|
||||
if t.Type != ot.Type {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Term) IsZero() bool {
|
||||
if t == nil {
|
||||
return true
|
||||
}
|
||||
return t.IRI == "" && !t.Prefix && !t.Protected &&
|
||||
!t.Reverse && t.BaseIRI == "" && t.Context == nil &&
|
||||
t.Container == nil && t.Direction == "" &&
|
||||
t.Index == "" && t.Language == "" && t.Nest == "" &&
|
||||
t.Type == ""
|
||||
}
|
||||
|
||||
type createTermOptions struct {
|
||||
baseURL string
|
||||
protected bool
|
||||
override bool
|
||||
remotes []string
|
||||
validate bool
|
||||
}
|
||||
|
||||
func newCreateTermOptions() createTermOptions {
|
||||
return createTermOptions{
|
||||
validate: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Processor) createTerm(
|
||||
activeContext *Context,
|
||||
localContext map[string]json.RawMessage,
|
||||
term string,
|
||||
defined map[string]*bool,
|
||||
opts createTermOptions,
|
||||
) error {
|
||||
// 1)
|
||||
if v := defined[term]; v != nil {
|
||||
if *v {
|
||||
return nil
|
||||
}
|
||||
return ErrCyclicIRIMapping
|
||||
}
|
||||
|
||||
// 2)
|
||||
if term == "" {
|
||||
return ErrInvalidTermDefinition
|
||||
} else {
|
||||
b := false
|
||||
defined[term] = &b
|
||||
}
|
||||
|
||||
// 3)
|
||||
value := localContext[term]
|
||||
|
||||
// 4)
|
||||
if term == KeywordType {
|
||||
if p.modeLD10 {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
|
||||
var obj map[string]json.RawMessage
|
||||
if err := json.Unmarshal(value, &obj); err != nil {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
|
||||
if len(obj) == 0 {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
|
||||
objCopy := maps.Clone(obj)
|
||||
delete(objCopy, KeywordContainer)
|
||||
delete(objCopy, KeywordProtected)
|
||||
if len(objCopy) != 0 {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
|
||||
if v, ok := obj[KeywordContainer]; ok {
|
||||
var s string
|
||||
if err := json.Unmarshal(v, &s); err != nil {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
if s != KeywordSet {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
}
|
||||
if v, ok := obj[KeywordProtected]; ok {
|
||||
var b bool
|
||||
if err := json.Unmarshal(v, &b); err != nil {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 5)
|
||||
if isKeyword(term) {
|
||||
return ErrKeywordRedefinition
|
||||
}
|
||||
|
||||
if looksLikeKeyword(term) {
|
||||
p.logger.Warn("keyword lookalike term encountered", slog.String("term", term))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 6)
|
||||
oldDef, oldDefOK := activeContext.defs[term]
|
||||
delete(activeContext.defs, term)
|
||||
if !oldDefOK {
|
||||
// check for aliasses
|
||||
for _, def := range activeContext.defs {
|
||||
if def.IRI != "" && def.IRI == term {
|
||||
oldDef = def
|
||||
oldDefOK = true
|
||||
delete(activeContext.defs, term)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
simpleTerm := false
|
||||
var valueObj map[string]json.RawMessage
|
||||
|
||||
// 7) 8)
|
||||
if json.IsNull(value) || json.IsString(value) {
|
||||
// 8)
|
||||
if json.IsString(value) {
|
||||
simpleTerm = true
|
||||
}
|
||||
|
||||
// 7)
|
||||
value = bytes.Join([][]byte{
|
||||
[]byte(`{"@id":`),
|
||||
value,
|
||||
[]byte(`}`),
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// 9)
|
||||
if err := json.Unmarshal(value, &valueObj); err != nil {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 10)
|
||||
termDef := Term{
|
||||
Prefix: false,
|
||||
Protected: opts.protected,
|
||||
Reverse: false,
|
||||
}
|
||||
|
||||
// 11)
|
||||
if prot, ok := valueObj[KeywordProtected]; ok {
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
var b bool
|
||||
if err := json.Unmarshal(prot, &b); err != nil {
|
||||
return ErrInvalidProtectedValue
|
||||
}
|
||||
termDef.Protected = b
|
||||
}
|
||||
|
||||
// at this point protected is finalised, so add the
|
||||
// term to the protected set on activeContext
|
||||
if termDef.Protected {
|
||||
activeContext.protected[term] = struct{}{}
|
||||
}
|
||||
|
||||
// 12)
|
||||
if typ, ok := valueObj[KeywordType]; ok {
|
||||
if json.IsNull(typ) {
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
|
||||
var s string
|
||||
// 12.1)
|
||||
if err := json.Unmarshal(typ, &s); err != nil {
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
|
||||
// 12.2)
|
||||
u, err := p.expandIRI(activeContext, s, false, true, localContext, defined)
|
||||
if err != nil {
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
|
||||
// 12.3
|
||||
if p.modeLD10 {
|
||||
if u == KeywordNone || u == KeywordJSON {
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
}
|
||||
|
||||
// 12.4)
|
||||
switch u {
|
||||
case KeywordID, KeywordJSON, KeywordNone, KeywordVocab:
|
||||
default:
|
||||
if !url.IsIRI(u) {
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
}
|
||||
|
||||
// 12.5)
|
||||
termDef.Type = u
|
||||
}
|
||||
|
||||
// prep for branch 14)
|
||||
id, idOK := valueObj[KeywordID]
|
||||
var idStr string
|
||||
idErr := json.Unmarshal(id, &idStr)
|
||||
|
||||
// 13)
|
||||
if rev, ok := valueObj[KeywordReverse]; ok {
|
||||
_, hasID := valueObj[KeywordID]
|
||||
_, hasNest := valueObj[KeywordNest]
|
||||
// 13.1)
|
||||
if hasID || hasNest {
|
||||
return ErrInvalidReverseProperty
|
||||
}
|
||||
|
||||
// 13.2)
|
||||
if json.IsNull(rev) {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
var s string
|
||||
if err := json.Unmarshal(rev, &s); err != nil {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
// 13.3)
|
||||
if looksLikeKeyword(s) {
|
||||
p.logger.Warn("keyword lookalike value encountered",
|
||||
slog.String("value", s))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 13.4)
|
||||
u, err := p.expandIRI(activeContext, s, false, true, localContext, defined)
|
||||
if err != nil {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
if !url.IsIRI(u) && u != BlankNode {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
termDef.IRI = u
|
||||
|
||||
// 13.5)
|
||||
if v, ok := valueObj[KeywordContainer]; ok {
|
||||
if json.IsNull(v) {
|
||||
termDef.Container = nil
|
||||
} else {
|
||||
var c string
|
||||
if err := json.Unmarshal(v, &c); err != nil {
|
||||
return ErrInvalidReverseProperty
|
||||
}
|
||||
|
||||
if c != KeywordSet && c != KeywordIndex {
|
||||
return ErrInvalidReverseProperty
|
||||
}
|
||||
|
||||
termDef.Container = []string{c}
|
||||
}
|
||||
}
|
||||
|
||||
// 13.6)
|
||||
termDef.Reverse = true
|
||||
|
||||
// This whole step is missing in the spec but without
|
||||
// it t0131 can't pass. So. YOLO.
|
||||
if slices.Contains(termDef.Container, KeywordIndex) {
|
||||
idxVal, idxOK := valueObj[KeywordIndex]
|
||||
if idxOK && !json.IsNull(idxVal) {
|
||||
var idx string
|
||||
if err := json.Unmarshal(idxVal, &idx); err != nil {
|
||||
return err
|
||||
}
|
||||
termDef.Index = idx
|
||||
}
|
||||
}
|
||||
|
||||
// 13.7
|
||||
activeContext.defs[term] = termDef
|
||||
b := true
|
||||
defined[term] = &b
|
||||
return nil
|
||||
} else if idOK && term != idStr {
|
||||
// 14.1) 14.2)
|
||||
if idErr != nil {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
// 14.1)
|
||||
if !json.IsNull(id) {
|
||||
// 14.2)
|
||||
if !isKeyword(idStr) && looksLikeKeyword(idStr) {
|
||||
// 14.2.2)
|
||||
p.logger.Warn("keyword lookalike value encountered",
|
||||
slog.String("value", idStr))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 14.2.3)
|
||||
u, err := p.expandIRI(activeContext, idStr, false, true, localContext, defined)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isKeyword(u) && !url.IsIRI(u) && u != BlankNode {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
if u == KeywordContext {
|
||||
return ErrInvalidKeywordAlias
|
||||
}
|
||||
|
||||
termDef.IRI = u
|
||||
|
||||
// 14.2.4)
|
||||
if strings.Contains(term, "/") || (!strings.HasPrefix(term, ":") && !strings.HasSuffix(term, ":") && strings.Contains(term, ":")) {
|
||||
b := true
|
||||
// 14.2.4.1)
|
||||
defined[term] = &b
|
||||
|
||||
// 14.2.4.2)
|
||||
tu, err := p.expandIRI(activeContext, term, false, true, localContext, defined)
|
||||
if err != nil {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
if tu != u {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
} else {
|
||||
// 14.2.5)
|
||||
if simpleTerm && url.EndsInGenDelim(u) || u == BlankNode {
|
||||
if v, ok := p.remapPrefixIRIs[u]; ok {
|
||||
termDef.IRI = v
|
||||
}
|
||||
termDef.Prefix = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(term[1:], ":") {
|
||||
// 15)
|
||||
prefix, suffix, _ := strings.Cut(term, ":")
|
||||
|
||||
// 15.1)
|
||||
if !strings.HasPrefix(suffix, "//") {
|
||||
if _, ok := localContext[prefix]; ok {
|
||||
err := p.createTerm(activeContext, localContext, prefix, defined, newCreateTermOptions())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 15.2)
|
||||
if def, ok := activeContext.defs[prefix]; ok {
|
||||
termDef.IRI = def.IRI + suffix
|
||||
} else {
|
||||
// 15.3)
|
||||
termDef.IRI = term
|
||||
}
|
||||
} else if strings.Contains(term, "/") {
|
||||
// 16)
|
||||
// 16.2)
|
||||
u, err := p.expandIRI(activeContext, term, false, true, nil, nil)
|
||||
if err != nil {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
if !url.IsIRI(u) {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
termDef.IRI = u
|
||||
} else if term == KeywordType {
|
||||
// 17)
|
||||
termDef.IRI = KeywordType
|
||||
} else if activeContext.vocabMapping != "" {
|
||||
// 18)
|
||||
termDef.IRI = activeContext.vocabMapping + term
|
||||
} else {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
|
||||
// 19)
|
||||
if cnt, ok := valueObj[KeywordContainer]; ok {
|
||||
if json.IsNull(cnt) {
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
|
||||
// 19.2)
|
||||
// do this check early since we're going to rewrap
|
||||
// into an array
|
||||
if p.modeLD10 && !json.IsString(cnt) {
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
|
||||
cnt = json.MakeArray(cnt)
|
||||
|
||||
// 19.1)
|
||||
var values []string
|
||||
if err := json.Unmarshal(cnt, &values); err != nil {
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
|
||||
for _, vl := range values {
|
||||
switch vl {
|
||||
case KeywordGraph, KeywordID, KeywordIndex,
|
||||
KeywordLanguage, KeywordList, KeywordSet,
|
||||
KeywordType:
|
||||
default:
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
}
|
||||
|
||||
if slices.Contains(values, KeywordGraph) && (slices.Contains(values, KeywordID) || slices.Contains(values, KeywordIndex)) {
|
||||
kws := map[string]struct{}{}
|
||||
for _, vl := range values {
|
||||
kws[vl] = struct{}{}
|
||||
}
|
||||
delete(kws, KeywordGraph)
|
||||
delete(kws, KeywordIndex)
|
||||
delete(kws, KeywordID)
|
||||
if _, ok := kws[KeywordSet]; ok && len(kws) != 1 {
|
||||
return ErrInvalidIRIMapping
|
||||
}
|
||||
} else if slices.Contains(values, KeywordSet) {
|
||||
for _, vl := range values {
|
||||
switch vl {
|
||||
case KeywordGraph, KeywordID, KeywordIndex,
|
||||
KeywordLanguage, KeywordType,
|
||||
KeywordSet:
|
||||
default:
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 19.2)
|
||||
if p.modeLD10 {
|
||||
if len(values) > 1 {
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
switch values[0] {
|
||||
case KeywordID, KeywordGraph, KeywordType:
|
||||
return ErrInvalidContainerMapping
|
||||
}
|
||||
}
|
||||
|
||||
// 19.3)
|
||||
termDef.Container = values
|
||||
|
||||
// 19.4)
|
||||
if slices.Contains(values, KeywordType) {
|
||||
// 19.4.1)
|
||||
if termDef.Type == "" {
|
||||
termDef.Type = KeywordID
|
||||
}
|
||||
// 19.4.2)
|
||||
switch termDef.Type {
|
||||
case KeywordID, KeywordVocab, "":
|
||||
default:
|
||||
return ErrInvalidTypeMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 20)
|
||||
if idx, ok := valueObj[KeywordIndex]; ok {
|
||||
// 20.1)
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
if !slices.Contains(termDef.Container, KeywordIndex) {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 20.2)
|
||||
var s string
|
||||
if err := json.Unmarshal(idx, &s); err != nil {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
u, err := p.expandIRI(activeContext, s, false, true, localContext, defined)
|
||||
if err != nil {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
if !url.IsIRI(u) {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 20.3)
|
||||
termDef.Index = s
|
||||
}
|
||||
|
||||
// 21)
|
||||
if ctx, ok := valueObj[KeywordContext]; ok {
|
||||
// 21.1)
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 21.3)
|
||||
resolvOpts := newCtxProcessingOpts()
|
||||
resolvOpts.override = true
|
||||
resolvOpts.remotes = slices.Clone(opts.remotes)
|
||||
resolvOpts.validate = false
|
||||
_, err := p.context(
|
||||
activeContext,
|
||||
ctx,
|
||||
opts.baseURL,
|
||||
resolvOpts,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return ErrInvalidScopedContext
|
||||
}
|
||||
|
||||
// 21.4
|
||||
termDef.Context = ctx
|
||||
termDef.BaseIRI = opts.baseURL
|
||||
}
|
||||
|
||||
_, hasType := valueObj[KeywordType]
|
||||
|
||||
// 22)
|
||||
if lang, ok := valueObj[KeywordLanguage]; ok && !hasType {
|
||||
if json.IsNull(lang) {
|
||||
termDef.Language = KeywordNull
|
||||
} else {
|
||||
var lm string
|
||||
// 22.1)
|
||||
if err := json.Unmarshal(lang, &lm); err != nil {
|
||||
return ErrInvalidLanguageMapping
|
||||
}
|
||||
|
||||
// 22.2)
|
||||
termDef.Language = strings.ToLower(lm)
|
||||
}
|
||||
}
|
||||
|
||||
// 23)
|
||||
if dir, ok := valueObj[KeywordDirection]; ok && !hasType {
|
||||
if json.IsNull(dir) {
|
||||
termDef.Direction = KeywordNull
|
||||
} else {
|
||||
var d string
|
||||
// 23.1)
|
||||
if err := json.Unmarshal(dir, &d); err != nil {
|
||||
return ErrInvalidBaseDirection
|
||||
}
|
||||
|
||||
switch d {
|
||||
case DirectionLTR, DirectionRTL:
|
||||
default:
|
||||
return ErrInvalidBaseDirection
|
||||
}
|
||||
|
||||
// 23.2)
|
||||
termDef.Direction = d
|
||||
}
|
||||
}
|
||||
|
||||
// 24)
|
||||
if nest, ok := valueObj[KeywordNest]; ok {
|
||||
// 24.1)
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
if json.IsNull(nest) {
|
||||
return ErrInvalidNestValue
|
||||
}
|
||||
|
||||
// 24.2)
|
||||
var n string
|
||||
if err := json.Unmarshal(nest, &n); err != nil {
|
||||
return ErrInvalidNestValue
|
||||
}
|
||||
|
||||
if isKeyword(n) && n != KeywordNest {
|
||||
return ErrInvalidNestValue
|
||||
}
|
||||
termDef.Nest = n
|
||||
}
|
||||
|
||||
// 25)
|
||||
if prefix, ok := valueObj[KeywordPrefix]; ok {
|
||||
// 25.1)
|
||||
if p.modeLD10 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 25.2)
|
||||
if json.IsNull(prefix) {
|
||||
return ErrInvalidPrefixValue
|
||||
}
|
||||
|
||||
if strings.Contains(term, ":") || strings.Contains(term, "/") {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
var p bool
|
||||
if err := json.Unmarshal(prefix, &p); err != nil {
|
||||
return ErrInvalidPrefixValue
|
||||
}
|
||||
|
||||
// 25.3)
|
||||
if p && isKeyword(termDef.IRI) {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
termDef.Prefix = p
|
||||
}
|
||||
|
||||
// 26)
|
||||
valKeys := map[string]struct{}{}
|
||||
for k := range valueObj {
|
||||
valKeys[k] = struct{}{}
|
||||
}
|
||||
|
||||
for _, kw := range []string{KeywordID, KeywordReverse, KeywordContainer,
|
||||
KeywordContext, KeywordDirection, KeywordIndex, KeywordLanguage,
|
||||
KeywordNest, KeywordPrefix, KeywordProtected, KeywordType} {
|
||||
delete(valKeys, kw)
|
||||
}
|
||||
|
||||
if len(valKeys) > 0 {
|
||||
return ErrInvalidTermDefinition
|
||||
}
|
||||
|
||||
// 27)
|
||||
if oldDefOK && oldDef.Protected && !opts.override {
|
||||
// 27.1)
|
||||
if !oldDef.equalWithoutProtected(&termDef) {
|
||||
return ErrProtectedTermRedefinition
|
||||
}
|
||||
// 27.2)
|
||||
termDef = oldDef
|
||||
}
|
||||
|
||||
// 28)
|
||||
activeContext.defs[term] = termDef
|
||||
b := true
|
||||
defined[term] = &b
|
||||
return nil
|
||||
}
|
||||
|
||||
func selectTerm(
|
||||
activeContext *Context,
|
||||
keyIriVar string,
|
||||
containers []string,
|
||||
typeLanguage string,
|
||||
preferredValues []string,
|
||||
) string {
|
||||
// 1)
|
||||
if activeContext.inverse == nil {
|
||||
activeContext.inverse = workIt(activeContext)
|
||||
}
|
||||
|
||||
// 2)
|
||||
inverse := activeContext.inverse
|
||||
|
||||
// 3)
|
||||
containerMap := inverse[keyIriVar]
|
||||
|
||||
for _, container := range containers {
|
||||
// 4.1)
|
||||
// 4.2)
|
||||
typeLanguageMap, ok := containerMap[container]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 4.3)
|
||||
var valMap map[string]string
|
||||
switch typeLanguage {
|
||||
case KeywordLanguage:
|
||||
valMap = typeLanguageMap.Language
|
||||
case KeywordType:
|
||||
valMap = typeLanguageMap.Type
|
||||
case KeywordAny:
|
||||
valMap = typeLanguageMap.Any
|
||||
}
|
||||
|
||||
// 4.4)
|
||||
for _, pval := range preferredValues {
|
||||
if v, ok := valMap[pval]; !ok {
|
||||
// 4.4.1)
|
||||
continue
|
||||
} else {
|
||||
// 4.4.2)
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5)
|
||||
return ""
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue