///|
/// The residue as the wire carries it.
pub fn Residue::to_json(self : Residue) -> Json {
  {
    "locus": Json::string(self.locus),
    "tier": Json::string(self.tier),
    "session_short_id": Json::string(self.session_short_id),
    "timestamp": Json::string(self.timestamp),
    "picked_up_from": or_null(self.picked_up_from),
    "what_i_did": strings_to_json(self.what_i_did),
    "what_i_left_open": strings_to_json(self.what_i_left_open),
    "recommendation": or_null(self.recommendation),
  }
}

///|
/// The locus as the wire carries it.
pub fn Locus::to_json(self : Locus) -> Json {
  {
    "name": Json::string(self.name),
    "spirit": Json::string(self.spirit),
    "tags": strings_to_json(self.tags),
    "trail_depth": Json::number(self.trail_depth.to_double()),
    "last_visit": match self.last_visit {
      Some(residue) => residue.to_json()
      None => Json::null()
    },
  }
}

///|
/// The trail as the wire carries it.
pub fn Trail::to_json(self : Trail) -> Json {
  {
    "locus": Json::string(self.locus),
    "sessions": Json::array(self.sessions.map(Residue::to_json)),
  }
}

///|
/// The event as one line's worth of JSON: its `"event"` name plus its payload,
/// flat.
///
/// This is the wire shape and the only place it is written. Two writers of one
/// event must not spell it two ways.
pub fn Event::to_json(self : Event) -> Json {
  match self {
    Ready(version~, engine~) =>
      {
        "event": Json::string("ready"),
        "version": Json::number(version.to_double()),
        "engine": Json::string(engine),
      }
    Entered(locus~, trail~) =>
      {
        "event": Json::string("entered"),
        "locus": locus.to_json(),
        "trail": trail.to_json(),
      }
    TrailListed(trail~) =>
      { "event": Json::string("trail_listed"), "trail": trail.to_json() }
    Located(loci~) =>
      {
        "event": Json::string("located"),
        "loci": Json::array(loci.map(Locus::to_json)),
      }
    ResidueFiled(residue~) =>
      { "event": Json::string("residue_filed"), "residue": residue.to_json() }
    Sealed(epoch~, root~) =>
      {
        "event": Json::string("sealed"),
        "epoch": Json::number(epoch.to_double()),
        "root": Json::string(root),
      }
    Stored(digest~, disposition~) =>
      {
        "event": Json::string("stored"),
        "digest": Json::string(digest),
        "disposition": Json::string(disposition),
      }
    Diffed(added~, removed~, changed~, unchanged~) =>
      {
        "event": Json::string("diffed"),
        "added": Json::number(added.to_double()),
        "removed": Json::number(removed.to_double()),
        "changed": Json::number(changed.to_double()),
        "unchanged": Json::number(unchanged.to_double()),
      }
    Diagnostic(severity~, message~) =>
      {
        "event": Json::string("diagnostic"),
        "severity": Json::string(severity.token()),
        "message": Json::string(message),
      }
    Finished(code~) =>
      {
        "event": Json::string("finished"),
        "code": Json::number(code.to_double()),
      }
  }
}

///|
/// The command as one line's worth of JSON.
pub fn Command::to_json(self : Command) -> Json {
  match self {
    Enter(locus~) =>
      { "command": Json::string("enter"), "locus": Json::string(locus) }
    Trail(locus~) =>
      { "command": Json::string("trail"), "locus": Json::string(locus) }
    Locate => { "command": Json::string("locate") }
    Sign(residue~) =>
      { "command": Json::string("sign"), "residue": residue.to_json() }
    Interrupt => { "command": Json::string("interrupt") }
    Shutdown => { "command": Json::string("shutdown") }
  }
}