Skip to content

🧪 Typed return values

One of Composer Skills' core differentiators from "hand-rolled exec.Command": every meaningful command returns a structured Go type, not raw strings. This page lists the main typed return values and contrasts them with the pain of string parsing.

🔄 Structured return data flow

💔 The pain of string parsing

go
// The old way: get a blob of text, split it, regex it, guess column widths
out, _ := exec.Command("composer", "audit", "--format=json").Output()
// out is []byte, you have to:
//   - json.Unmarshal into a struct you invent
//   - guess a field name wrong → empty value
//   - Composer update changes schema → silent failure

Not only is it tedious to write, testing is hard: you have to mock an entire subprocess. Composer Skills defines and tests these structs for you, returning them directly.

✅ Typed return value overview

Here are the most commonly used structured return types, all from pkg/composer:

TypeGetter methodMeaning
🔒 AuditResultAuditWithJSON()Security audit result: vulnerability count, advisory list, severity levels
📊 AuditInfoGetAuditInfo()Audited summary info (higher-level abstracted view)
📦 OutdatedResult / OutdatedInfoOutdatedPackages() / GetOutdatedInfo()Outdated package list: current version, latest version, direct dependency flag
🏷️ VersionInfoGetVersionInfo()Composer's own version information
📋 PackageInfoGetPackageInfo(name)Structured info for an installed package
ValidateResultValidateStructured()composer.json validation result: validity, error list
🖥️ PlatformReqsCheckPlatformReqsStructured()Platform requirement check results (PHP version, extensions)
📄 LicensesInfoGetLicensesInfo()License info for all project dependencies
⚙️ ConfigInfoGetConfigStructured()Structured view of Composer config
🔍 SearchInfoSearchInfo(query)Local search results
🩺 DiagnoseInfoDiagnoseStructured()Structured result of composer diagnose

Naming convention

Methods suffixed with WithJSON return the struct parsed from Composer's raw JSON (most complete fields); methods suffixed with Info or Structured return a higher-level abstracted view (easier to use). Both are type-safe.

🌰 Example: structured audit

go
result, err := comp.AuditWithJSON()
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Total vulnerabilities: %d\n", result.Found)
for _, v := range result.Advisories {
    fmt.Printf("  ⚠ %s: %s\n", v.Package, v.Title)
    fmt.Printf("    Severity: %s\n", v.Severity)
    fmt.Printf("    CVE: %s\n", v.CVE)
    fmt.Printf("    Fix version: %s\n", v.Solution)
}

// CI failure decision, one line
if result.Found > 0 {
    os.Exit(1)
}

Contrast with string parsing: you get result.Found (int), v.Package (string) — the compiler checks types for you, so there's no "column width changed, now my index is out of bounds".

🌰 Example: outdated package monitoring

go
outdated, err := comp.GetOutdatedInfo()
if err != nil {
    log.Fatal(err)
}
for _, p := range outdated.Packages {
    fmt.Printf("%s: %s%s (direct: %v)\n",
        p.Name, p.Version, p.Latest, p.DirectDependency)
}

🌰 Example: composer.json validation

go
res, err := comp.ValidateStructured()
if err != nil {
    log.Fatal(err)
}
if !res.Valid {
    for _, e := range res.Errors {
        fmt.Printf("❌ Line %d: %s\n", e.Line, e.Message)
    }
}

🌐 Packagist API is also typed

Both SDKs follow the same philosophy — pkg/client also returns structs:

go
pkg, _ := c.GetPackage("monolog/monolog")
// pkg.Package.Name / .Description / .Versions ... all structured fields

advisories, _ := c.GetSecurityAdvisories()
// advisories.Advisories is []Advisory, each with Package/Title/Severity

stats, _ := c.GetStatistics()
// stats.Packages / .Downloads are int

💡 Why structured returns matter

  • 🛡️ Reliability: Field names are bound by the SDK; minor Composer output tweaks won't silently break your program.
  • 🧪 Testability: SDK has built-in SetupMockOutput; inject structured expectations in tests without actually running Composer.
  • 🤖 Composability: After getting an AuditResult, you can feed it directly to a report generator, CI failure check, or alerting system.
  • 📖 Readability: result.Found > 0 is a hundred times clearer than strings.Contains(out, "Vulnerabilities").

Pay attention to return type naming

Some methods end with WithJSON and return a direct mapping of Composer's raw JSON — most complete but also most "raw". If you just want a higher-level abstraction, prefer Get*Info or *Structured variants. Both return typed structs.

🧭 Next steps

Released under the MIT License