convert the following code to rust

import * as childProcess from "child_process";
import { dicogs, pb, search } from "./utils";
import fs from "graceful-fs";
import * as NodeID3 from "node-id3";
import { Release } from "./interfaces";

enum error {
  RELEASE_NOT_FOUND = "RELEASE_NOT_FOUND",
  VIDEO_URL_NOT_FOUND = "VIDEO_URL_NOT_FOUND",
}

class Controller {
  index_name = "collections-v-0-2";
  bucket = "private-records";
  offset = 0;

  constructor() {}

  async getRelease(offset: number) {
    const releases = await search
      .index(this.index_name)
      .getDocuments({ limit: 1, offset });
    if (releases.results.length > 0) {
      return releases.results[0];
    }
    throw new Error(error.RELEASE_NOT_FOUND);
  }

  async getFullRelease(id: number) {
    const db = dicogs.database();
    return db.getRelease(id);
  }

  async getVideoUrl(release: any) {
    if (release && release.videos && release.videos.length > 0) {
      return release.videos[0].uri;
    }
    throw new Error(error.VIDEO_URL_NOT_FOUND);
  }

  async getImage(release: Release) {
    let url = release.images[0].resource_url;
    url = url ?? release.thumb;
    const db = dicogs.database();
    return await db.getImage(url);
  }

  async auth() {
    return await pb.admins.authWithPassword(
      "174yye4gt@mozmail.com",
      "zerfyz-nyxqUs-tutki3"
    );
  }

  async writeTags(release: Release, file: string, imageBuffer: Buffer) {
    const tags: NodeID3.Tags = {
      album: release.labels.map((l) => `${l.name} (${l.catno})`).join(","),
      composer: release.artists.map((a) => `${a.name}`).join(","),
      genre: release.genres.map((a) => `${a}`).join(","),
      title: release.title,
      year: `${release.year}`,
      image: {
        mime: "image/jpeg",
        description: "",
        type: {
          id: NodeID3.TagConstants.AttachedPicture.PictureType.FRONT_COVER,
        },
        imageBuffer,
      },
    };
    return await NodeID3.write(tags, file);
  }

  async downloadMp3(url: string, id: number, callback: () => {}) {
    // const cmd = `yt-dlp -U --extract-audio ${url}`;
    const cmd = `yt-dlp -o ./mp3/${id}.mp3 -f bestaudio --extract-audio --audio-format mp3 ${url}`;

    childProcess.exec(cmd, (error) => {
      if (error) {
        console.error(`Error downloading ${id}: ${error}`);
        return;
      }

      callback();
    });
  }

  //   "Martin Eyerer & Oliver Klein - Tiflis (My Best Friend (MBF 12031)",
  //   artists: [ "" ],
  //   labels: [ ""

  save(what) {
    fs.readFile("results.json", function (err, data) {
      var json = JSON.parse(data);
      json.push(what);
      fs.writeFile("results.json", JSON.stringify(json), function (err) {
        if (err) throw err;
        console.log('The "data to append" was appended to file!');
        setTimeout(async () => {
          try {
            await controller.run();
          } catch (error) {
            console.error("🚀 ~ error:", error);
            setTimeout(async () => {
              await controller.run();
            }, 10000);
          }
        }, 5000);
      });
    });
  }

  async run() {
    const random = Math.floor(Math.random() * 28000);
    controller.offset = random;

    const release = await this.getRelease(this.offset);
    const fullRelease = await this.getFullRelease(release.id);
    // console.log("🚀 ~ Controller ~ run ~ fullRelease:", fullRelease);

    if (fullRelease.videos.length == 0) {
      console.log("no video");
      await this.run();
      return;
    }

    const id = fullRelease.id;
    fullRelease.discogs_id = id;
    delete fullRelease.id;

    const video = await this.getVideoUrl(fullRelease);
    const image = await this.getImage(fullRelease);

    console.log("🚀 ~ run ~ video:", video);

    try {
      const downloaded = await this.downloadMp3(
        video.split("=")[1],
        release.id,
        async () => {
          const tags = await this.writeTags(
            fullRelease,
            `./mp3/${id}.mp3`,
            image
          );
          console.log("🚀 ~ writeTags:");
          this.save(tags);
        }
      );
    } catch (error) {
      console.error("🚀 ~ Controller ~ run ~ error:", error);
      await this.run();
    }
  }
}