← Memento lesson
Lab

Memento

Sign in to run your code

Read the brief and the starter here. With a free account you can edit it, run the sample, step through it and check your work, and your progress is kept.

// The memento: an empty token. Nothing is saved in it yet.
type PortCheckpoint = Readonly<object>;

// A switch port. vlan and allowed are its configuration, set by operators;
// link and rx are live, changed by the cable and the traffic.
class Port {
  name: string;
  vlan: number;
  allowed: number[];
  link = 'up'; // live: the cable changes it
  rx = 0; // live: packets received

  constructor(name: string, vlan: number, allowed: number[]) {
    this.name = name;
    this.vlan = vlan;
    this.allowed = [...allowed];
  }

  setVlan(vlan: number): void {
    this.vlan = vlan;
  }

  allow(vlan: number): void {
    if (!this.allowed.includes(vlan)) this.allowed.push(vlan);
  }

  disallow(vlan: number): void {
    const at = this.allowed.indexOf(vlan);
    if (at >= 0) this.allowed.splice(at, 1);
  }

  receive(packets: number): void {
    this.rx += packets;
  }

  setLink(state: string): void {
    this.link = state;
  }

  // Not written yet: the history copies the port's fields itself (below).
  capture(): PortCheckpoint {
    return Object.freeze({});
  }

  restore(token: PortCheckpoint): void {
    // Not written yet: nothing to put back.
  }
}

// The console's rollback history. It was written when a port had only a
// VLAN: it saves the port as it was and writes it back.
type Entry = { label: string; vlan: number; link: string; rx: number };

class RollbackHistory {
  private entries: Entry[] = [];

  checkpoint(port: Port, label: string): void {
    this.entries.push({ label, vlan: port.vlan, link: port.link, rx: port.rx });
  }

  rollback(port: Port, label: string): void {
    const entry = this.entries.filter((e) => e.label === label).pop();
    if (!entry) throw new Error('no checkpoint');
    port.vlan = entry.vlan;
    port.link = entry.link;
    port.rx = entry.rx;
  }
}