about summary refs log tree commit diff
path: root/third_party/nix/src/libexpr/value-to-xml.cc
#include "libexpr/value-to-xml.hh"

#include <cstdlib>

#include "libexpr/eval-inline.hh"
#include "libutil/util.hh"
#include "libutil/xml-writer.hh"

namespace nix {

static XMLAttrs singletonAttrs(const std::string& name,
                               const std::string& value) {
  XMLAttrs attrs;
  attrs[name] = value;
  return attrs;
}

static void printValueAsXML(EvalState& state, bool strict, bool location,
                            Value& v, XMLWriter& doc, PathSet& context,
                            PathSet& drvsSeen);

static void posToXML(XMLAttrs& xmlAttrs, const Pos& pos) {
  xmlAttrs["path"] = pos.file.value();
  xmlAttrs["line"] = (format("%1%") % pos.line).str();
  xmlAttrs["column"] = (format("%1%") % pos.column).str();
}

static void showAttrs(EvalState& state, bool strict, bool location,
                      Bindings& attrs, XMLWriter& doc, PathSet& context,
                      PathSet& drvsSeen) {
  StringSet names;

  for (auto& i : attrs) {
    names.insert(i.second.name);
  }

  for (auto& i : names) {
    auto& [_, a] = *attrs.find(state.symbols.Create(i));

    XMLAttrs xmlAttrs;
    xmlAttrs["name"] = i;
    if (location && a.pos != &noPos) {
      posToXML(xmlAttrs, *a.pos);
    }

    XMLOpenElement elem(doc, "attr", xmlAttrs);
    printValueAsXML(state, strict, location, *a.value, doc, context, drvsSeen);
  }
}

static void printValueAsXML(EvalState& state, bool strict, bool location,
                            Value& v, XMLWriter& doc, PathSet& context,
                            PathSet& drvsSeen) {
  checkInterrupt();

  if (strict) {
    state.forceValue(v);
  }

  switch (v.type) {
    case tInt:
      doc.writeEmptyElement(
          "int", singletonAttrs("value", (format("%1%") % v.integer).str()));
      break;

    case tBool:
      doc.writeEmptyElement(
          "bool", singletonAttrs("value", v.boolean ? "true" : "false"));
      break;

    case tString:
      /* !!! show the context? */
      copyContext(v, context);
      doc.writeEmptyElement("string", singletonAttrs("value", v.string.s));
      break;

    case tPath:
      doc.writeEmptyElement("path", singletonAttrs("value", v.path));
      break;

    case tNull:
      doc.writeEmptyElement("null");
      break;

    case tAttrs:
      if (state.isDerivation(v)) {
        XMLAttrs xmlAttrs;

        Bindings::iterator a =
            v.attrs->find(state.symbols.Create("derivation"));

        Path drvPath;
        a = v.attrs->find(state.sDrvPath);
        if (a != v.attrs->end()) {
          if (strict) {
            state.forceValue(*a->second.value);
          }
          if (a->second.value->type == tString) {
            xmlAttrs["drvPath"] = drvPath = a->second.value->string.s;
          }
        }

        a = v.attrs->find(state.sOutPath);
        if (a != v.attrs->end()) {
          if (strict) {
            state.forceValue(*a->second.value);
          }
          if (a->second.value->type == tString) {
            xmlAttrs["outPath"] = a->second.value->string.s;
          }
        }

        XMLOpenElement _(doc, "derivation", xmlAttrs);

        if (!drvPath.empty() && drvsSeen.find(drvPath) == drvsSeen.end()) {
          drvsSeen.insert(drvPath);
          showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
        } else {
          doc.writeEmptyElement("repeated");
        }
      }

      else {
        XMLOpenElement _(doc, "attrs");
        showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen);
      }

      break;

    case tList1:
    case tList2:
    case tListN: {
      XMLOpenElement _(doc, "list");
      for (unsigned int n = 0; n < v.listSize(); ++n) {
        printValueAsXML(state, strict, location, *v.listElems()[n], doc,
                        context, drvsSeen);
      }
      break;
    }

    case tLambda: {
      XMLAttrs xmlAttrs;
      if (location) {
        posToXML(xmlAttrs, v.lambda.fun->pos);
      }
      XMLOpenElement _(doc, "function", xmlAttrs);

      if (v.lambda.fun->matchAttrs) {
        XMLAttrs attrs;
        if (!v.lambda.fun->arg.empty()) {
          attrs["name"] = v.lambda.fun->arg;
        }
        if (v.lambda.fun->formals->ellipsis) {
          attrs["ellipsis"] = "1";
        }
        XMLOpenElement _(doc, "attrspat", attrs);
        for (auto& i : v.lambda.fun->formals->formals) {
          doc.writeEmptyElement("attr", singletonAttrs("name", i.name));
        }
      } else {
        doc.writeEmptyElement("varpat",
                              singletonAttrs("name", v.lambda.fun->arg));
      }

      break;
    }

    case tExternal:
      v.external->printValueAsXML(state, strict, location, doc, context,
                                  drvsSeen);
      break;

    case tFloat:
      doc.writeEmptyElement(
          "float", singletonAttrs("value", (format("%1%") % v.fpoint).str()));
      break;

    default:
      doc.writeEmptyElement("unevaluated");
  }
}

void ExternalValueBase::printValueAsXML(EvalState& state, bool strict,
                                        bool location, XMLWriter& doc,
                                        PathSet& context,
                                        PathSet& drvsSeen) const {
  doc.writeEmptyElement("unevaluated");
}

void printValueAsXML(EvalState& state, bool strict, bool location, Value& v,
                     std::ostream& out, PathSet& context) {
  XMLWriter doc(true, out);
  XMLOpenElement root(doc, "expr");
  PathSet drvsSeen;
  printValueAsXML(state, strict, location, v, doc, context, drvsSeen);
}

}  // namespace nix