Open-CMSIS-Pack/cpackget

View on GitHub
cmd/xml/pdsc.go

Summary

Maintainability
A
0 mins
Test Coverage
C
73%
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright Contributors to the cpackget project. */

package xml

import (
    "encoding/xml"
    "strings"

    "github.com/open-cmsis-pack/cpackget/cmd/utils"
    log "github.com/sirupsen/logrus"
)

// PdscXML maps few tags of a PDSC file.
// Ref: https://github.com/Open-CMSIS-Pack/Open-CMSIS-Pack-Spec/blob/main/schema/PACK.xsd
type PdscXML struct {
    XMLName xml.Name `xml:"package"`
    Vendor  string   `xml:"vendor"`
    URL     string   `xml:"url"`
    Name    string   `xml:"name"`
    License string   `xml:"license"`

    ReleasesTag struct {
        XMLName  xml.Name     `xml:"releases"`
        Releases []ReleaseTag `xml:"release"`
    } `xml:"releases"`

    RequirementsTag struct {
        XMLName  xml.Name      `xml:"requirements"`
        Packages []PackagesTag `xml:"packages"`
    } `xml:"requirements"`

    FileName string
}

// ReleaseTag maps the <release> tag of a PDSC file.
type ReleaseTag struct {
    XMLName xml.Name `xml:"release"`
    Version string   `xml:"version,attr"`
    Date    string   `xml:"Date,attr"`
    URL     string   `xml:"url,attr"`
}

// PackagesTag only has one possible child, which is <package>
type PackagesTag struct {
    XMLName  xml.Name     `xml:"packages"`
    Packages []PackageTag `xml:"package"`
}

// Package represents a direct dependency/requirement of this package
type PackageTag struct {
    XMLName xml.Name `xml:"package"`
    Vendor  string   `xml:"vendor,attr"`
    Name    string   `xml:"name,attr"`
    Version string   `xml:"version,attr"`
}

// NewPdscXML receives a PDSC file name to be later read into the PdscXML struct
func NewPdscXML(fileName string) *PdscXML {
    log.Debugf("Initializing PdscXML object for \"%s\"", fileName)
    p := new(PdscXML)
    p.FileName = fileName
    return p
}

// LatestVersion returns a string containing version of the first tag within
// the <releases> tag.
func (p *PdscXML) LatestVersion() string {
    releases := p.ReleasesTag.Releases
    if len(releases) > 0 {
        return releases[0].Version
    }
    return ""
}

// AllReleases returns a slice of strings containing all available releases in this Pdsc file
func (p *PdscXML) AllReleases() []string {
    allReleases := []string{}
    if len(p.ReleasesTag.Releases) > 0 {
        for _, releaseTag := range p.ReleasesTag.Releases {
            allReleases = append(allReleases, releaseTag.Version)
        }
    }

    return allReleases
}

// FindReleaseTagByVersion iterates over the PDSC file's releases tag and returns
// the release that matching version.
func (p *PdscXML) FindReleaseTagByVersion(version string) *ReleaseTag {
    releases := p.ReleasesTag.Releases
    if len(releases) > 0 {
        if version == "" {
            return &releases[0]
        }

        for _, releaseTag := range releases {
            if utils.SemverCompare(releaseTag.Version, version) == 0 {
                return &releaseTag
            }
        }
    }
    return nil
}

// Tag returns a PdscTag representation of a PDSC file.
func (p *PdscXML) Tag() PdscTag {
    return PdscTag{
        Vendor:  p.Vendor,
        URL:     p.URL,
        Name:    p.Name,
        Version: p.LatestVersion(),
    }
}

// Read reads the PDSC file specified in p.FileName into the PdscXML struct
func (p *PdscXML) Read() error {
    log.Debugf("Reading pdsc from file \"%s\"", p.FileName)
    return utils.ReadXML(p.FileName, p)
}

// PackURL returns a url for the Pack described in this PDSC file
func (p *PdscXML) PackURL(version string) string {
    baseURL := p.URL
    lenBaseURL := len(baseURL)
    if lenBaseURL > 0 && baseURL[len(baseURL)-1] != '/' {
        baseURL += "/"
    }

    if version == "" {
        version = p.LatestVersion()
    }

    return baseURL + p.Vendor + "." + p.Name + "." + utils.SemverStripMeta(version) + ".pack"
}

// Dependencies returns all the listed packs that need to be installed
// alongside, as per the <requirements> section. It returns a [][]string
// array containing the packs in [<Name>, <Vendor>, <Version>] format.
// <Version> is an internal interpretation:
// latest -> install the latest available
// x.y.z:x.y.z -> install exact version
// a.b.c:x.y.z -> install latest ranging from a.b.c to x.y.z
// x.y.z:_ -> install latest newer than x.y.z
// Ref: https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/element_requirements_pg.html#element_packages
func (p *PdscXML) Dependencies() [][]string {
    dependencies := [][]string{}
    if p.RequirementsTag.Packages == nil {
        return nil
    }
    for i, pack := range p.RequirementsTag.Packages {
        for _, pk := range pack.Packages {
            // empty -> install the latest
            if pk.Version == "" {
                pk.Version = "latest"
            } else {
                // If it's a single version, it's the minimum to install
                // fake a range one
                if strings.Count(pk.Version, ":") == 0 {
                    pk.Version = pk.Version + ":_"
                }
            }
            dependencies = append(dependencies, []string{pk.Name, pk.Vendor, pk.Version})
            log.Debugf("found %v dependency", dependencies[i])
        }
    }
    return dependencies
}