{
  description = "QEMU QMP and Guest Agent API";
  inputs = {
    flakelib.url = "github:flakelib/fl";
    nixpkgs = { };
    schema = {
      flake = false;
      type = "github";
      owner = "arcnmx";
      repo = "qemu-qapi-filtered";
      ref = "v9.2.0-rc2"; # keep in sync with schema submodule
    };
    rust = {
      url = "github:arcnmx/nixexprs-rust";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };
  outputs = { self, schema, flakelib, nixpkgs, rust, ... }@inputs: let
    featureMatrix = rec {
      qmp = [ "qmp" ];
      qga = [ "qga" ];
      all = qmp ++ qga;
      async = all ++ [ "async-tower" ];
      tokio = all ++ [ "async-tokio-all" ];
    };
    nixlib = nixpkgs.lib;
    inherit (self.lib) crate;
    libs = nixlib.filterAttrs (_: crate: crate.package.publish or true) crate.members;
    testCrate = package: { buildFeatures ? [ ], ... }@args: with nixlib; let
      crate = self.lib.crate.members.${package};
      flags = [ "-p" crate.name ];
    in { rustPlatform, source }: rustPlatform.buildRustPackage (args // {
      pname = crate.name;
      inherit (crate) cargoLock version;
      src = source;
      cargoTestFlags = flags ++ args.cargoTestFlags or [ ];
      cargoBuildFlags = flags ++ args.cargoBuildFlags or [ ];
      buildType = "debug";
      meta = {
        name = let
          features = " --features ${concatStringsSep "," buildFeatures}";
          cmd = if args.doCheck or true then "test" else "build";
        in "cargo ${cmd} -p ${crate.name}" + optionalString (buildFeatures != [ ]) features;
      } // args.meta or { };
      auditable = false;
      passthru.ci = {
        cache.inputs = [ (rustPlatform.importCargoLock crate.cargoLock) ];
      };
    });
  in flakelib {
    inherit inputs;
    systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
    devShells = {
      plain = {
        mkShell, writeShellScriptBin
      , enableRust ? true, cargo
      , rustTools ? [ ]
      , generate
      }: mkShell {
        inherit rustTools;
        nativeBuildInputs = nixlib.optional enableRust cargo
          ++ [
            (writeShellScriptBin "generate" ''nix run .#generate "$@"'')
          ];
      };
      stable = { rust'stable, outputs'devShells'plain }: outputs'devShells'plain.override {
        inherit (rust'stable) mkShell;
        enableRust = false;
      };
      dev = { rust'unstable, rust-w64-overlay, outputs'devShells'plain }: let
        channel = rust'unstable.override {
          channelOverlays = [ rust-w64-overlay ];
        };
      in outputs'devShells'plain.override {
        inherit (channel) mkShell;
        enableRust = false;
        rustTools = [ "rust-analyzer" ];
      };
      default = { outputs'devShells }: outputs'devShells.plain;
    };
    packages = {
      examples = testCrate "examples" {
        doCheck = false;
      };
      examples-windows = { rust-w64, examples }: (examples.override {
        inherit (rust-w64.latest) rustPlatform;
      }).overrideAttrs (old: {
        meta = old.meta // {
          name = "cargo build --target ${rust-w64.latest.hostTarget.triple} -p qapi-examples";
        };
      });
      default = { examples }: examples;
    };
    legacyPackages = {
      # manual src fixup for submodule symlinks
      source = { runCommand }: runCommand crate.src.name {
        preferLocalBuild = true;
        inherit (crate) src;
        inherit (crate.src) pname version;
        inherit schema;
        readme = ./README.md;
      } ''
        mkdir $out
        cp --no-preserve=mode -rs $src/* $out/
        for api in qmp qga; do
          cp -frd $src/$api/schema/* $out/$api/schema/
        done
        ln -s $schema $out/schema
        ln -s $readme $out/README.md
      '';

      rust-w64 = { pkgsCross'mingwW64 }: import inputs.rust { inherit (pkgsCross'mingwW64) pkgs; };
      rust-w64-overlay = { rust-w64 }: let
        target = rust-w64.lib.rustTargetEnvironment {
          inherit (rust-w64) pkgs;
          rustcFlags = [ "-L native=${rust-w64.pkgs.windows.pthreads}/lib" ];
        };
      in cself: csuper: {
        sysroot-std = csuper.sysroot-std ++ [ cself.manifest.targets.${target.triple}.rust-std ];
        cargo-cc = csuper.cargo-cc // cself.context.rlib.cargoEnv {
          inherit target;
        };
        rustc-cc = csuper.rustc-cc // cself.context.rlib.rustcCcEnv {
          inherit target;
        };
      };

      generate = { rust'builders, outputHashes }: rust'builders.generateFiles {
        paths = {
          "lock.nix" = outputHashes;
        };
      };
      outputHashes = { rust'builders }: rust'builders.cargoOutputHashes {
        inherit crate;
      };
    };
    checks = with nixlib; {
      versions = { rust'builders, source }: rust'builders.check-contents {
        src = source;
        patterns = [
          { path = "README.md";
            plain = ''version = "${versions.majorMinor crate.members.qapi.version}"'';
          }
        ] ++ mapAttrsToList (dir: crate: {
          path = "${dir}/src/lib.rs";
          docs'rs = { inherit (crate) name version; };
        }) libs;
      };
    } // mapAttrs' (dir: crate: nameValuePair "test-${dir}" (testCrate dir { })) libs
    // mapAttrs' (name: buildFeatures: nameValuePair "test-qapi-${name}" (testCrate "qapi" {
      inherit buildFeatures;
    })) featureMatrix;
    lib = {
      crate = rust.lib.importCargo {
        inherit self;
        path = ./Cargo.toml;
        inherit (import ./lock.nix) outputHashes;
      };
      inherit (crate.package) version;
    };
    config = rec {
      name = "qapi-rs";
      packages.namespace = [ name ];
    };
  };
}