commit 3c0df12b506be75acbb35a6d9887f2717cacaa09 Author: mat ess Date: Mon Apr 17 18:22:35 2023 -0400 Initial version diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bc91cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.direnv +/result +/target +/.pre-commit-config.yaml + + +# Added by cargo +# +# already existing elements were commented out + +#/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..16aba8c --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cover" +version = "0.1.0" +dependencies = [ + "nanoserde", +] + +[[package]] +name = "nanoserde" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755e7965536bc54d7c9fba2df5ada5bf835b0443fd613f0a53fa199a301839d3" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7a94da6c6181c35d043fc61c43ac96d3a5d739e7b8027f77650ba41504d6ab" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..665409b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "cover" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nanoserde = "0.1.32" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0653ec0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,237 @@ +{ + "nodes": { + "crane": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1681680516, + "narHash": "sha256-EB8Adaeg4zgcYDJn9sR6UMjN/OHdIiMMK19+3LmmXQY=", + "owner": "ipetkov", + "repo": "crane", + "rev": "54b63c8eae4c50172cb50b612946ff1d2bc1c75c", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1680392223, + "narHash": "sha256-n3g7QFr85lDODKt250rkZj2IFS3i4/8HBU2yKHO3tqw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "dcc36e45d054d7bb554c9cdab69093debd91a0b5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1678901627, + "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1681648924, + "narHash": "sha256-pzi3HISK8+7mpEtv08Yr80wswyHKsz+RP1CROG1Qf6s=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f294325aed382b66c7a188482101b0f336d1d7db", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1680213900, + "narHash": "sha256-cIDr5WZIj3EkKyCgj/6j3HBH4Jj1W296z7HTcWj1aMA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e3652e0735fbec227f342712f180f4f21f0594f2", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1678872516, + "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1681413034, + "narHash": "sha256-/t7OjNQcNkeWeSq/CFLYVBfm+IEnkjoSm9iKvArnUUI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "d3de8f69ca88fb6f8b09e5b598be5ac98d28ede5", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "pre-commit": "pre-commit" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "crane", + "flake-utils" + ], + "nixpkgs": [ + "crane", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1680488274, + "narHash": "sha256-0vYMrZDdokVmPQQXtFpnqA2wEgCCUXf5a3dDuDVshn0=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "7ec2ff598a172c6e8584457167575b3a1a5d80d8", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e9648c0 --- /dev/null +++ b/flake.nix @@ -0,0 +1,70 @@ +{ + description = "FILL-ME-IN"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + crane.url = "github:ipetkov/crane"; + crane.inputs.nixpkgs.follows = "nixpkgs"; + pre-commit.url = "github:cachix/pre-commit-hooks.nix"; + pre-commit.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = inputs@{ self, flake-parts, crane, pre-commit, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ pre-commit.flakeModule ]; + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + "x86_64-linux" + ]; + perSystem = { config, pkgs, system, ... }: + let + crane-lib = crane.lib.${system}; + package = crane-lib.buildPackage { + src = crane-lib.cleanCargoSource ./.; + nativeBuildInputs = pkgs.lib.optionals pkgs.stdenv.isDarwin [ pkgs.libiconv ]; + }; + in + { + pre-commit.settings.hooks = { + rustfmt.enable = true; + clippy.enable = true; + cargo-check.enable = true; + }; + checks = { inherit package; }; + packages.default = package; + devShells.default = pkgs.mkShell { + shellHook = '' + ${config.pre-commit.installationScript} + echo "welcome to your rust project ミ(・・)ミ" 1>&2 + ''; + inputsFrom = builtins.attrValues self.checks; + + RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + nativeBuildInputs = builtins.attrValues ({ + inherit (pkgs) + cargo + rustc + ; + } // pkgs.lib.optionalAttrs pkgs.stdenv.isDarwin { + inherit (pkgs) libiconv; + inherit (pkgs.darwin.apple_sdk.frameworks) Security; + }); + buildInputs = builtins.attrValues { + inherit (pkgs) + rust-analyzer + # rustfmt + clippy + ; + rustfmt = pkgs.rustfmt.overrideAttrs (old: { + preFixup = pkgs.lib.optionalString pkgs.stdenv.isDarwin '' + install_name_tool -add_rpath "${pkgs.rustc}/lib" "$out/bin/rustfmt" + ''; + }); + }; + }; + }; + }; +} diff --git a/library.json b/library.json new file mode 100644 index 0000000..a4a1841 --- /dev/null +++ b/library.json @@ -0,0 +1,21 @@ +{ + "Taylor Swift": [ + { + "title": "Love Story", + "composer": "Taylor Swift", + "performer": "Taylor Swift" + } + ], + "black midi": [ + { + "title": "Love Story", + "composer": "Taylor Swift", + "performer": "black midi" + }, + { + "title": "Love Story", + "composer": "black midi", + "performer": "black midi" + } + ] +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9648012 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,88 @@ +use nanoserde::DeJson; +use std::{ + collections::{HashMap, HashSet}, + env, fs, + hash::Hash, +}; + +fn main() { + let file_path = env::args() + .nth(1) + .expect("Usage: cover path/to/library.json"); + let data = fs::read_to_string(file_path).expect("Failed to open library file"); + let json: HashMap> = + DeJson::deserialize_json(&data).expect("Failed to deserialize library"); + let library = Library::new(json); + for cover in library.find_covers() { + println!("Found cover!"); + println!( + "{}, by {}, covered by {}", + cover.title, cover.composer, cover.performer + ) + } +} + +#[derive(Debug, Clone)] +struct Library { + artists: HashSet, +} + +impl Library { + fn new(json: HashMap>) -> Self { + let artists = json + .into_iter() + .map(|(name, catalog)| Artist { + name, + catalog: catalog.into_iter().collect(), + }) + .collect(); + Library { artists } + } +} + +impl Library { + fn find_covers(&self) -> HashSet<&Song> { + let names = self + .artists + .iter() + .map(|artist| artist.name.clone()) + .collect::>(); + self.artists + .iter() + .fold(HashSet::new(), |mut covers, artist| { + covers.extend(artist.covered_songs()); + covers + }) + .into_iter() + .filter(|song| names.contains(&song.composer)) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Artist { + name: String, + catalog: HashSet, +} + +impl Hash for Artist { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl Artist { + fn covered_songs(&self) -> HashSet<&Song> { + self.catalog + .iter() + .filter(|song| song.composer != self.name) + .collect() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, DeJson)] +struct Song { + title: String, + performer: String, + composer: String, +}