1
1
mirror of https://github.com/nektos/act.git synced 2024-09-11 12:35:25 +03:00

support list/map/scalar for on and needs

Signed-off-by: Casey Lee <cplee@nektos.com>
This commit is contained in:
Casey Lee 2020-02-10 16:35:00 -08:00
parent 0582306861
commit ac8258db4b
No known key found for this signature in database
GPG Key ID: 1899120ECD0A1784
35 changed files with 2454 additions and 1696 deletions

3
go.mod
View File

@ -15,7 +15,6 @@ require (
github.com/gogo/protobuf v1.2.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/gorilla/mux v1.7.0 // indirect
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
@ -43,7 +42,7 @@ require (
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git-fixtures.v3 v3.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.9.1
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
gotest.tools v2.2.0+incompatible
)

6
go.sum
View File

@ -49,8 +49,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0=
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@ -175,8 +173,8 @@ gopkg.in/src-d/go-git.v4 v4.9.1 h1:0oKHJZY8tM7B71378cfTg2c5jmWyNlXvestTT6WfY+4=
gopkg.in/src-d/go-git.v4 v4.9.1/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -3,7 +3,7 @@ package model
import (
"io"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
// ActionRunsUsing is the type of runner for the action

View File

@ -88,8 +88,10 @@ type workflowPlanner struct {
func (wp *workflowPlanner) PlanEvent(eventName string) *Plan {
plan := new(Plan)
for _, w := range wp.workflows {
if w.On == eventName {
plan.mergeStages(createStages(w, w.GetJobIDs()...))
for _, e := range w.On() {
if e == eventName {
plan.mergeStages(createStages(w, w.GetJobIDs()...))
}
}
}
return plan
@ -110,14 +112,19 @@ func (wp *workflowPlanner) GetEvents() []string {
for _, w := range wp.workflows {
found := false
for _, e := range events {
if e == w.On {
found = true
for _, we := range w.On() {
if e == we {
found = true
break
}
}
if found {
break
}
}
if !found {
events = append(events, w.On)
events = append(events, w.On()...)
}
}
@ -163,8 +170,8 @@ func createStages(w *Workflow, jobIDs ...string) []*Stage {
// make sure we haven't visited this job yet
if _, ok := jobDependencies[jID]; !ok {
if job := w.GetJob(jID); job != nil {
jobDependencies[jID] = job.Needs
newJobIDs = append(newJobIDs, job.Needs...)
jobDependencies[jID] = job.Needs()
newJobIDs = append(newJobIDs, job.Needs()...)
}
}
}

View File

@ -6,21 +6,45 @@ import (
"regexp"
"strings"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
// Workflow is the structure of the files in .github/workflows
type Workflow struct {
Name string `yaml:"name"`
On string `yaml:"on"`
Env map[string]string `yaml:"env"`
Jobs map[string]*Job `yaml:"jobs"`
Name string `yaml:"name"`
RawOn yaml.Node `yaml:"on"`
Env map[string]string `yaml:"env"`
Jobs map[string]*Job `yaml:"jobs"`
}
// On events for the workflow
func (w *Workflow) On() []string {
switch w.RawOn.Kind {
case yaml.ScalarNode:
var val string
w.RawOn.Decode(&val)
return []string{val}
case yaml.SequenceNode:
var val []string
w.RawOn.Decode(&val)
return val
case yaml.MappingNode:
var val map[string]interface{}
w.RawOn.Decode(&val)
var keys []string
for k := range val {
keys = append(keys, k)
}
return keys
}
return nil
}
// Job is the structure of one job in a workflow
type Job struct {
Name string `yaml:"name"`
Needs []string `yaml:"needs"`
RawNeeds yaml.Node `yaml:"needs"`
RunsOn string `yaml:"runs-on"`
Env map[string]string `yaml:"env"`
If string `yaml:"if"`
@ -30,6 +54,22 @@ type Job struct {
Services map[string]*ContainerSpec `yaml:"services"`
}
// Needs list for Job
func (j *Job) Needs() []string {
switch j.RawNeeds.Kind {
case yaml.ScalarNode:
var val string
j.RawNeeds.Decode(&val)
return []string{val}
case yaml.SequenceNode:
var val []string
j.RawNeeds.Decode(&val)
return val
}
return nil
}
// ContainerSpec is the specification of the container to use for the job
type ContainerSpec struct {
Image string `yaml:"image"`

View File

@ -0,0 +1,68 @@
package model
import (
"strings"
"testing"
"gotest.tools/assert"
)
func TestReadWorkflow_StringEvent(t *testing.T) {
yaml := `
name: local-action-docker-url
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./actions/docker-url
`
workflow, err := ReadWorkflow(strings.NewReader(yaml))
assert.NilError(t, err, "read workflow should succeed")
assert.DeepEqual(t, workflow.On(), []string{"push"})
}
func TestReadWorkflow_ListEvent(t *testing.T) {
yaml := `
name: local-action-docker-url
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./actions/docker-url
`
workflow, err := ReadWorkflow(strings.NewReader(yaml))
assert.NilError(t, err, "read workflow should succeed")
assert.DeepEqual(t, workflow.On(), []string{"push", "pull_request"})
}
func TestReadWorkflow_MapEvent(t *testing.T) {
yaml := `
name: local-action-docker-url
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./actions/docker-url
`
workflow, err := ReadWorkflow(strings.NewReader(yaml))
assert.NilError(t, err, "read workflow should succeed")
assert.DeepEqual(t, workflow.On(), []string{"push", "pull_request"})
}

View File

@ -1,11 +0,0 @@
language: go
os:
- linux
- osx
go:
- 1.3
- 1.4
- 1.5
- tip

View File

@ -1,15 +0,0 @@
ISC License
Copyright (c) 2012 Chris Howey
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -1,384 +0,0 @@
Unless otherwise noted, all files in this distribution are released
under the Common Development and Distribution License (CDDL).
Exceptions are noted within the associated source files.
--------------------------------------------------------------------
COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0
1. Definitions.
1.1. "Contributor" means each individual or entity that creates
or contributes to the creation of Modifications.
1.2. "Contributor Version" means the combination of the Original
Software, prior Modifications used by a Contributor (if any),
and the Modifications made by that particular Contributor.
1.3. "Covered Software" means (a) the Original Software, or (b)
Modifications, or (c) the combination of files containing
Original Software with files containing Modifications, in
each case including portions thereof.
1.4. "Executable" means the Covered Software in any form other
than Source Code.
1.5. "Initial Developer" means the individual or entity that first
makes Original Software available under this License.
1.6. "Larger Work" means a work which combines Covered Software or
portions thereof with code not governed by the terms of this
License.
1.7. "License" means this document.
1.8. "Licensable" means having the right to grant, to the maximum
extent possible, whether at the time of the initial grant or
subsequently acquired, any and all of the rights conveyed
herein.
1.9. "Modifications" means the Source Code and Executable form of
any of the following:
A. Any file that results from an addition to, deletion from or
modification of the contents of a file containing Original
Software or previous Modifications;
B. Any new file that contains any part of the Original
Software or previous Modifications; or
C. Any new file that is contributed or otherwise made
available under the terms of this License.
1.10. "Original Software" means the Source Code and Executable
form of computer software code that is originally released
under this License.
1.11. "Patent Claims" means any patent claim(s), now owned or
hereafter acquired, including without limitation, method,
process, and apparatus claims, in any patent Licensable by
grantor.
1.12. "Source Code" means (a) the common form of computer software
code in which modifications are made and (b) associated
documentation included in or with such code.
1.13. "You" (or "Your") means an individual or a legal entity
exercising rights under, and complying with all of the terms
of, this License. For legal entities, "You" includes any
entity which 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.
2.1. The Initial Developer Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, the Initial
Developer hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or
trademark) Licensable by Initial Developer, to use,
reproduce, modify, display, perform, sublicense and
distribute the Original Software (or portions thereof),
with or without Modifications, and/or as part of a Larger
Work; and
(b) under Patent Claims infringed by the making, using or
selling of Original Software, to make, have made, use,
practice, sell, and offer for sale, and/or otherwise
dispose of the Original Software (or portions thereof).
(c) The licenses granted in Sections 2.1(a) and (b) are
effective on the date Initial Developer first distributes
or otherwise makes the Original Software available to a
third party under the terms of this License.
(d) Notwithstanding Section 2.1(b) above, no patent license is
granted: (1) for code that You delete from the Original
Software, or (2) for infringements caused by: (i) the
modification of the Original Software, or (ii) the
combination of the Original Software with other software
or devices.
2.2. Contributor Grant.
Conditioned upon Your compliance with Section 3.1 below and
subject to third party intellectual property claims, 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 Contributor to use, reproduce,
modify, display, perform, sublicense and distribute the
Modifications created by such Contributor (or portions
thereof), either on an unmodified basis, with other
Modifications, as Covered Software and/or as part of a
Larger Work; and
(b) under Patent Claims infringed by the making, using, or
selling of Modifications made by that Contributor either
alone and/or in combination with its Contributor Version
(or portions of such combination), to make, use, sell,
offer for sale, have made, and/or otherwise dispose of:
(1) Modifications made by that Contributor (or portions
thereof); and (2) the combination of Modifications made by
that Contributor with its Contributor Version (or portions
of such combination).
(c) The licenses granted in Sections 2.2(a) and 2.2(b) are
effective on the date Contributor first distributes or
otherwise makes the Modifications available to a third
party.
(d) Notwithstanding Section 2.2(b) above, no patent license is
granted: (1) for any code that Contributor has deleted
from the Contributor Version; (2) for infringements caused
by: (i) third party modifications of Contributor Version,
or (ii) the combination of Modifications made by that
Contributor with other software (except as part of the
Contributor Version) or other devices; or (3) under Patent
Claims infringed by Covered Software in the absence of
Modifications made by that Contributor.
3. Distribution Obligations.
3.1. Availability of Source Code.
Any Covered Software that You distribute or otherwise make
available in Executable form must also be made available in Source
Code form and that Source Code form must be distributed only under
the terms of this License. You must include a copy of this
License with every copy of the Source Code form of the Covered
Software You distribute or otherwise make available. You must
inform recipients of any such Covered Software in Executable form
as to how they can obtain such Covered Software in Source Code
form in a reasonable manner on or through a medium customarily
used for software exchange.
3.2. Modifications.
The Modifications that You create or to which You contribute are
governed by the terms of this License. You represent that You
believe Your Modifications are Your original creation(s) and/or
You have sufficient rights to grant the rights conveyed by this
License.
3.3. Required Notices.
You must include a notice in each of Your Modifications that
identifies You as the Contributor of the Modification. You may
not remove or alter any copyright, patent or trademark notices
contained within the Covered Software, or any notices of licensing
or any descriptive text giving attribution to any Contributor or
the Initial Developer.
3.4. Application of Additional Terms.
You may not offer or impose any terms on any Covered Software in
Source Code form that alters or restricts the applicable version
of this License or the recipients' rights hereunder. 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 the Initial Developer or 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 the Initial Developer and every
Contributor for any liability incurred by the Initial Developer or
such Contributor as a result of warranty, support, indemnity or
liability terms You offer.
3.5. Distribution of Executable Versions.
You may distribute the Executable form of the Covered Software
under the terms of this License or under the terms of a license of
Your choice, which may contain terms different from this License,
provided that You are in compliance with the terms of this License
and that the license for the Executable form does not attempt to
limit or alter the recipient's rights in the Source Code form from
the rights set forth in this License. If You distribute the
Covered Software in Executable form under a different license, You
must make it absolutely clear that any terms which differ from
this License are offered by You alone, not by the Initial
Developer or Contributor. You hereby agree to indemnify the
Initial Developer and every Contributor for any liability incurred
by the Initial Developer or such Contributor as a result of any
such terms You offer.
3.6. Larger Works.
You may create a Larger Work by combining Covered Software with
other code not governed by the terms of this License and
distribute the Larger Work as a single product. In such a case,
You must make sure the requirements of this License are fulfilled
for the Covered Software.
4. Versions of the License.
4.1. New Versions.
Sun Microsystems, Inc. is the initial license steward and may
publish revised and/or new versions of this License from time to
time. Each version will be given a distinguishing version number.
Except as provided in Section 4.3, no one other than the license
steward has the right to modify this License.
4.2. Effect of New Versions.
You may always continue to use, distribute or otherwise make the
Covered Software available under the terms of the version of the
License under which You originally received the Covered Software.
If the Initial Developer includes a notice in the Original
Software prohibiting it from being distributed or otherwise made
available under any subsequent version of the License, You must
distribute and make the Covered Software available under the terms
of the version of the License under which You originally received
the Covered Software. Otherwise, You may also choose to use,
distribute or otherwise make the Covered Software available under
the terms of any subsequent version of the License published by
the license steward.
4.3. Modified Versions.
When You are an Initial Developer and You want to create a new
license for Your Original Software, You may create and use a
modified version of this License if You: (a) rename the license
and remove any references to the name of the license steward
(except to note that the license differs from this License); and
(b) otherwise make it clear that the license contains terms which
differ from this License.
5. DISCLAIMER OF WARRANTY.
COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
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 THE
INITIAL DEVELOPER OR ANY OTHER 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 HEREUNDER EXCEPT UNDER THIS
DISCLAIMER.
6. TERMINATION.
6.1. This License and the rights granted hereunder will terminate
automatically if You fail to comply with terms herein and fail to
cure such breach within 30 days of becoming aware of the breach.
Provisions which, by their nature, must remain in effect beyond
the termination of this License shall survive.
6.2. If You assert a patent infringement claim (excluding
declaratory judgment actions) against Initial Developer or a
Contributor (the Initial Developer or Contributor against whom You
assert such claim is referred to as "Participant") alleging that
the Participant Software (meaning the Contributor Version where
the Participant is a Contributor or the Original Software where
the Participant is the Initial Developer) directly or indirectly
infringes any patent, then any and all rights granted directly or
indirectly to You by such Participant, the Initial Developer (if
the Initial Developer is not the Participant) and all Contributors
under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
notice from Participant terminate prospectively and automatically
at the expiration of such 60 day notice period, unless if within
such 60 day period You withdraw Your claim with respect to the
Participant Software against such Participant either unilaterally
or pursuant to a written agreement with Participant.
6.3. In the event of termination under Sections 6.1 or 6.2 above,
all end user licenses that have been validly granted by You or any
distributor hereunder prior to termination (excluding licenses
granted to You by any distributor) shall survive termination.
7. LIMITATION OF LIABILITY.
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
(INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
LIABLE TO ANY PERSON FOR ANY 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. U.S. GOVERNMENT END USERS.
The Covered Software is a "commercial item," as that term is
defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
computer software" (as that term is defined at 48
C.F.R. 252.227-7014(a)(1)) and "commercial computer software
documentation" as such terms are used in 48 C.F.R. 12.212
(Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
U.S. Government End Users acquire Covered Software with only those
rights set forth herein. This U.S. Government Rights clause is in
lieu of, and supersedes, any other FAR, DFAR, or other clause or
provision that addresses Government rights in computer software
under this License.
9. MISCELLANEOUS.
This License represents the complete agreement concerning 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. This License shall be governed
by the law of the jurisdiction specified in a notice contained
within the Original Software (except to the extent applicable law,
if any, provides otherwise), excluding such jurisdiction's
conflict-of-law provisions. Any litigation relating to this
License shall be subject to the jurisdiction of the courts located
in the jurisdiction and venue specified in a notice contained
within the Original Software, with the losing party responsible
for costs, including, without limitation, court costs and
reasonable attorneys' fees and expenses. The application of the
United Nations Convention on Contracts for the International Sale
of Goods is expressly excluded. Any law or regulation which
provides that the language of a contract shall be construed
against the drafter shall not apply to this License. You agree
that You alone are responsible for compliance with the United
States export administration regulations (and the export control
laws and regulation of any other countries) when You use,
distribute or otherwise make available any Covered Software.
10. RESPONSIBILITY FOR CLAIMS.
As between Initial Developer and the Contributors, each party is
responsible for claims and damages arising, directly or
indirectly, out of its utilization of rights under this License
and You agree to work with Initial Developer and Contributors to
distribute such responsibility on an equitable basis. Nothing
herein is intended or shall be deemed to constitute any admission
of liability.
--------------------------------------------------------------------
NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
DISTRIBUTION LICENSE (CDDL)
For Covered Software in this distribution, this License shall
be governed by the laws of the State of California (excluding
conflict-of-law provisions).
Any litigation relating to this License shall be subject to the
jurisdiction of the Federal Courts of the Northern District of
California and the state courts of the State of California, with
venue lying in Santa Clara County, California.

View File

@ -1,27 +0,0 @@
# getpasswd in Go [![GoDoc](https://godoc.org/github.com/howeyc/gopass?status.svg)](https://godoc.org/github.com/howeyc/gopass) [![Build Status](https://secure.travis-ci.org/howeyc/gopass.png?branch=master)](http://travis-ci.org/howeyc/gopass)
Retrieve password from user terminal or piped input without echo.
Verified on BSD, Linux, and Windows.
Example:
```go
package main
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
// Silent. For printing *'s use gopass.GetPasswdMasked()
pass, err := gopass.GetPasswd()
if err != nil {
// Handle gopass.ErrInterrupted or getch() read error
}
// Do something with pass
}
```
Caution: Multi-byte characters not supported!

View File

@ -1,110 +0,0 @@
package gopass
import (
"errors"
"fmt"
"io"
"os"
)
type FdReader interface {
io.Reader
Fd() uintptr
}
var defaultGetCh = func(r io.Reader) (byte, error) {
buf := make([]byte, 1)
if n, err := r.Read(buf); n == 0 || err != nil {
if err != nil {
return 0, err
}
return 0, io.EOF
}
return buf[0], nil
}
var (
maxLength = 512
ErrInterrupted = errors.New("interrupted")
ErrMaxLengthExceeded = fmt.Errorf("maximum byte limit (%v) exceeded", maxLength)
// Provide variable so that tests can provide a mock implementation.
getch = defaultGetCh
)
// getPasswd returns the input read from terminal.
// If prompt is not empty, it will be output as a prompt to the user
// If masked is true, typing will be matched by asterisks on the screen.
// Otherwise, typing will echo nothing.
func getPasswd(prompt string, masked bool, r FdReader, w io.Writer) ([]byte, error) {
var err error
var pass, bs, mask []byte
if masked {
bs = []byte("\b \b")
mask = []byte("*")
}
if isTerminal(r.Fd()) {
if oldState, err := makeRaw(r.Fd()); err != nil {
return pass, err
} else {
defer func() {
restore(r.Fd(), oldState)
fmt.Fprintln(w)
}()
}
}
if prompt != "" {
fmt.Fprint(w, prompt)
}
// Track total bytes read, not just bytes in the password. This ensures any
// errors that might flood the console with nil or -1 bytes infinitely are
// capped.
var counter int
for counter = 0; counter <= maxLength; counter++ {
if v, e := getch(r); e != nil {
err = e
break
} else if v == 127 || v == 8 {
if l := len(pass); l > 0 {
pass = pass[:l-1]
fmt.Fprint(w, string(bs))
}
} else if v == 13 || v == 10 {
break
} else if v == 3 {
err = ErrInterrupted
break
} else if v != 0 {
pass = append(pass, v)
fmt.Fprint(w, string(mask))
}
}
if counter > maxLength {
err = ErrMaxLengthExceeded
}
return pass, err
}
// GetPasswd returns the password read from the terminal without echoing input.
// The returned byte array does not include end-of-line characters.
func GetPasswd() ([]byte, error) {
return getPasswd("", false, os.Stdin, os.Stdout)
}
// GetPasswdMasked returns the password read from the terminal, echoing asterisks.
// The returned byte array does not include end-of-line characters.
func GetPasswdMasked() ([]byte, error) {
return getPasswd("", true, os.Stdin, os.Stdout)
}
// GetPasswdPrompt prompts the user and returns the password read from the terminal.
// If mask is true, then asterisks are echoed.
// The returned byte array does not include end-of-line characters.
func GetPasswdPrompt(prompt string, mask bool, r FdReader, w io.Writer) ([]byte, error) {
return getPasswd(prompt, mask, r, w)
}

View File

@ -1,25 +0,0 @@
// +build !solaris
package gopass
import "golang.org/x/crypto/ssh/terminal"
type terminalState struct {
state *terminal.State
}
func isTerminal(fd uintptr) bool {
return terminal.IsTerminal(int(fd))
}
func makeRaw(fd uintptr) (*terminalState, error) {
state, err := terminal.MakeRaw(int(fd))
return &terminalState{
state: state,
}, err
}
func restore(fd uintptr, oldState *terminalState) error {
return terminal.Restore(int(fd), oldState.state)
}

View File

@ -1,69 +0,0 @@
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
// Below is derived from Solaris source, so CDDL license is included.
package gopass
import (
"syscall"
"golang.org/x/sys/unix"
)
type terminalState struct {
state *unix.Termios
}
// isTerminal returns true if there is a terminal attached to the given
// file descriptor.
// Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
func isTerminal(fd uintptr) bool {
var termio unix.Termio
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
return err == nil
}
// makeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
// Source: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
func makeRaw(fd uintptr) (*terminalState, error) {
oldTermiosPtr, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
if err != nil {
return nil, err
}
oldTermios := *oldTermiosPtr
newTermios := oldTermios
newTermios.Lflag &^= syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL
if err := unix.IoctlSetTermios(int(fd), unix.TCSETS, &newTermios); err != nil {
return nil, err
}
return &terminalState{
state: oldTermiosPtr,
}, nil
}
func restore(fd uintptr, oldState *terminalState) error {
return unix.IoctlSetTermios(int(fd), unix.TCSETS, oldState.state)
}

201
vendor/gopkg.in/yaml.v2/LICENSE generated vendored
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

390
vendor/gopkg.in/yaml.v2/encode.go generated vendored
View File

@ -1,390 +0,0 @@
package yaml
import (
"encoding"
"fmt"
"io"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)
// jsonNumber is the interface of the encoding/json.Number datatype.
// Repeating the interface here avoids a dependency on encoding/json, and also
// supports other libraries like jsoniter, which use a similar datatype with
// the same interface. Detecting this interface is useful when dealing with
// structures containing json.Number, which is a string under the hood. The
// encoder should prefer the use of Int64(), Float64() and string(), in that
// order, when encoding this type.
type jsonNumber interface {
Float64() (float64, error)
Int64() (int64, error)
String() string
}
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
// doneInit holds whether the initial stream_start_event has been
// emitted.
doneInit bool
}
func newEncoder() *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func newEncoderWithWriter(w io.Writer) *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_writer(&e.emitter, w)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func (e *encoder) init() {
if e.doneInit {
return
}
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
e.emit()
e.doneInit = true
}
func (e *encoder) finish() {
e.emitter.open_ended = false
yaml_stream_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
e.must(yaml_emitter_emit(&e.emitter, &e.event))
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
e.init()
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.emit()
e.marshal(tag, in)
yaml_document_end_event_initialize(&e.event, true)
e.emit()
}
func (e *encoder) marshal(tag string, in reflect.Value) {
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}
iface := in.Interface()
switch m := iface.(type) {
case jsonNumber:
integer, err := m.Int64()
if err == nil {
// In this case the json.Number is a valid int64
in = reflect.ValueOf(integer)
break
}
float, err := m.Float64()
if err == nil {
// In this case the json.Number is a valid float64
in = reflect.ValueOf(float)
break
}
// fallback case - no number could be obtained
in = reflect.ValueOf(m.String())
case time.Time, *time.Time:
// Although time.Time implements TextMarshaler,
// we don't want to treat it as a string for YAML
// purposes because YAML has special support for
// timestamps.
case Marshaler:
v, err := m.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
in = reflect.ValueOf(v)
case encoding.TextMarshaler:
text, err := m.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
case nil:
e.nilv()
return
}
switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
if in.Type() == ptrTimeType {
e.timev(tag, in.Elem())
} else {
e.marshal(tag, in.Elem())
}
case reflect.Struct:
if in.Type() == timeType {
e.timev(tag, in)
} else {
e.structv(tag, in)
}
case reflect.Slice, reflect.Array:
if in.Type().Elem() == mapItemType {
e.itemsv(tag, in)
} else {
e.slicev(tag, in)
}
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if in.Type() == durationType {
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
} else {
e.intv(tag, in)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) itemsv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
for _, item := range slice {
e.marshal("", reflect.ValueOf(item.Key))
e.marshal("", reflect.ValueOf(item.Value))
}
})
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = in.FieldByIndex(info.Inline)
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
e.emit()
f()
yaml_mapping_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
canUsePlain := true
switch {
case !utf8.ValidString(s):
if tag == yaml_BINARY_TAG {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = yaml_BINARY_TAG
s = encodeBase64(s)
case tag == "":
// Check to see if it would resolve to a specific
// tag when encoded unquoted. If it doesn't,
// there's no need to quote it.
rtag, _ := resolve("", s)
canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
}
// Note: it's possible for user code to emit invalid YAML
// if they explicitly specify a tag and a string containing
// text that's incompatible with that tag.
switch {
case strings.Contains(s, "\n"):
style = yaml_LITERAL_SCALAR_STYLE
case canUsePlain:
style = yaml_PLAIN_SCALAR_STYLE
default:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
precision := 64
if in.Kind() == reflect.Float32 {
precision = 32
}
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
implicit := tag == ""
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.emit()
}

26
vendor/gopkg.in/yaml.v2/writerc.go generated vendored
View File

@ -1,26 +0,0 @@
package yaml
// Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_WRITER_ERROR
emitter.problem = problem
return false
}
// Flush the output buffer.
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
if emitter.write_handler == nil {
panic("write handler not set")
}
// Check if the buffer is empty.
if emitter.buffer_pos == 0 {
return true
}
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
return true
}

View File

@ -13,4 +13,4 @@ go:
- "1.13.x"
- "tip"
go_import_path: gopkg.in/yaml.v2
go_import_path: gopkg.in/yaml.v3

View File

@ -1,16 +1,17 @@
This project is covered by two different licenses: MIT and Apache.
#### MIT License ####
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original copyright and license:
are still covered by their original MIT license, with the additional
copyright staring in 2011 when the project was ported over:
apic.go
emitterc.go
parserc.go
readerc.go
scannerc.go
writerc.go
yamlh.go
yamlprivateh.go
apic.go emitterc.go parserc.go readerc.go scannerc.go
writerc.go yamlh.go yamlprivateh.go
Copyright (c) 2006 Kirill Simonov
Copyright (c) 2006-2010 Kirill Simonov
Copyright (c) 2006-2011 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@ -29,3 +30,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
### Apache License ###
All the remaining project files are covered by the Apache license:
Copyright (c) 2011-2019 Canonical Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -12,7 +12,23 @@ C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package supports most of YAML 1.1 and 1.2, including support for
The yaml package supports most of YAML 1.2, but preserves some behavior
from 1.1 for backwards compatibility.
Specifically, as of v3 of the yaml package:
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being
decoded into a typed bool value. Otherwise they behave as a string. Booleans
in YAML 1.2 are _true/false_ only.
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_
as specified in YAML 1.2, because most parsers still use the old format.
Octals in the _0o777_ format are supported though, so new files work.
- Does not support base-60 floats. These are gone from YAML 1.2, and were
actually never supported by this package as it's clearly a poor choice.
and offers backwards
compatibility with YAML 1.1 in some cases.
1.2, including support for
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
implemented, and base-60 floats from YAML 1.1 are purposefully not
supported since they're a poor design and are gone in YAML 1.2.
@ -20,29 +36,30 @@ supported since they're a poor design and are gone in YAML 1.2.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v2*.
The import path for the package is *gopkg.in/yaml.v3*.
To install it, run:
go get gopkg.in/yaml.v2
go get gopkg.in/yaml.v3
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3)
API stability
-------------
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details.
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.
Example
@ -55,7 +72,7 @@ import (
"fmt"
"log"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
var data = `

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -138,7 +160,7 @@ func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
emitter.canonical = canonical
}
//// Set the indentation increment.
// Set the indentation increment.
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
if indent < 2 || indent > 9 {
indent = 2
@ -288,29 +310,14 @@ func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
}
}
///*
// * Create ALIAS.
// */
//
//YAML_DECLARE(int)
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
//{
// mark yaml_mark_t = { 0, 0, 0 }
// anchor_copy *yaml_char_t = NULL
//
// assert(event) // Non-NULL event object is expected.
// assert(anchor) // Non-NULL anchor is expected.
//
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
//
// anchor_copy = yaml_strdup(anchor)
// if (!anchor_copy)
// return 0
//
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
//
// return 1
//}
// Create ALIAS.
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
*event = yaml_event_t{
typ: yaml_ALIAS_EVENT,
anchor: anchor,
}
return true
}
// Create SCALAR.
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -11,33 +26,14 @@ import (
"time"
)
const (
documentNode = 1 << iota
mappingNode
sequenceNode
scalarNode
aliasNode
)
type node struct {
kind int
line, column int
tag string
// For an alias node, alias holds the resolved alias.
alias *node
value string
implicit bool
children []*node
anchors map[string]*node
}
// ----------------------------------------------------------------------------
// Parser, produces a node tree out of a libyaml event stream.
type parser struct {
parser yaml_parser_t
event yaml_event_t
doc *node
doc *Node
anchors map[string]*Node
doneInit bool
}
@ -66,6 +62,7 @@ func (p *parser) init() {
if p.doneInit {
return
}
p.anchors = make(map[string]*Node)
p.expect(yaml_STREAM_START_EVENT)
p.doneInit = true
}
@ -132,13 +129,14 @@ func (p *parser) fail() {
failf("%s%s", where, msg)
}
func (p *parser) anchor(n *node, anchor []byte) {
func (p *parser) anchor(n *Node, anchor []byte) {
if anchor != nil {
p.doc.anchors[string(anchor)] = n
n.Anchor = string(anchor)
p.anchors[n.Anchor] = n
}
}
func (p *parser) parse() *node {
func (p *parser) parse() *Node {
p.init()
switch p.peek() {
case yaml_SCALAR_EVENT:
@ -154,67 +152,145 @@ func (p *parser) parse() *node {
case yaml_STREAM_END_EVENT:
// Happens when attempting to decode an empty buffer.
return nil
case yaml_TAIL_COMMENT_EVENT:
panic("internal error: unexpected tail comment event (please report)")
default:
panic("attempted to parse unknown event: " + p.event.typ.String())
panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String())
}
}
func (p *parser) node(kind int) *node {
return &node{
kind: kind,
line: p.event.start_mark.line,
column: p.event.start_mark.column,
func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
var style Style
if tag != "" && tag != "!" {
tag = shortTag(tag)
style = TaggedStyle
} else if defaultTag != "" {
tag = defaultTag
} else if kind == ScalarNode {
tag, _ = resolve("", value)
}
return &Node{
Kind: kind,
Tag: tag,
Value: value,
Style: style,
Line: p.event.start_mark.line + 1,
Column: p.event.start_mark.column + 1,
HeadComment: string(p.event.head_comment),
LineComment: string(p.event.line_comment),
FootComment: string(p.event.foot_comment),
}
}
func (p *parser) document() *node {
n := p.node(documentNode)
n.anchors = make(map[string]*node)
func (p *parser) parseChild(parent *Node) *Node {
child := p.parse()
parent.Content = append(parent.Content, child)
return child
}
func (p *parser) document() *Node {
n := p.node(DocumentNode, "", "", "")
p.doc = n
p.expect(yaml_DOCUMENT_START_EVENT)
n.children = append(n.children, p.parse())
p.parseChild(n)
if p.peek() == yaml_DOCUMENT_END_EVENT {
n.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_DOCUMENT_END_EVENT)
return n
}
func (p *parser) alias() *node {
n := p.node(aliasNode)
n.value = string(p.event.anchor)
n.alias = p.doc.anchors[n.value]
if n.alias == nil {
failf("unknown anchor '%s' referenced", n.value)
func (p *parser) alias() *Node {
n := p.node(AliasNode, "", "", string(p.event.anchor))
n.Alias = p.anchors[n.Value]
if n.Alias == nil {
failf("unknown anchor '%s' referenced", n.Value)
}
p.expect(yaml_ALIAS_EVENT)
return n
}
func (p *parser) scalar() *node {
n := p.node(scalarNode)
n.value = string(p.event.value)
n.tag = string(p.event.tag)
n.implicit = p.event.implicit
func (p *parser) scalar() *Node {
var parsedStyle = p.event.scalar_style()
var nodeStyle Style
switch {
case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = DoubleQuotedStyle
case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = SingleQuotedStyle
case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0:
nodeStyle = LiteralStyle
case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0:
nodeStyle = FoldedStyle
}
var nodeValue = string(p.event.value)
var nodeTag = string(p.event.tag)
var defaultTag string
if nodeStyle == 0 {
if nodeValue == "<<" {
defaultTag = mergeTag
}
} else {
defaultTag = strTag
}
n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue)
n.Style |= nodeStyle
p.anchor(n, p.event.anchor)
p.expect(yaml_SCALAR_EVENT)
return n
}
func (p *parser) sequence() *node {
n := p.node(sequenceNode)
func (p *parser) sequence() *Node {
n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_SEQUENCE_START_EVENT)
for p.peek() != yaml_SEQUENCE_END_EVENT {
n.children = append(n.children, p.parse())
p.parseChild(n)
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
p.expect(yaml_SEQUENCE_END_EVENT)
return n
}
func (p *parser) mapping() *node {
n := p.node(mappingNode)
func (p *parser) mapping() *Node {
n := p.node(MappingNode, mapTag, string(p.event.tag), "")
block := true
if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
block = false
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_MAPPING_START_EVENT)
for p.peek() != yaml_MAPPING_END_EVENT {
n.children = append(n.children, p.parse(), p.parse())
k := p.parseChild(n)
if block && k.FootComment != "" {
// Must be a foot comment for the prior value when being dedented.
if len(n.Content) > 2 {
n.Content[len(n.Content)-3].FootComment = k.FootComment
k.FootComment = ""
}
}
v := p.parseChild(n)
if k.FootComment == "" && v.FootComment != "" {
k.FootComment = v.FootComment
v.FootComment = ""
}
if p.peek() == yaml_TAIL_COMMENT_EVENT {
if k.FootComment == "" {
k.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_TAIL_COMMENT_EVENT)
}
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 {
n.Content[len(n.Content)-2].FootComment = n.FootComment
n.FootComment = ""
}
p.expect(yaml_MAPPING_END_EVENT)
return n
@ -224,48 +300,68 @@ func (p *parser) mapping() *node {
// Decoder, unmarshals a node into a provided value.
type decoder struct {
doc *node
aliases map[*node]bool
mapType reflect.Type
doc *Node
aliases map[*Node]bool
terrors []string
strict bool
stringMapType reflect.Type
generalMapType reflect.Type
knownFields bool
uniqueKeys bool
decodeCount int
aliasCount int
aliasDepth int
}
var (
mapItemType = reflect.TypeOf(MapItem{})
nodeType = reflect.TypeOf(Node{})
durationType = reflect.TypeOf(time.Duration(0))
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = defaultMapType.Elem()
stringMapType = reflect.TypeOf(map[string]interface{}{})
generalMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = generalMapType.Elem()
timeType = reflect.TypeOf(time.Time{})
ptrTimeType = reflect.TypeOf(&time.Time{})
)
func newDecoder(strict bool) *decoder {
d := &decoder{mapType: defaultMapType, strict: strict}
d.aliases = make(map[*node]bool)
func newDecoder() *decoder {
d := &decoder{
stringMapType: stringMapType,
generalMapType: generalMapType,
uniqueKeys: true,
}
d.aliases = make(map[*Node]bool)
return d
}
func (d *decoder) terror(n *node, tag string, out reflect.Value) {
if n.tag != "" {
tag = n.tag
func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
if n.Tag != "" {
tag = n.Tag
}
value := n.value
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
value := n.Value
if tag != seqTag && tag != mapTag {
if len(value) > 10 {
value = " `" + value[:7] + "...`"
} else {
value = " `" + value + "`"
}
}
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type()))
}
func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
err := u.UnmarshalYAML(n)
if e, ok := err.(*TypeError); ok {
d.terrors = append(d.terrors, e.Errors...)
return false
}
if err != nil {
fail(err)
}
return true
}
func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
terrlen := len(d.terrors)
err := u.UnmarshalYAML(func(v interface{}) (err error) {
defer handleErr(&err)
@ -294,8 +390,8 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
// its types unmarshalled appropriately.
//
// If n holds a null value, prepare returns before doing anything.
func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) {
func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.ShortTag() == nullTag {
return out, false, false
}
again := true
@ -309,15 +405,40 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm
again = true
}
if out.CanAddr() {
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
outi := out.Addr().Interface()
if u, ok := outi.(Unmarshaler); ok {
good = d.callUnmarshaler(n, u)
return out, true, good
}
if u, ok := outi.(obsoleteUnmarshaler); ok {
good = d.callObsoleteUnmarshaler(n, u)
return out, true, good
}
}
}
return out, false, false
}
func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) {
if n.ShortTag() == nullTag {
return reflect.Value{}
}
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
const (
// 400,000 decode operations is ~500kb of dense object declarations, or
// ~5kb of dense object declarations with 10000% alias expansion
@ -347,7 +468,7 @@ func allowedAliasRatio(decodeCount int) float64 {
}
}
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
d.decodeCount++
if d.aliasDepth > 0 {
d.aliasCount++
@ -355,46 +476,50 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
failf("document contains excessive aliasing")
}
switch n.kind {
case documentNode:
if out.Type() == nodeType {
out.Set(reflect.ValueOf(n).Elem())
return true
}
switch n.Kind {
case DocumentNode:
return d.document(n, out)
case aliasNode:
case AliasNode:
return d.alias(n, out)
}
out, unmarshaled, good := d.prepare(n, out)
if unmarshaled {
return good
}
switch n.kind {
case scalarNode:
switch n.Kind {
case ScalarNode:
good = d.scalar(n, out)
case mappingNode:
case MappingNode:
good = d.mapping(n, out)
case sequenceNode:
case SequenceNode:
good = d.sequence(n, out)
default:
panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind)))
}
return good
}
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
if len(n.children) == 1 {
func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
if len(n.Content) == 1 {
d.doc = n
d.unmarshal(n.children[0], out)
d.unmarshal(n.Content[0], out)
return true
}
return false
}
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
if d.aliases[n] {
// TODO this could actually be allowed in some circumstances.
failf("anchor '%s' value contains itself", n.value)
failf("anchor '%s' value contains itself", n.Value)
}
d.aliases[n] = true
d.aliasDepth++
good = d.unmarshal(n.alias, out)
good = d.unmarshal(n.Alias, out)
d.aliasDepth--
delete(d.aliases, n)
return good
@ -408,15 +533,15 @@ func resetMap(out reflect.Value) {
}
}
func (d *decoder) scalar(n *node, out reflect.Value) bool {
func (d *decoder) scalar(n *Node, out reflect.Value) bool {
var tag string
var resolved interface{}
if n.tag == "" && !n.implicit {
tag = yaml_STR_TAG
resolved = n.value
if n.indicatedString() {
tag = strTag
resolved = n.Value
} else {
tag, resolved = resolve(n.tag, n.value)
if tag == yaml_BINARY_TAG {
tag, resolved = resolve(n.Tag, n.Value)
if tag == binaryTag {
data, err := base64.StdEncoding.DecodeString(resolved.(string))
if err != nil {
failf("!!binary value contains invalid base64 data")
@ -425,12 +550,14 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
}
}
if resolved == nil {
if out.Kind() == reflect.Map && !out.CanAddr() {
resetMap(out)
} else {
out.Set(reflect.Zero(out.Type()))
if out.CanAddr() {
switch out.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
out.Set(reflect.Zero(out.Type()))
return true
}
}
return true
return false
}
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
// We've resolved to exactly the type we want, so use that.
@ -443,13 +570,13 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
if ok {
var text []byte
if tag == yaml_BINARY_TAG {
if tag == binaryTag {
text = []byte(resolved.(string))
} else {
// We let any value be unmarshaled into TextUnmarshaler.
// That might be more lax than we'd like, but the
// TextUnmarshaler itself should bowl out any dubious values.
text = []byte(n.value)
text = []byte(n.Value)
}
err := u.UnmarshalText(text)
if err != nil {
@ -460,47 +587,37 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
}
switch out.Kind() {
case reflect.String:
if tag == yaml_BINARY_TAG {
if tag == binaryTag {
out.SetString(resolved.(string))
return true
}
if resolved != nil {
out.SetString(n.value)
return true
}
out.SetString(n.Value)
return true
case reflect.Interface:
if resolved == nil {
out.Set(reflect.Zero(out.Type()))
} else if tag == yaml_TIMESTAMP_TAG {
// It looks like a timestamp but for backward compatibility
// reasons we set it as a string, so that code that unmarshals
// timestamp-like values into interface{} will continue to
// see a string and not a time.Time.
// TODO(v3) Drop this.
out.Set(reflect.ValueOf(n.value))
} else {
out.Set(reflect.ValueOf(resolved))
}
out.Set(reflect.ValueOf(resolved))
return true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// This used to work in v2, but it's very unfriendly.
isDuration := out.Type() == durationType
switch resolved := resolved.(type) {
case int:
if !out.OverflowInt(int64(resolved)) {
if !isDuration && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case int64:
if !out.OverflowInt(resolved) {
if !isDuration && !out.OverflowInt(resolved) {
out.SetInt(resolved)
return true
}
case uint64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case float64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
@ -541,6 +658,17 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
case bool:
out.SetBool(resolved)
return true
case string:
// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html).
// It only works if explicitly attempting to unmarshal into a typed bool value.
switch resolved {
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON":
out.SetBool(true)
return true
case "n", "N", "no", "No", "NO", "off", "Off", "OFF":
out.SetBool(false)
return true
}
}
case reflect.Float32, reflect.Float64:
switch resolved := resolved.(type) {
@ -563,13 +691,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
return true
}
case reflect.Ptr:
if out.Type().Elem() == reflect.TypeOf(resolved) {
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
elem := reflect.New(out.Type().Elem())
elem.Elem().Set(reflect.ValueOf(resolved))
out.Set(elem)
return true
}
panic("yaml internal error: please report the issue")
}
d.terror(n, tag, out)
return false
@ -582,8 +704,8 @@ func settableValueOf(i interface{}) reflect.Value {
return sv
}
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
l := len(n.children)
func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
var iface reflect.Value
switch out.Kind() {
@ -598,7 +720,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
iface = out
out = settableValueOf(make([]interface{}, l))
default:
d.terror(n, yaml_SEQ_TAG, out)
d.terror(n, seqTag, out)
return false
}
et := out.Type().Elem()
@ -606,7 +728,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
j := 0
for i := 0; i < l; i++ {
e := reflect.New(et).Elem()
if ok := d.unmarshal(n.children[i], e); ok {
if ok := d.unmarshal(n.Content[i], e); ok {
out.Index(j).Set(e)
j++
}
@ -620,51 +742,65 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
return true
}
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
if d.uniqueKeys {
nerrs := len(d.terrors)
for i := 0; i < l; i += 2 {
ni := n.Content[i]
for j := i + 2; j < l; j += 2 {
nj := n.Content[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
}
}
}
if len(d.terrors) > nerrs {
return false
}
}
switch out.Kind() {
case reflect.Struct:
return d.mappingStruct(n, out)
case reflect.Slice:
return d.mappingSlice(n, out)
case reflect.Map:
// okay
case reflect.Interface:
if d.mapType.Kind() == reflect.Map {
iface := out
out = reflect.MakeMap(d.mapType)
iface.Set(out)
iface := out
if isStringMap(n) {
out = reflect.MakeMap(d.stringMapType)
} else {
slicev := reflect.New(d.mapType).Elem()
if !d.mappingSlice(n, slicev) {
return false
}
out.Set(slicev)
return true
out = reflect.MakeMap(d.generalMapType)
}
iface.Set(out)
default:
d.terror(n, yaml_MAP_TAG, out)
d.terror(n, mapTag, out)
return false
}
outt := out.Type()
kt := outt.Key()
et := outt.Elem()
mapType := d.mapType
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
d.mapType = outt
stringMapType := d.stringMapType
generalMapType := d.generalMapType
if outt.Elem() == ifaceType {
if outt.Key().Kind() == reflect.String {
d.stringMapType = outt
} else if outt.Key() == ifaceType {
d.generalMapType = outt
}
}
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
}
l := len(n.children)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
if isMerge(n.Content[i]) {
d.merge(n.Content[i+1], out)
continue
}
k := reflect.New(kt).Elem()
if d.unmarshal(n.children[i], k) {
if d.unmarshal(n.Content[i], k) {
kkind := k.Kind()
if kkind == reflect.Interface {
kkind = k.Elem().Kind()
@ -673,61 +809,34 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
failf("invalid map key: %#v", k.Interface())
}
e := reflect.New(et).Elem()
if d.unmarshal(n.children[i+1], e) {
d.setMapIndex(n.children[i+1], out, k, e)
if d.unmarshal(n.Content[i+1], e) {
out.SetMapIndex(k, e)
}
}
}
d.mapType = mapType
d.stringMapType = stringMapType
d.generalMapType = generalMapType
return true
}
func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) {
if d.strict && out.MapIndex(k) != zeroValue {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface()))
return
}
out.SetMapIndex(k, v)
}
func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
outt := out.Type()
if outt.Elem() != mapItemType {
d.terror(n, yaml_MAP_TAG, out)
func isStringMap(n *Node) bool {
if n.Kind != MappingNode {
return false
}
mapType := d.mapType
d.mapType = outt
var slice []MapItem
var l = len(n.children)
l := len(n.Content)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
continue
}
item := MapItem{}
k := reflect.ValueOf(&item.Key).Elem()
if d.unmarshal(n.children[i], k) {
v := reflect.ValueOf(&item.Value).Elem()
if d.unmarshal(n.children[i+1], v) {
slice = append(slice, item)
}
if n.Content[i].ShortTag() != strTag {
return false
}
}
out.Set(reflect.ValueOf(slice))
d.mapType = mapType
return true
}
func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
sinfo, err := getStructInfo(out.Type())
if err != nil {
panic(err)
}
name := settableValueOf("")
l := len(n.children)
var inlineMap reflect.Value
var elemType reflect.Type
@ -737,23 +846,30 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
elemType = inlineMap.Type().Elem()
}
for _, index := range sinfo.InlineUnmarshalers {
field := d.fieldByIndex(n, out, index)
d.prepare(n, field)
}
var doneFields []bool
if d.strict {
if d.uniqueKeys {
doneFields = make([]bool, len(sinfo.FieldsList))
}
name := settableValueOf("")
l := len(n.Content)
for i := 0; i < l; i += 2 {
ni := n.children[i]
ni := n.Content[i]
if isMerge(ni) {
d.merge(n.children[i+1], out)
d.merge(n.Content[i+1], out)
continue
}
if !d.unmarshal(ni, name) {
continue
}
if info, ok := sinfo.FieldsMap[name.String()]; ok {
if d.strict {
if d.uniqueKeys {
if doneFields[info.Id] {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type()))
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
continue
}
doneFields[info.Id] = true
@ -762,18 +878,18 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
if info.Inline == nil {
field = out.Field(info.Num)
} else {
field = out.FieldByIndex(info.Inline)
field = d.fieldByIndex(n, out, info.Inline)
}
d.unmarshal(n.children[i+1], field)
d.unmarshal(n.Content[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.children[i+1], value)
d.setMapIndex(n.children[i+1], inlineMap, name, value)
} else if d.strict {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type()))
d.unmarshal(n.Content[i+1], value)
inlineMap.SetMapIndex(name, value)
} else if d.knownFields {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
}
}
return true
@ -783,24 +899,24 @@ func failWantMap() {
failf("map merge requires map or sequence of maps as the value")
}
func (d *decoder) merge(n *node, out reflect.Value) {
switch n.kind {
case mappingNode:
func (d *decoder) merge(n *Node, out reflect.Value) {
switch n.Kind {
case MappingNode:
d.unmarshal(n, out)
case aliasNode:
if n.alias != nil && n.alias.kind != mappingNode {
case AliasNode:
if n.Alias != nil && n.Alias.Kind != MappingNode {
failWantMap()
}
d.unmarshal(n, out)
case sequenceNode:
case SequenceNode:
// Step backwards as earlier nodes take precedence.
for i := len(n.children) - 1; i >= 0; i-- {
ni := n.children[i]
if ni.kind == aliasNode {
if ni.alias != nil && ni.alias.kind != mappingNode {
for i := len(n.Content) - 1; i >= 0; i-- {
ni := n.Content[i]
if ni.Kind == AliasNode {
if ni.Alias != nil && ni.Alias.Kind != MappingNode {
failWantMap()
}
} else if ni.kind != mappingNode {
} else if ni.Kind != MappingNode {
failWantMap()
}
d.unmarshal(ni, out)
@ -810,6 +926,6 @@ func (d *decoder) merge(n *node, out reflect.Value) {
}
}
func isMerge(n *node) bool {
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
func isMerge(n *Node) bool {
return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
}

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -43,8 +65,13 @@ func put_break(emitter *yaml_emitter_t) bool {
default:
panic("unknown line break setting")
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and below and drop from everywhere else (see commented lines).
emitter.indention = true
return true
}
@ -97,8 +124,13 @@ func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
if !write(emitter, s, i) {
return false
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and above and drop from everywhere else (see commented lines).
emitter.indention = true
}
return true
}
@ -204,6 +236,10 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
}
} else if !indentless {
emitter.indent += emitter.best_indent
// [Go] If inside a block sequence item, discount the space taken by the indicator.
if emitter.best_indent > 2 && emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
emitter.indent -= 2
}
}
return true
}
@ -228,16 +264,22 @@ func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bo
return yaml_emitter_emit_document_end(emitter, event)
case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, true)
return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false)
case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true)
case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false)
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, true)
return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false)
case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true)
case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false)
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
@ -298,6 +340,8 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t
emitter.column = 0
emitter.whitespace = true
emitter.indention = true
emitter.space_above = true
emitter.foot_indent = -1
if emitter.encoding != yaml_UTF8_ENCODING {
if !yaml_emitter_write_bom(emitter) {
@ -392,13 +436,22 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
return false
}
if emitter.canonical {
if emitter.canonical || true {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
}
if len(emitter.head_comment) > 0 {
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !put_break(emitter) {
return false
}
}
emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
return true
}
@ -425,7 +478,20 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
// Expect the root node.
func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
return yaml_emitter_emit_node(emitter, event, true, false, false, false)
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_emit_node(emitter, event, true, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect DOCUMENT-END.
@ -433,6 +499,12 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
if event.typ != yaml_DOCUMENT_END_EVENT {
return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
}
// [Go] Force document foot separation.
emitter.foot_indent = 0
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.foot_indent = -1
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -454,7 +526,7 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
}
// Expect a flow item node.
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
return false
@ -466,13 +538,15 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.canonical && !first {
if emitter.canonical && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.column == 0 || emitter.canonical && !first {
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -480,29 +554,62 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first {
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a flow key node.
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
return false
@ -514,13 +621,18 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
}
if event.typ == yaml_MAPPING_END_EVENT {
if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.canonical && !first {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -528,16 +640,33 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first {
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
@ -571,16 +700,41 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
return false
}
}
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block item node.
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first {
if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) {
// [Go] The original logic here would not indent the sequence when inside a mapping.
// In Go we always indent it, but take the sequence indicator out of the indentation.
indentless := emitter.best_indent == 2 && emitter.mapping_context && (emitter.column == 0 || !emitter.indention)
original := emitter.indent
if !yaml_emitter_increase_indent(emitter, false, indentless) {
return false
}
if emitter.indent > original+2 {
emitter.indent -= 2
}
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
@ -589,6 +743,9 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -596,7 +753,16 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
return false
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block key node.
@ -606,6 +772,9 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if event.typ == yaml_MAPPING_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
@ -642,7 +811,16 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
}
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a node.
@ -908,6 +1086,71 @@ func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
panic("unknown scalar style")
}
// Write a head comment.
func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
if len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.tail_comment) {
return false
}
emitter.tail_comment = emitter.tail_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
}
if len(emitter.head_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.head_comment) {
return false
}
emitter.head_comment = emitter.head_comment[:0]
return true
}
// Write an line comment.
func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
if len(emitter.line_comment) == 0 {
return true
}
if !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
if !yaml_emitter_write_comment(emitter, emitter.line_comment) {
return false
}
emitter.line_comment = emitter.line_comment[:0]
return true
}
// Write a foot comment.
func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool {
if len(emitter.foot_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.foot_comment) {
return false
}
emitter.foot_comment = emitter.foot_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
return true
}
// Check if a %YAML directive is valid.
func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
if version_directive.major != 1 || version_directive.minor != 1 {
@ -987,6 +1230,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
flow_indicators = false
line_breaks = false
special_characters = false
tab_characters = false
leading_space = false
leading_break = false
@ -1055,7 +1299,9 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
}
}
if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
if value[i] == '\t' {
tab_characters = true
} else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
special_characters = true
}
if is_space(value, i) {
@ -1110,10 +1356,12 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || special_characters {
if space_break || tab_characters || special_characters {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || special_characters {
emitter.scalar_data.block_allowed = false
}
if line_breaks {
@ -1137,6 +1385,19 @@ func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bo
emitter.tag_data.suffix = nil
emitter.scalar_data.value = nil
if len(event.head_comment) > 0 {
emitter.head_comment = event.head_comment
}
if len(event.line_comment) > 0 {
emitter.line_comment = event.line_comment
}
if len(event.foot_comment) > 0 {
emitter.foot_comment = event.foot_comment
}
if len(event.tail_comment) > 0 {
emitter.tail_comment = event.tail_comment
}
switch event.typ {
case yaml_ALIAS_EVENT:
if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
@ -1208,13 +1469,20 @@ func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
return false
}
}
if emitter.foot_indent == indent {
if !put_break(emitter) {
return false
}
}
for emitter.column < indent {
if !put(emitter, ' ') {
return false
}
}
emitter.whitespace = true
emitter.indention = true
//emitter.indention = true
emitter.space_above = false
emitter.foot_indent = -1
return true
}
@ -1311,7 +1579,7 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_
}
func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
if !emitter.whitespace {
if len(value) > 0 && !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
@ -1341,7 +1609,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1358,7 +1626,9 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
}
}
emitter.whitespace = false
if len(value) > 0 {
emitter.whitespace = false
}
emitter.indention = false
if emitter.root_context {
emitter.open_ended = true
@ -1397,7 +1667,7 @@ func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []by
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1599,7 +1869,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !put_break(emitter) {
return false
}
emitter.indention = true
//emitter.indention = true
emitter.whitespace = true
breaks := true
for i := 0; i < len(value); {
@ -1607,7 +1877,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1637,7 +1907,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !put_break(emitter) {
return false
}
emitter.indention = true
//emitter.indention = true
emitter.whitespace = true
breaks := true
@ -1658,7 +1928,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1683,3 +1953,40 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
}
return true
}
func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool {
breaks := false
pound := false
for i := 0; i < len(comment); {
if is_break(comment, i) {
if !write_break(emitter, comment, &i) {
return false
}
//emitter.indention = true
breaks = true
pound = false
} else {
if breaks && !yaml_emitter_write_indent(emitter) {
return false
}
if !pound {
if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) {
return false
}
pound = true
}
if !write(emitter, comment, &i) {
return false
}
emitter.indention = false
breaks = false
}
}
if !breaks && !put_break(emitter) {
return false
}
emitter.whitespace = true
//emitter.indention = true
return true
}

546
vendor/gopkg.in/yaml.v3/encode.go generated vendored Normal file
View File

@ -0,0 +1,546 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
"encoding"
"fmt"
"io"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
indent int
doneInit bool
}
func newEncoder() *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func newEncoderWithWriter(w io.Writer) *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_writer(&e.emitter, w)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func (e *encoder) init() {
if e.doneInit {
return
}
if e.indent == 0 {
e.indent = 4
}
e.emitter.best_indent = e.indent
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
e.emit()
e.doneInit = true
}
func (e *encoder) finish() {
e.emitter.open_ended = false
yaml_stream_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
e.must(yaml_emitter_emit(&e.emitter, &e.event))
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
e.init()
var node *Node
if in.IsValid() {
node, _ = in.Interface().(*Node)
}
if node != nil && node.Kind == DocumentNode {
e.nodev(in)
} else {
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.emit()
e.marshal(tag, in)
yaml_document_end_event_initialize(&e.event, true)
e.emit()
}
}
func (e *encoder) marshal(tag string, in reflect.Value) {
tag = shortTag(tag)
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}
iface := in.Interface()
switch value := iface.(type) {
case *Node:
e.nodev(in)
return
case time.Time:
e.timev(tag, in)
return
case *time.Time:
e.timev(tag, in.Elem())
return
case time.Duration:
e.stringv(tag, reflect.ValueOf(value.String()))
return
case Marshaler:
v, err := value.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
e.marshal(tag, reflect.ValueOf(v))
return
case encoding.TextMarshaler:
text, err := value.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
case nil:
e.nilv()
return
}
switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
e.marshal(tag, in.Elem())
case reflect.Struct:
e.structv(tag, in)
case reflect.Slice, reflect.Array:
e.slicev(tag, in)
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.intv(tag, in)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = e.fieldByIndex(in, info.Inline)
if !value.IsValid() {
continue
}
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
e.emit()
f()
yaml_mapping_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
canUsePlain := true
switch {
case !utf8.ValidString(s):
if tag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
s = encodeBase64(s)
case tag == "":
// Check to see if it would resolve to a specific
// tag when encoded unquoted. If it doesn't,
// there's no need to quote it.
rtag, _ := resolve("", s)
canUsePlain = rtag == strTag && !isBase60Float(s)
}
// Note: it's possible for user code to emit invalid YAML
// if they explicitly specify a tag and a string containing
// text that's incompatible with that tag.
switch {
case strings.Contains(s, "\n"):
if e.flow {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
} else {
style = yaml_LITERAL_SCALAR_STYLE
}
case canUsePlain:
style = yaml_PLAIN_SCALAR_STYLE
default:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
precision := 64
if in.Kind() == reflect.Float32 {
precision = 32
}
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
implicit := tag == ""
if !implicit {
tag = longTag(tag)
}
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.event.head_comment = head
e.event.line_comment = line
e.event.foot_comment = foot
e.event.tail_comment = tail
e.emit()
}
func (e *encoder) nodev(in reflect.Value) {
e.node(in.Interface().(*Node), "")
}
func (e *encoder) node(node *Node, tail string) {
// If the tag was not explicitly requested, and dropping it won't change the
// implicit tag of the value, don't include it in the presentation.
var tag = node.Tag
var stag = shortTag(tag)
var rtag string
var forceQuoting bool
if tag != "" && node.Style&TaggedStyle == 0 {
if node.Kind == ScalarNode {
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
tag = ""
} else {
rtag, _ = resolve("", node.Value)
if rtag == stag {
tag = ""
} else if stag == strTag {
tag = ""
forceQuoting = true
}
}
} else {
switch node.Kind {
case MappingNode:
rtag = mapTag
case SequenceNode:
rtag = seqTag
}
if rtag == stag {
tag = ""
}
}
}
switch node.Kind {
case DocumentNode:
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
yaml_document_end_event_initialize(&e.event, true)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case SequenceNode:
style := yaml_BLOCK_SEQUENCE_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style))
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case MappingNode:
style := yaml_BLOCK_MAPPING_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)
e.event.tail_comment = []byte(tail)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
// The tail logic below moves the foot comment of prior keys to the following key,
// since the value for each key may be a nested structure and the foot needs to be
// processed only the entirety of the value is streamed. The last tail is processed
// with the mapping end event.
var tail string
for i := 0; i+1 < len(node.Content); i += 2 {
k := node.Content[i]
foot := k.FootComment
if foot != "" {
kopy := *k
kopy.FootComment = ""
k = &kopy
}
e.node(k, tail)
tail = foot
v := node.Content[i+1]
e.node(v, "")
}
yaml_mapping_end_event_initialize(&e.event)
e.event.tail_comment = []byte(tail)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case AliasNode:
yaml_alias_event_initialize(&e.event, []byte(node.Value))
e.event.head_comment = []byte(node.HeadComment)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case ScalarNode:
value := node.Value
if !utf8.ValidString(value) {
if tag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
value = encodeBase64(value)
}
style := yaml_PLAIN_SCALAR_STYLE
switch {
case node.Style&DoubleQuotedStyle != 0:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
case node.Style&SingleQuotedStyle != 0:
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
case node.Style&LiteralStyle != 0:
style = yaml_LITERAL_SCALAR_STYLE
case node.Style&FoldedStyle != 0:
style = yaml_FOLDED_SCALAR_STYLE
case strings.Contains(value, "\n"):
style = yaml_LITERAL_SCALAR_STYLE
case forceQuoting:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
}
}

View File

@ -1,4 +1,4 @@
module "gopkg.in/yaml.v2"
module "gopkg.in/yaml.v3"
require (
"gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -45,11 +67,46 @@ import (
// Peek the next token in the token queue.
func peek_token(parser *yaml_parser_t) *yaml_token_t {
if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
return &parser.tokens[parser.tokens_head]
token := &parser.tokens[parser.tokens_head]
yaml_parser_unfold_comments(parser, token)
return token
}
return nil
}
// yaml_parser_unfold_comments walks through the comments queue and joins all
// comments behind the position of the provided token into the respective
// top-level comment slices in the parser.
func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) {
for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index {
comment := &parser.comments[parser.comments_head]
if len(comment.head) > 0 {
if token.typ == yaml_BLOCK_END_TOKEN {
// No heads on ends, so keep comment.head for a follow up token.
break
}
if len(parser.head_comment) > 0 {
parser.head_comment = append(parser.head_comment, '\n')
}
parser.head_comment = append(parser.head_comment, comment.head...)
}
if len(comment.foot) > 0 {
if len(parser.foot_comment) > 0 {
parser.foot_comment = append(parser.foot_comment, '\n')
}
parser.foot_comment = append(parser.foot_comment, comment.foot...)
}
if len(comment.line) > 0 {
if len(parser.line_comment) > 0 {
parser.line_comment = append(parser.line_comment, '\n')
}
parser.line_comment = append(parser.line_comment, comment.line...)
}
*comment = yaml_comment_t{}
parser.comments_head++
}
}
// Remove the next token from the queue (must be called after peek_token).
func skip_token(parser *yaml_parser_t) {
parser.token_available = false
@ -224,10 +281,32 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
parser.state = yaml_PARSE_BLOCK_NODE_STATE
var head_comment []byte
if len(parser.head_comment) > 0 {
// [Go] Scan the header comment backwards, and if an empty line is found, break
// the header so the part before the last empty line goes into the
// document header, while the bottom of it goes into a follow up event.
for i := len(parser.head_comment) - 1; i > 0; i-- {
if parser.head_comment[i] == '\n' {
if i == len(parser.head_comment)-1 {
head_comment = parser.head_comment[:i]
parser.head_comment = parser.head_comment[i+1:]
break
} else if parser.head_comment[i-1] == '\n' {
head_comment = parser.head_comment[:i-1]
parser.head_comment = parser.head_comment[i+1:]
break
}
}
}
}
*event = yaml_event_t{
typ: yaml_DOCUMENT_START_EVENT,
start_mark: token.start_mark,
end_mark: token.end_mark,
head_comment: head_comment,
}
} else if token.typ != yaml_STREAM_END_TOKEN {
@ -284,6 +363,7 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
if token == nil {
return false
}
if token.typ == yaml_VERSION_DIRECTIVE_TOKEN ||
token.typ == yaml_TAG_DIRECTIVE_TOKEN ||
token.typ == yaml_DOCUMENT_START_TOKEN ||
@ -327,9 +407,25 @@ func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t)
end_mark: end_mark,
implicit: implicit,
}
yaml_parser_set_event_comments(parser, event)
if len(event.head_comment) > 0 && len(event.foot_comment) == 0 {
event.foot_comment = event.head_comment
event.head_comment = nil
}
return true
}
func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) {
event.head_comment = parser.head_comment
event.line_comment = parser.line_comment
event.foot_comment = parser.foot_comment
parser.head_comment = nil
parser.line_comment = nil
parser.foot_comment = nil
parser.tail_comment = nil
parser.stem_comment = nil
}
// Parse the productions:
// block_node_or_indentless_sequence ::=
// ALIAS
@ -373,6 +469,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
end_mark: token.end_mark,
anchor: token.value,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -486,6 +583,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
quoted_implicit: quoted_implicit,
style: yaml_style_t(token.style),
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -502,6 +600,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
}
yaml_parser_set_event_comments(parser, event)
return true
}
if token.typ == yaml_FLOW_MAPPING_START_TOKEN {
@ -516,6 +615,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
}
yaml_parser_set_event_comments(parser, event)
return true
}
if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
@ -530,6 +630,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
}
if parser.stem_comment != nil {
event.head_comment = parser.stem_comment
parser.stem_comment = nil
}
return true
}
if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN {
@ -590,11 +694,25 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
if token.typ == yaml_BLOCK_ENTRY_TOKEN {
mark := token.end_mark
prior_head := len(parser.head_comment)
skip_token(parser)
token = peek_token(parser)
if token == nil {
return false
}
if prior_head > 0 && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
// [Go] It's a sequence under a sequence entry, so the former head comment
// is for the list itself, not the first list item under it.
parser.stem_comment = parser.head_comment[:prior_head]
if len(parser.head_comment) == prior_head {
parser.head_comment = nil
} else {
// Copy suffix to prevent very strange bugs if someone ever appends
// further bytes to the prefix in the stem_comment slice above.
parser.head_comment = append([]byte(nil), parser.head_comment[prior_head+1:]...)
}
}
if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
return yaml_parser_parse_node(parser, event, true, false)
@ -684,6 +802,19 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
return false
}
// [Go] A tail comment was left from the prior mapping value processed. Emit an event
// as it needs to be processed with that value and not the following key.
if len(parser.tail_comment) > 0 {
*event = yaml_event_t{
typ: yaml_TAIL_COMMENT_EVENT,
start_mark: token.start_mark,
end_mark: token.end_mark,
foot_comment: parser.tail_comment,
}
parser.tail_comment = nil
return true
}
if token.typ == yaml_KEY_TOKEN {
mark := token.end_mark
skip_token(parser)
@ -709,6 +840,7 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -820,6 +952,7 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
@ -959,6 +1092,7 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -95,7 +117,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
// [Go] This function was changed to guarantee the requested length size at EOF.
// The fact we need to do this is pretty awful, but the description above implies
// for that to be the case, and there are tests
// for that to be the case, and there are tests
// If the EOF flag is set and the raw buffer is empty, do nothing.
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -34,18 +49,14 @@ func init() {
tag string
l []string
}{
{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", yaml_MERGE_TAG, []string{"<<"}},
{true, boolTag, []string{"true", "True", "TRUE"}},
{false, boolTag, []string{"false", "False", "FALSE"}},
{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", mergeTag, []string{"<<"}},
}
m := resolveMap
@ -56,11 +67,37 @@ func init() {
}
}
const (
nullTag = "!!null"
boolTag = "!!bool"
strTag = "!!str"
intTag = "!!int"
floatTag = "!!float"
timestampTag = "!!timestamp"
seqTag = "!!seq"
mapTag = "!!map"
binaryTag = "!!binary"
mergeTag = "!!merge"
)
var longTags = make(map[string]string)
var shortTags = make(map[string]string)
func init() {
for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
ltag := longTag(stag)
longTags[stag] = ltag
shortTags[ltag] = stag
}
}
const longTagPrefix = "tag:yaml.org,2002:"
func shortTag(tag string) string {
// TODO This can easily be made faster and produce less garbage.
if strings.HasPrefix(tag, longTagPrefix) {
if stag, ok := shortTags[tag]; ok {
return stag
}
return "!!" + tag[len(longTagPrefix):]
}
return tag
@ -68,6 +105,9 @@ func shortTag(tag string) string {
func longTag(tag string) string {
if strings.HasPrefix(tag, "!!") {
if ltag, ok := longTags[tag]; ok {
return ltag
}
return longTagPrefix + tag[2:]
}
return tag
@ -75,7 +115,7 @@ func longTag(tag string) string {
func resolvableTag(tag string) bool {
switch tag {
case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG:
case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
return true
}
return false
@ -84,23 +124,24 @@ func resolvableTag(tag string) bool {
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
func resolve(tag string, in string) (rtag string, out interface{}) {
tag = shortTag(tag)
if !resolvableTag(tag) {
return tag, in
}
defer func() {
switch tag {
case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
case "", rtag, strTag, binaryTag:
return
case yaml_FLOAT_TAG:
if rtag == yaml_INT_TAG {
case floatTag:
if rtag == intTag {
switch v := out.(type) {
case int64:
rtag = yaml_FLOAT_TAG
rtag = floatTag
out = float64(v)
return
case int:
rtag = yaml_FLOAT_TAG
rtag = floatTag
out = float64(v)
return
}
@ -115,7 +156,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
if in != "" {
hint = resolveTable[in[0]]
}
if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
if hint != 0 && tag != strTag && tag != binaryTag {
// Handle things we can lookup in a map.
if item, ok := resolveMap[in]; ok {
return item.tag, item.value
@ -133,17 +174,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
// Not in the map, so maybe a normal float.
floatv, err := strconv.ParseFloat(in, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
return floatTag, floatv
}
case 'D', 'S':
// Int, float, or timestamp.
// Only try values as a timestamp if the value is unquoted or there's an explicit
// !!timestamp tag.
if tag == "" || tag == yaml_TIMESTAMP_TAG {
if tag == "" || tag == timestampTag {
t, ok := parseTimestamp(in)
if ok {
return yaml_TIMESTAMP_TAG, t
return timestampTag, t
}
}
@ -151,49 +192,76 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
intv, err := strconv.ParseInt(plain, 0, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain, 0, 64)
if err == nil {
return yaml_INT_TAG, uintv
return intTag, uintv
}
if yamlStyleFloat.MatchString(plain) {
floatv, err := strconv.ParseFloat(plain, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
return floatTag, floatv
}
}
if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
if err == nil {
return yaml_INT_TAG, uintv
return intTag, uintv
}
} else if strings.HasPrefix(plain, "-0b") {
intv, err := strconv.ParseInt("-" + plain[3:], 2, 64)
intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
if err == nil {
if true || intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
}
// Octals as introduced in version 1.2 of the spec.
// Octals from the 1.1 spec, spelled as 0777, are still
// decoded by default in v3 as well for compatibility.
// May be dropped in v4 depending on how usage evolves.
if strings.HasPrefix(plain, "0o") {
intv, err := strconv.ParseInt(plain[2:], 8, 64)
if err == nil {
if intv == int64(int(intv)) {
return intTag, int(intv)
} else {
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain[2:], 8, 64)
if err == nil {
return intTag, uintv
}
} else if strings.HasPrefix(plain, "-0o") {
intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
if err == nil {
if true || intv == int64(int(intv)) {
return intTag, int(intv)
} else {
return intTag, intv
}
}
}
default:
panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
}
}
return yaml_STR_TAG, in
return strTag, in
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -489,6 +511,9 @@ func cache(parser *yaml_parser_t, length int) bool {
// Advance the buffer pointer.
func skip(parser *yaml_parser_t) {
if !is_blank(parser.buffer, parser.buffer_pos) {
parser.newlines = 0
}
parser.mark.index++
parser.mark.column++
parser.unread--
@ -502,17 +527,22 @@ func skip_line(parser *yaml_parser_t) {
parser.mark.line++
parser.unread -= 2
parser.buffer_pos += 2
parser.newlines++
} else if is_break(parser.buffer, parser.buffer_pos) {
parser.mark.index++
parser.mark.column = 0
parser.mark.line++
parser.unread--
parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
parser.newlines++
}
}
// Copy a character to a string buffer and advance pointers.
func read(parser *yaml_parser_t, s []byte) []byte {
if !is_blank(parser.buffer, parser.buffer_pos) {
parser.newlines = 0
}
w := width(parser.buffer[parser.buffer_pos])
if w == 0 {
panic("invalid character sequence")
@ -564,6 +594,7 @@ func read_line(parser *yaml_parser_t, s []byte) []byte {
parser.mark.column = 0
parser.mark.line++
parser.unread--
parser.newlines++
return s
}
@ -626,9 +657,13 @@ func trace(args ...interface{}) func() {
func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
// While we need more tokens to fetch, do it.
for {
if parser.tokens_head != len(parser.tokens) {
// If queue is non-empty, check if any potential simple key may
// occupy the head position.
// [Go] The comment parsing logic requires a lookahead of two tokens
// so that foot comments may be parsed in time of associating them
// with the tokens that are parsed before them, and also for line
// comments to be transformed into head comments in some edge cases.
if parser.tokens_head < len(parser.tokens)-2 {
// If a potential simple key is at the head position, we need to fetch
// the next token to disambiguate it.
head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
if !ok {
break
@ -649,7 +684,7 @@ func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
}
// The dispatcher for token fetchers.
func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
// Ensure that the buffer is initialized.
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
@ -660,13 +695,19 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
return yaml_parser_fetch_stream_start(parser)
}
scan_mark := parser.mark
// Eat whitespaces and comments until we reach the next token.
if !yaml_parser_scan_to_next_token(parser) {
return false
}
// [Go] While unrolling indents, transform the head comments of prior
// indentation levels observed after scan_start into foot comments at
// the respective indexes.
// Check the indentation level against the current column.
if !yaml_parser_unroll_indent(parser, parser.mark.column) {
if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) {
return false
}
@ -699,6 +740,21 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)
}
comment_mark := parser.mark
if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') {
// Associate any following comments with the prior token.
comment_mark = parser.tokens[len(parser.tokens)-1].start_mark
}
defer func() {
if !ok {
return
}
if !yaml_parser_scan_line_comment(parser, comment_mark) {
ok = false
return
}
}()
// Is it the flow sequence start indicator?
if buf[pos] == '[' {
return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)
@ -792,7 +848,7 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
// if it is followed by a non-space character.
//
// The last rule is more restrictive than the specification requires.
// [Go] Make this logic more reasonable.
// [Go] TODO Make this logic more reasonable.
//switch parser.buffer[parser.buffer_pos] {
//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':
//}
@ -965,19 +1021,49 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml
// Pop indentation levels from the indents stack until the current level
// becomes less or equal to the column. For each indentation level, append
// the BLOCK-END token.
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool {
// In the flow context, do nothing.
if parser.flow_level > 0 {
return true
}
block_mark := scan_mark
block_mark.index--
// Loop through the indentation levels in the stack.
for parser.indent > column {
// [Go] Reposition the end token before potential following
// foot comments of parent blocks. For that, search
// backwards for recent comments that were at the same
// indent as the block that is ending now.
stop_index := block_mark.index
for i := len(parser.comments) - 1; i >= 0; i-- {
comment := &parser.comments[i]
if comment.end_mark.index < stop_index {
// Don't go back beyond the start of the comment/whitespace scan, unless column < 0.
// If requested indent column is < 0, then the document is over and everything else
// is a foot anyway.
break
}
if comment.start_mark.column == parser.indent+1 {
// This is a good match. But maybe there's a former comment
// at that same indent level, so keep searching.
block_mark = comment.start_mark
}
// While the end of the former comment matches with
// the start of the following one, we know there's
// nothing in between and scanning is still safe.
stop_index = comment.scan_mark.index
}
// Create a token and append it to the queue.
token := yaml_token_t{
typ: yaml_BLOCK_END_TOKEN,
start_mark: parser.mark,
end_mark: parser.mark,
start_mark: block_mark,
end_mark: block_mark,
}
yaml_insert_token(parser, -1, &token)
@ -1026,7 +1112,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
}
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1050,7 +1136,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1074,7 +1160,7 @@ func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
// Produce the DOCUMENT-START or DOCUMENT-END token.
func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1107,6 +1193,7 @@ func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_
// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {
// The indicators '[' and '{' may start a simple key.
if !yaml_parser_save_simple_key(parser) {
return false
@ -1442,6 +1529,8 @@ func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {
// Eat whitespaces and comments until the next token is found.
func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
scan_mark := parser.mark
// Until the next token is not found.
for {
// Allow the BOM mark to start a line.
@ -1468,13 +1557,33 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
}
}
// Check if we just had a line comment under a sequence entry that
// looks more like a header to the following content. Similar to this:
//
// - # The comment
// - Some data
//
// If so, transform the line comment to a head comment and reposition.
if len(parser.comments) > 0 && len(parser.tokens) > 1 {
tokenA := parser.tokens[len(parser.tokens)-2]
tokenB := parser.tokens[len(parser.tokens)-1]
comment := &parser.comments[len(parser.comments)-1]
if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) {
// If it was in the prior line, reposition so it becomes a
// header of the follow up token. Otherwise, keep it in place
// so it becomes a header of the former.
comment.head = comment.line
comment.line = nil
if comment.start_mark.line == parser.mark.line-1 {
comment.token_mark = parser.mark
}
}
}
// Eat a comment until a line break.
if parser.buffer[parser.buffer_pos] == '#' {
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if !yaml_parser_scan_comments(parser, scan_mark) {
return false
}
}
@ -1572,6 +1681,10 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool
}
if parser.buffer[parser.buffer_pos] == '#' {
// [Go] Discard this inline comment for the time being.
//if !yaml_parser_scan_line_comment(parser, start_mark) {
// return false
//}
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@ -1987,7 +2100,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte
// '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
// '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
// '%'.
// [Go] Convert this into more reasonable logic.
// [Go] TODO Convert this into more reasonable logic.
for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||
parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||
parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||
@ -2142,6 +2255,10 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
}
}
if parser.buffer[parser.buffer_pos] == '#' {
// TODO Test this and then re-enable it.
//if !yaml_parser_scan_line_comment(parser, start_mark) {
// return false
//}
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@ -2709,3 +2826,200 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b
}
return true
}
func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool {
if parser.newlines > 0 {
return true
}
var start_mark yaml_mark_t
var text []byte
for peek := 0; peek < 512; peek++ {
if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
break
}
if is_blank(parser.buffer, parser.buffer_pos+peek) {
continue
}
if parser.buffer[parser.buffer_pos+peek] == '#' {
seen := parser.mark.index+peek
for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if is_breakz(parser.buffer, parser.buffer_pos) {
if parser.mark.index >= seen {
break
}
if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
return false
}
skip_line(parser)
} else {
if parser.mark.index >= seen {
if len(text) == 0 {
start_mark = parser.mark
}
text = append(text, parser.buffer[parser.buffer_pos])
}
skip(parser)
}
}
}
break
}
if len(text) > 0 {
parser.comments = append(parser.comments, yaml_comment_t{
token_mark: token_mark,
start_mark: start_mark,
line: text,
})
}
return true
}
func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool {
token := parser.tokens[len(parser.tokens)-1]
if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 {
token = parser.tokens[len(parser.tokens)-2]
}
var token_mark = token.start_mark
var start_mark yaml_mark_t
var recent_empty = false
var first_empty = parser.newlines <= 1
var line = parser.mark.line
var column = parser.mark.column
var text []byte
// The foot line is the place where a comment must start to
// still be considered as a foot of the prior content.
// If there's some content in the currently parsed line, then
// the foot is the line below it.
var foot_line = -1
if scan_mark.line > 0 {
foot_line = parser.mark.line-parser.newlines+1
if parser.newlines == 0 && parser.mark.column > 1 {
foot_line++
}
}
var peek = 0
for ; peek < 512; peek++ {
if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
break
}
column++
if is_blank(parser.buffer, parser.buffer_pos+peek) {
continue
}
c := parser.buffer[parser.buffer_pos+peek]
if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') {
// Got line break or terminator.
if !recent_empty {
if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) {
// This is the first empty line and there were no empty lines before,
// so this initial part of the comment is a foot of the prior token
// instead of being a head for the following one. Split it up.
if len(text) > 0 {
if start_mark.column-1 < parser.indent {
// If dedented it's unrelated to the prior token.
token_mark = start_mark
}
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: token_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
foot: text,
})
scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
token_mark = scan_mark
text = nil
}
} else {
if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 {
text = append(text, '\n')
}
}
}
if !is_break(parser.buffer, parser.buffer_pos+peek) {
break
}
first_empty = false
recent_empty = true
column = 0
line++
continue
}
if len(text) > 0 && column < parser.indent+1 && column != start_mark.column {
// The comment at the different indentation is a foot of the
// preceding data rather than a head of the upcoming one.
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: token_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
foot: text,
})
scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
token_mark = scan_mark
text = nil
}
if parser.buffer[parser.buffer_pos+peek] != '#' {
break
}
if len(text) == 0 {
start_mark = yaml_mark_t{parser.mark.index + peek, line, column}
} else {
text = append(text, '\n')
}
recent_empty = false
// Consume until after the consumed comment line.
seen := parser.mark.index+peek
for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if is_breakz(parser.buffer, parser.buffer_pos) {
if parser.mark.index >= seen {
break
}
if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
return false
}
skip_line(parser)
} else {
if parser.mark.index >= seen {
text = append(text, parser.buffer[parser.buffer_pos])
}
skip(parser)
}
}
peek = 0
column = 0
line = parser.mark.line
}
if len(text) > 0 {
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: start_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column},
head: text,
})
}
return true
}

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -37,8 +52,10 @@ func (l keyList) Less(i, j int) bool {
return ak < bk
}
ar, br := []rune(a.String()), []rune(b.String())
digits := false
for i := 0; i < len(ar) && i < len(br); i++ {
if ar[i] == br[i] {
digits = unicode.IsDigit(ar[i])
continue
}
al := unicode.IsLetter(ar[i])
@ -47,12 +64,16 @@ func (l keyList) Less(i, j int) bool {
return ar[i] < br[i]
}
if al || bl {
return bl
if digits {
return al
} else {
return bl
}
}
var ai, bi int
var an, bn int64
if ar[i] == '0' || br[i] == '0' {
for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
if ar[j] != '0' {
an = 1
bn = 1

48
vendor/gopkg.in/yaml.v3/writerc.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
// Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_WRITER_ERROR
emitter.problem = problem
return false
}
// Flush the output buffer.
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
if emitter.write_handler == nil {
panic("write handler not set")
}
// Check if the buffer is empty.
if emitter.buffer_pos == 0 {
return true
}
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
return true
}

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package yaml implements YAML support for the Go language.
//
// Source code and other details for the project are available at GitHub:
@ -13,23 +28,16 @@ import (
"reflect"
"strings"
"sync"
"unicode/utf8"
)
// MapSlice encodes and decodes as a YAML map.
// The order of keys is preserved when encoding and decoding.
type MapSlice []MapItem
// MapItem is an item in a MapSlice.
type MapItem struct {
Key, Value interface{}
// The Unmarshaler interface may be implemented by types to customize their
// behavior when being unmarshaled from a YAML document.
type Unmarshaler interface {
UnmarshalYAML(value *Node) error
}
// The Unmarshaler interface may be implemented by types to customize their
// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
// method receives a function that may be called to unmarshal the original
// YAML value into a field or variable. It is safe to call the unmarshal
// function parameter more than once if necessary.
type Unmarshaler interface {
type obsoleteUnmarshaler interface {
UnmarshalYAML(unmarshal func(interface{}) error) error
}
@ -81,18 +89,10 @@ func Unmarshal(in []byte, out interface{}) (err error) {
return unmarshal(in, out, false)
}
// UnmarshalStrict is like Unmarshal except that any fields that are found
// in the data that do not have corresponding struct members, or mapping
// keys that are duplicates, will result in
// an error.
func UnmarshalStrict(in []byte, out interface{}) (err error) {
return unmarshal(in, out, true)
}
// A Decoder reads and decodes YAML values from an input stream.
// A Decorder reads and decodes YAML values from an input stream.
type Decoder struct {
strict bool
parser *parser
parser *parser
knownFields bool
}
// NewDecoder returns a new decoder that reads from r.
@ -105,10 +105,10 @@ func NewDecoder(r io.Reader) *Decoder {
}
}
// SetStrict sets whether strict decoding behaviour is enabled when
// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict.
func (dec *Decoder) SetStrict(strict bool) {
dec.strict = strict
// KnownFields ensures that the keys in decoded mappings to
// exist as fields in the struct being decoded into.
func (dec *Decoder) KnownFields(enable bool) {
dec.knownFields = enable
}
// Decode reads the next YAML-encoded value from its input
@ -117,7 +117,8 @@ func (dec *Decoder) SetStrict(strict bool) {
// See the documentation for Unmarshal for details about the
// conversion of YAML into a Go value.
func (dec *Decoder) Decode(v interface{}) (err error) {
d := newDecoder(dec.strict)
d := newDecoder()
d.knownFields = dec.knownFields
defer handleErr(&err)
node := dec.parser.parse()
if node == nil {
@ -134,9 +135,27 @@ func (dec *Decoder) Decode(v interface{}) (err error) {
return nil
}
// Decode decodes the node and stores its data into the value pointed to by v.
//
// See the documentation for Unmarshal for details about the
// conversion of YAML into a Go value.
func (n *Node) Decode(v interface{}) (err error) {
d := newDecoder()
defer handleErr(&err)
out := reflect.ValueOf(v)
if out.Kind() == reflect.Ptr && !out.IsNil() {
out = out.Elem()
}
d.unmarshal(n, out)
if len(d.terrors) > 0 {
return &TypeError{d.terrors}
}
return nil
}
func unmarshal(in []byte, out interface{}, strict bool) (err error) {
defer handleErr(&err)
d := newDecoder(strict)
d := newDecoder()
p := newParser(in)
defer p.destroy()
node := p.parse()
@ -233,6 +252,14 @@ func (e *Encoder) Encode(v interface{}) (err error) {
return nil
}
// SetIndent changes the used indentation used when encoding.
func (e *Encoder) SetIndent(spaces int) {
if spaces < 0 {
panic("yaml: cannot indent to a negative number of spaces")
}
e.encoder.indent = spaces
}
// Close closes the encoder by writing any remaining data.
// It does not write a stream terminating string "...".
func (e *Encoder) Close() (err error) {
@ -275,6 +302,150 @@ func (e *TypeError) Error() string {
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
}
type Kind uint32
const (
DocumentNode Kind = 1 << iota
SequenceNode
MappingNode
ScalarNode
AliasNode
)
type Style uint32
const (
TaggedStyle Style = 1 << iota
DoubleQuotedStyle
SingleQuotedStyle
LiteralStyle
FoldedStyle
FlowStyle
)
// Node represents an element in the YAML document hierarchy. While documents
// are typically encoded and decoded into higher level types, such as structs
// and maps, Node is an intermediate representation that allows detailed
// control over the content being decoded or encoded.
//
// Values that make use of the Node type interact with the yaml package in the
// same way any other type would do, by encoding and decoding yaml data
// directly or indirectly into them.
//
// For example:
//
// var person struct {
// Name string
// Address yaml.Node
// }
// err := yaml.Unmarshal(data, &person)
//
// Or by itself:
//
// var person Node
// err := yaml.Unmarshal(data, &person)
//
type Node struct {
// Kind defines whether the node is a document, a mapping, a sequence,
// a scalar value, or an alias to another node. The specific data type of
// scalar nodes may be obtained via the ShortTag and LongTag methods.
Kind Kind
// Style allows customizing the apperance of the node in the tree.
Style Style
// Tag holds the YAML tag defining the data type for the value.
// When decoding, this field will always be set to the resolved tag,
// even when it wasn't explicitly provided in the YAML content.
// When encoding, if this field is unset the value type will be
// implied from the node properties, and if it is set, it will only
// be serialized into the representation if TaggedStyle is used or
// the implicit tag diverges from the provided one.
Tag string
// Value holds the unescaped and unquoted represenation of the value.
Value string
// Anchor holds the anchor name for this node, which allows aliases to point to it.
Anchor string
// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
Alias *Node
// Content holds contained nodes for documents, mappings, and sequences.
Content []*Node
// HeadComment holds any comments in the lines preceding the node and
// not separated by an empty line.
HeadComment string
// LineComment holds any comments at the end of the line where the node is in.
LineComment string
// FootComment holds any comments following the node and before empty lines.
FootComment string
// Line and Column hold the node position in the decoded YAML text.
// These fields are not respected when encoding the node.
Line int
Column int
}
// LongTag returns the long form of the tag that indicates the data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
func (n *Node) LongTag() string {
return longTag(n.ShortTag())
}
// ShortTag returns the short form of the YAML tag that indicates data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
func (n *Node) ShortTag() string {
if n.indicatedString() {
return strTag
}
if n.Tag == "" || n.Tag == "!" {
switch n.Kind {
case MappingNode:
return mapTag
case SequenceNode:
return seqTag
case AliasNode:
if n.Alias != nil {
return n.Alias.ShortTag()
}
case ScalarNode:
tag, _ := resolve("", n.Value)
return tag
}
return ""
}
return shortTag(n.Tag)
}
func (n *Node) indicatedString() bool {
return n.Kind == ScalarNode &&
(shortTag(n.Tag) == strTag ||
(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
}
// SetString is a convenience function that sets the node to a string value
// and defines its style in a pleasant way depending on its content.
func (n *Node) SetString(s string) {
n.Kind = ScalarNode
if utf8.ValidString(s) {
n.Value = s
n.Tag = strTag
} else {
n.Value = encodeBase64(s)
n.Tag = binaryTag
}
if strings.Contains(n.Value, "\n") {
n.Style = LiteralStyle
}
}
// --------------------------------------------------------------------------
// Maintain a mapping of keys to structure field indexes
@ -289,6 +460,10 @@ type structInfo struct {
// InlineMap is the number of the field in the struct that
// contains an ,inline map, or -1 if there's none.
InlineMap int
// InlineUnmarshalers holds indexes to inlined fields that
// contain unmarshaler values.
InlineUnmarshalers [][]int
}
type fieldInfo struct {
@ -306,6 +481,12 @@ type fieldInfo struct {
var structMap = make(map[reflect.Type]*structInfo)
var fieldMapMutex sync.RWMutex
var unmarshalerType reflect.Type
func init() {
var v Unmarshaler
unmarshalerType = reflect.ValueOf(&v).Elem().Type()
}
func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldMapMutex.RLock()
@ -319,6 +500,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldsMap := make(map[string]fieldInfo)
fieldsList := make([]fieldInfo, 0, n)
inlineMap := -1
inlineUnmarshalers := [][]int(nil)
for i := 0; i != n; i++ {
field := st.Field(i)
if field.PkgPath != "" && !field.Anonymous {
@ -347,7 +529,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
case "inline":
inline = true
default:
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
}
}
tag = fields[0]
@ -357,34 +539,47 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
switch field.Type.Kind() {
case reflect.Map:
if inlineMap >= 0 {
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
return nil, errors.New("multiple ,inline maps in struct " + st.String())
}
if field.Type.Key() != reflect.TypeOf("") {
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Struct:
sinfo, err := getStructInfo(field.Type)
if err != nil {
return nil, err
case reflect.Struct, reflect.Ptr:
ftype := field.Type
for ftype.Kind() == reflect.Ptr {
ftype = ftype.Elem()
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
if ftype.Kind() != reflect.Struct {
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
if reflect.PtrTo(ftype).Implements(unmarshalerType) {
inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
} else {
sinfo, err := getStructInfo(ftype)
if err != nil {
return nil, err
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
for _, index := range sinfo.InlineUnmarshalers {
inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
}
finfo.Id = len(fieldsList)
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
finfo.Id = len(fieldsList)
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
default:
//return nil, errors.New("Option ,inline needs a struct value or map field")
return nil, errors.New("Option ,inline needs a struct value field")
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
continue
}
@ -396,7 +591,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
}
if _, found = fieldsMap[info.Key]; found {
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
msg := "duplicated key '" + info.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
@ -406,9 +601,10 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
}
sinfo = &structInfo{
FieldsMap: fieldsMap,
FieldsList: fieldsList,
InlineMap: inlineMap,
FieldsMap: fieldsMap,
FieldsList: fieldsList,
InlineMap: inlineMap,
InlineUnmarshalers: inlineUnmarshalers,
}
fieldMapMutex.Lock()

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -73,13 +95,13 @@ type yaml_scalar_style_t yaml_style_t
// Scalar styles.
const (
// Let the emitter choose the style.
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0
yaml_PLAIN_SCALAR_STYLE // The plain scalar style.
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style.
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
)
type yaml_sequence_style_t yaml_style_t
@ -238,6 +260,7 @@ const (
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
yaml_MAPPING_START_EVENT // A MAPPING-START event.
yaml_MAPPING_END_EVENT // A MAPPING-END event.
yaml_TAIL_COMMENT_EVENT
)
var eventStrings = []string{
@ -252,6 +275,7 @@ var eventStrings = []string{
yaml_SEQUENCE_END_EVENT: "sequence end",
yaml_MAPPING_START_EVENT: "mapping start",
yaml_MAPPING_END_EVENT: "mapping end",
yaml_TAIL_COMMENT_EVENT: "tail comment",
}
func (e yaml_event_type_t) String() string {
@ -279,6 +303,12 @@ type yaml_event_t struct {
// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
tag_directives []yaml_tag_directive_t
// The comments
head_comment []byte
line_comment []byte
foot_comment []byte
tail_comment []byte
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
anchor []byte
@ -554,6 +584,8 @@ type yaml_parser_t struct {
unread int // The number of unread characters in the buffer.
newlines int // The number of line breaks since last non-break/non-blank character
raw_buffer []byte // The raw buffer.
raw_buffer_pos int // The current position of the buffer.
@ -562,6 +594,17 @@ type yaml_parser_t struct {
offset int // The offset of the current position (in bytes).
mark yaml_mark_t // The mark of the current position.
// Comments
head_comment []byte // The current head comments
line_comment []byte // The current line comments
foot_comment []byte // The current foot comments
tail_comment []byte // Foot comment that happens at the end of a block.
stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc)
comments []yaml_comment_t // The folded comments for all parsed tokens
comments_head int
// Scanner stuff
stream_start_produced bool // Have we started to scan the input stream?
@ -595,6 +638,18 @@ type yaml_parser_t struct {
document *yaml_document_t // The currently parsed document.
}
type yaml_comment_t struct {
scan_mark yaml_mark_t // Position where scanning for comments started
token_mark yaml_mark_t // Position after which tokens will be associated with this comment
start_mark yaml_mark_t // Position of '#' comment mark
end_mark yaml_mark_t // Position where comment terminated
head []byte
line []byte
foot []byte
}
// Emitter Definitions
// The prototype of a write handler.
@ -625,8 +680,10 @@ const (
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
@ -698,6 +755,9 @@ type yaml_emitter_t struct {
indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
open_ended bool // If an explicit document end is required?
space_above bool // Is there's an empty line above?
foot_indent int // The indent used to write the foot comment above, or -1 if none.
// Anchor analysis.
anchor_data struct {
anchor []byte // The anchor value.
@ -721,6 +781,12 @@ type yaml_emitter_t struct {
style yaml_scalar_style_t // The output style.
}
// Comments
head_comment []byte
line_comment []byte
foot_comment []byte
tail_comment []byte
// Dumper stuff
opened bool // If the stream was already opened?

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
const (
@ -114,8 +136,9 @@ func is_crlf(b []byte, i int) bool {
// Check if the character is a line break or NUL.
func is_breakz(b []byte, i int) bool {
//return is_break(b, i) || is_z(b, i)
return ( // is_break:
b[i] == '\r' || // CR (#xD)
return (
// is_break:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
@ -127,8 +150,9 @@ func is_breakz(b []byte, i int) bool {
// Check if the character is a line break, space, or NUL.
func is_spacez(b []byte, i int) bool {
//return is_space(b, i) || is_breakz(b, i)
return ( // is_space:
b[i] == ' ' ||
return (
// is_space:
b[i] == ' ' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
@ -141,8 +165,9 @@ func is_spacez(b []byte, i int) bool {
// Check if the character is a line break, space, tab, or NUL.
func is_blankz(b []byte, i int) bool {
//return is_blank(b, i) || is_breakz(b, i)
return ( // is_blank:
b[i] == ' ' || b[i] == '\t' ||
return (
// is_blank:
b[i] == ' ' || b[i] == '\t' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)

6
vendor/modules.txt vendored
View File

@ -60,8 +60,6 @@ github.com/google/go-cmp/cmp
github.com/google/go-cmp/cmp/internal/diff
github.com/google/go-cmp/cmp/internal/function
github.com/google/go-cmp/cmp/internal/value
# github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c
github.com/howeyc/gopass
# github.com/inconshreveable/mousetrap v1.0.0
github.com/inconshreveable/mousetrap
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
@ -185,8 +183,8 @@ gopkg.in/src-d/go-git.v4/utils/merkletrie/internal/frame
gopkg.in/src-d/go-git.v4/utils/merkletrie/noder
# gopkg.in/warnings.v0 v0.1.2
gopkg.in/warnings.v0
# gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
gopkg.in/yaml.v3
# gotest.tools v2.2.0+incompatible
gotest.tools/assert
gotest.tools/assert/cmp