Flake support/frontend (#324)
* Setup flake info extraction Prepare data model fro derivations (#1) Add flake info data (#1) Implement fetching general flake info (#1) Expose CLI (#1) Keep cargo happy Add some doc comments Pin to local nixpkgs to excessive downloads Extend visibility of some data objects Add command to extract infomation about defivations (#1) Add call new feature in main (#1) Include more information in derivation (#1) Add log access Always debug log stderr from nix Format nix script Collect systems per package Remove unnecessary imports Create flake Remove top level version field Represent collected systems/version pairs in rust Fix quotation marks in tests Add correct cargo hash Add iconv dependency Return a list from nix script Export as json Undo version by platform distinction Remove nixpkgs override Apply cargo fmt Flatten export structure Allow for complex licenses Prepare using a central nix file Implement nix part o accessing apps Include the correct filename Add accessor for `all` key Access all available information by default Track more information about Apps Run cargo fmt Fix: allow local builds Prepare next version of the flake info tool Include examples and pull script Expose flake info as library Include thiserror for custom errors Define a source data type Collects source types and their metadata, collected in a json file Add command line argument for input files Mutually exclusive with --flake Refactor functions to extract information given a flake identifier Add kind specifier as CLI argument Amend Argument parsing to require eiteher flake or targets to be defined Run extraction for specified flake or list of flakes as specified in a json file Resolves #5 References #7 Use internal tag to distnguich target types Include target falg usage in examples Set include provided source if available (resolves #9) Resolve flake name Update examples Dont include empty license or description Fix a misfomatting in cargot.toml Add elastic dependencies Implement a wrapper around the elasticsearch client Implements pushing exports (#4) Temporarily skip serializing an unimplemented field in elastic output Extract reading source list files from binary Add lazy_static as dependency Implement createing and pushing to elastic index Add elastic options Provide default name and env falbac for elastic index Modify app binary and type as optionals App can be a derivation too Update examples Add more elastic commands Supported: - ensure - clear - push Rename elastic search config struct Add elastic push support to binary Rename flag to enable elastic push Imporve error messages and format binary source Fix nix file incorrectly expecting meta fields Changing flake descriotions to an optional field deserialize git_ref as hash Implement temporary stores and gc of these prevents flakes from accessing store paths Pass extra arguments to nix Update cargo hash and skip integration tests Move flake.nix to root folder and add apps for all components Fix command invocation that fails test Update README(s) Add help for extra arguments (cherry picked from commit be4bc3dd929178bef66114c2201aaa88e47e9add) * Safely read legacyPackages * Read nixosOptions from flake * Update ES Mapping * Show more detailed error and backtrace if available * Try reading options only if key is defined * Format nix script * Add error context when attempting deserialization * Fix derivation representation to fit nix output * Add push elasticsearch settings * Add Flake channel * Rename import module * Remove Flakes Channel * Prepare nixpkgs import * Separate import/export types * Break up nixpkgs package representation * Use the same naming scheme for Nixpkgs entries and flake entries * Document import module * Remove serialization attributes * Reversable type and SerDe implemetation * Add *_reverse fields * Unpublicating export fields * Read from NixOption struct * serialize empty fields as null * Tag export json variants * Serialize a single option-declaration * Format npkgs parse test * Define NixOption Sorry thats too late.. * Parse system key * Make Package output compatible with the frontend * Add Url-only licesnse variant * Add StringOrStruct type * Add accessor method for elements catched by OneOrMany * New utility to flatten in homogenous lists recursively * Add Maintainer type catching maintaiers used in nixpkgs * Format Implementatio n * Remove explicit representation of platforms * Open nixpkgs parser to cover all packages * Convert all imported fields to their export representation * Define reverse fields in ES schema * Format nixpkgs command runner * Expose shorthand to pull a specific channel by command line * Extract utility functions into their own module * Implement AttributeQuery generation * Integrate query in export * Implement *_set attributes * Document purpose of export module * Use more descriptive github blob route * Complete Option Export representation Reuse the same option type for nixpkgs and flakes * Enable nixpkgs option import * Expose nixpkgs option import functions and integrate in binary * Chunk ElasticSearch Bulk operations * Address Example/Default field formatting * Add abort strategy for existing indices * New command line interface using subcommands * Document new interface * Bump version * Add nixpkgs cron job * More precise name for nixpkgs cron job * Add Flakes cron job * Read version from file and fix channel names * Correct file names for flake group import * Fix group command * Run new cron jobs on PR * Update Cargo sources * Integrate new flakes route * Add search type option * Add flake types and messages * Add flake types * Extract Request body builders and expose more types * Combine Package and Option Search * Factor out html body * Dispatch messages by search type to flake page * Correct type naming * Remove Debug instructions and unimplemented flake search type * Do not reload Flake search page while the search subject is unchanged * Implement switching subjects * Fix init type signature * Add url/git based flakes to mapping * Parse flake info * Link to flake repo and show flake maintainer * Fix optional decoded values * Add group and type as search buckts * Show search selection in all cases * Move flake decoding to search * Show flake information for options * Hardcode flakesearch to only search a specified flake index * Improve experimental state notice * Fix category select not present in some cases * Change default flake index * Show correct category title * Add missing imports * Serialize more optional fields as null * elastic-test.rs file not needed anymore * names of the workflows should be immediately obvious. * better jobs names * rename the flakes group * need to provide --elastic-schema-version via the cli option * typo * Fix errors in workflow files * Install flake enabled nix by default * Fix variable substitution * Use string group names * Provide elastic schema verion only through cli * Fix scheduled task name * Improve error reporting * Allow insecure packages here * Add missing imports * Tree-wide: cargo-fmt * only import nixpkgs for now * no importing of flakes * also bump a version * fixing cron-flakes.yml * make it obvious that this is for frontend * missed this when merging * Extract hashes - Split out revision info from flakes - Retrieve current nixpkgs revision from github * Write aliases, enabled by default * Filter additional platforms * Change alias structure * expect channel like nixpkgs identifiers * Don't cause error if push is aborted * Specify correct channel identifier * Allow options to evaluate unfree packages * Retrieve and delete specific aliases * Specify import path correclty * Fix channel warmup * Abort push if channel already indexed * Remove debug pr hook for import action * Fix indentation by tab * Make flakes show again * Fix import group naming in flake wrokflow * Rename flake group to match imported index * Run nixpkgs import on pr activity (Debugging behavior) * Just show literal Examples, resolves #336 * Use actual nixpkgs branch names * Trim derivation/option declaration path, resolves #335 Remove /nix/store/*-source prefix * Fix sidebar width and close button position * Placeholder texts in flake result area * Show flake install info * Don't show package/option selection before search * Make sure install command for nixos is always shown * Add toml source config support * Rewrite current example flake group as toml * Update flake cron job * flake-info: use saner nix packaging method When trying to first work in this I naive approached it with `nix-shell`. That of course lead to the fixed output bollocks failing with a hash mismatch. By making use of the `cargoLock` attribute on `buildRustPackage` we can tame the FOD-beast and only have to provide one hash manually for a single package (that we fetch from a GitHub repository). This also means that updating dependencies will be simpler as the native Cargo.lock file can be used. (cherry picked from commit c3a0e46d1eb56e128e6923e6c493eb836fc81e85) * Update flake lock file * Do not build python import script * add flake names to the title as well * Disable debug imports on prs Co-authored-by: Rok Garbas <rok@garbas.si> Co-authored-by: Andreas Rammhold <andreas@rammhold.de>
This commit is contained in:
parent
5ad71362e5
commit
af3b494217
69
.github/workflows/cron-flakes.yml
vendored
Normal file
69
.github/workflows/cron-flakes.yml
vendored
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
name: "Flakes: Hourly import to Elasticsearch"
|
||||||
|
|
||||||
|
on:
|
||||||
|
|
||||||
|
schedule:
|
||||||
|
- cron: '0 * * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
hourly-import-channel:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
group:
|
||||||
|
- "manual"
|
||||||
|
|
||||||
|
env:
|
||||||
|
RUST_LOG: debug
|
||||||
|
FI_ES_EXISTS_STRATEGY: recreate
|
||||||
|
FI_ES_URL: ${{ secrets.ELASTICSEARCH_URL }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checking out the repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Installing Nix
|
||||||
|
uses: cachix/install-nix-action@v13
|
||||||
|
with:
|
||||||
|
install_url: https://nixos-nix-install-tests.cachix.org/serve/i6laym9jw3wg9mw6ncyrk6gjx4l34vvx/install
|
||||||
|
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
|
||||||
|
extra_nix_config: |
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: cachix/cachix-action@v10
|
||||||
|
with:
|
||||||
|
name: nixos-search
|
||||||
|
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
|
||||||
|
|
||||||
|
- name: Install unstable channel
|
||||||
|
run: |
|
||||||
|
nix-channel --add https://nixos.org/channels/nixpkgs-unstable
|
||||||
|
nix-channel --update
|
||||||
|
|
||||||
|
- name: Installing jq
|
||||||
|
run: |
|
||||||
|
nix-env -iA nixpkgs.nixFlakes nixpkgs.jq
|
||||||
|
|
||||||
|
- name: Building import_scripts
|
||||||
|
run: |
|
||||||
|
nix build ./#packages.x86_64-linux.flake_info
|
||||||
|
|
||||||
|
- name: Import ${{ matrix.group }} group
|
||||||
|
run: |
|
||||||
|
./result/bin/flake-info --push --elastic-schema-version=$(cat ./VERSION) group ./flakes/${{ matrix.group }}.toml ${{ matrix.group }}
|
||||||
|
if: github.repository == 'NixOS/nixos-search'
|
||||||
|
|
||||||
|
- name: Warmup ${{ matrix.group }} channel
|
||||||
|
run: |
|
||||||
|
curl ${{ secrets.ELASTICSEARCH_URL }}/$(cat VERSION)-${{ matrix.group }}/_search | jq '.took'
|
||||||
|
curl ${{ secrets.ELASTICSEARCH_URL }}/$(cat VERSION)-${{ matrix.group }}/_search | jq '.took'
|
||||||
|
curl ${{ secrets.ELASTICSEARCH_URL }}/$(cat VERSION)-${{ matrix.group }}/_search | jq '.took'
|
||||||
|
if: github.repository == 'NixOS/nixos-search'
|
1
.github/workflows/cron-nixpkgs.yml
vendored
1
.github/workflows/cron-nixpkgs.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 * * * *'
|
- cron: '0 * * * *'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
|
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: "Build & Deploy to Netlify"
|
name: "Frontend: Build & Deploy to Netlify"
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
|
@ -34,10 +34,6 @@ jobs:
|
||||||
cat /etc/nix/nix.conf
|
cat /etc/nix/nix.conf
|
||||||
echo "$HOME/.nix-profile/bin" >> $GITHUB_PATH
|
echo "$HOME/.nix-profile/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Building import_scripts
|
|
||||||
run: |
|
|
||||||
nix build ./#packages.x86_64-linux.import_scripts
|
|
||||||
|
|
||||||
- name: Building search.nixos.org
|
- name: Building search.nixos.org
|
||||||
run: |
|
run: |
|
||||||
nix build ./#packages.x86_64-linux.frontend
|
nix build ./#packages.x86_64-linux.frontend
|
||||||
|
|
10
flake-info/Cargo.lock
generated
10
flake-info/Cargo.lock
generated
|
@ -308,6 +308,7 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1455,6 +1456,15 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
|
@ -10,6 +10,7 @@ edition = "2018"
|
||||||
clap = "^2.33"
|
clap = "^2.33"
|
||||||
serde = {version="1.0", features = ["derive"]}
|
serde = {version="1.0", features = ["derive"]}
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
toml = "0.5"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }: with pkgs;
|
{ pkgs ? import <nixpkgs> { } }: with pkgs;
|
||||||
|
rustPlatform.buildRustPackage {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rustPlatform.buildRustPackage rec {
|
|
||||||
name = "flake-info";
|
name = "flake-info";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoSha256 = "sha256-TA1WEvmOfnxQ+rRwkIPN1t4VPrDL6pq+WnPHVu2/CPE=";
|
cargoLock = {
|
||||||
|
lockFile = ./Cargo.lock;
|
||||||
|
outputHashes = {
|
||||||
|
"elasticsearch-8.0.0-alpha.1" = "0x8iw4m16vy6i28mj30aqdwfw4a3hd174l8l9yigddn3cr53cagx";
|
||||||
|
};
|
||||||
|
};
|
||||||
nativeBuildInputs = [ pkg-config ];
|
nativeBuildInputs = [ pkg-config ];
|
||||||
buildInputs = [ openssl openssl.dev ] ++ lib.optional pkgs.stdenv.isDarwin [libiconv darwin.apple_sdk.frameworks.Security];
|
buildInputs = [ openssl openssl.dev ] ++ lib.optional pkgs.stdenv.isDarwin [ libiconv darwin.apple_sdk.frameworks.Security ];
|
||||||
checkFlags = [
|
checkFlags = [
|
||||||
"--skip elastic::tests"
|
"--skip elastic::tests"
|
||||||
"--skip nix_gc::tests"
|
"--skip nix_gc::tests"
|
||||||
|
|
|
@ -57,7 +57,7 @@ enum Command {
|
||||||
channel: String,
|
channel: String,
|
||||||
},
|
},
|
||||||
Group {
|
Group {
|
||||||
#[structopt(help = "Points to a JSON file containing info targets")]
|
#[structopt(help = "Points to a TOML or JSON file containing info targets. If file does not end in 'toml' json is assumed")]
|
||||||
targets: PathBuf,
|
targets: PathBuf,
|
||||||
|
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -217,7 +217,7 @@ async fn run_command(
|
||||||
.await
|
.await
|
||||||
.map_err(FlakeInfoError::Nixpkgs)?;
|
.map_err(FlakeInfoError::Nixpkgs)?;
|
||||||
let ident = (
|
let ident = (
|
||||||
"nixpkgs".to_owned(),
|
"nixos".to_owned(),
|
||||||
nixpkgs.channel.clone(),
|
nixpkgs.channel.clone(),
|
||||||
nixpkgs.git_ref.clone(),
|
nixpkgs.git_ref.clone(),
|
||||||
);
|
);
|
||||||
|
@ -335,7 +335,6 @@ async fn push_to_elastic(
|
||||||
ensure?;
|
ensure?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
es.push_exports(&config, successes)
|
es.push_exports(&config, successes)
|
||||||
.await
|
.await
|
||||||
.with_context(|| "Failed to push results to elasticsearch".to_string())?;
|
.with_context(|| "Failed to push results to elasticsearch".to_string())?;
|
||||||
|
|
|
@ -73,14 +73,17 @@ let
|
||||||
cleanUpOption = module: opt:
|
cleanUpOption = module: opt:
|
||||||
let
|
let
|
||||||
applyOnAttr = n: f: lib.optionalAttrs (lib.hasAttr n opt) { ${n} = f opt.${n}; };
|
applyOnAttr = n: f: lib.optionalAttrs (lib.hasAttr n opt) { ${n} = f opt.${n}; };
|
||||||
# mkDeclaration = decl: rec {
|
mkDeclaration = decl:
|
||||||
# path = stripModulePathPrefixes decl;
|
let
|
||||||
# url = mkModuleUrl path;
|
discard = lib.concatStringsSep "/" (lib.take 4 (lib.splitString "/" decl));
|
||||||
# channelPath = "${channelName}/${path}";
|
path = if lib.hasPrefix builtins.storeDir decl then lib.removePrefix discard decl else decl;
|
||||||
# };
|
in
|
||||||
|
path;
|
||||||
|
|
||||||
# Replace functions by the string <function>
|
# Replace functions by the string <function>
|
||||||
substFunction = x:
|
substFunction = x:
|
||||||
if builtins.isAttrs x then
|
if x ? _type && x._type == "literalExample" then x.text
|
||||||
|
else if builtins.isAttrs x then
|
||||||
lib.mapAttrs (name: substFunction) x
|
lib.mapAttrs (name: substFunction) x
|
||||||
else if builtins.isList x then
|
else if builtins.isList x then
|
||||||
map substFunction x
|
map substFunction x
|
||||||
|
@ -93,15 +96,15 @@ let
|
||||||
// applyOnAttr "example" substFunction
|
// applyOnAttr "example" substFunction
|
||||||
// applyOnAttr "default" substFunction
|
// applyOnAttr "default" substFunction
|
||||||
// applyOnAttr "type" substFunction
|
// applyOnAttr "type" substFunction
|
||||||
|
// applyOnAttr "declarations" (map mkDeclaration)
|
||||||
// lib.optionalAttrs (!isNixOS) { flake = [ flake module ]; };
|
// lib.optionalAttrs (!isNixOS) { flake = [ flake module ]; };
|
||||||
# // applyOnAttr "declarations" (map mkDeclaration)
|
|
||||||
|
|
||||||
|
|
||||||
options = lib.mapAttrs (
|
options = lib.mapAttrs (
|
||||||
attr: module: let
|
attr: module: let
|
||||||
list = lib.optionAttrSetToDocList (declarations module);
|
list = lib.optionAttrSetToDocList (declarations module);
|
||||||
in
|
in
|
||||||
map (cleanUpOption attr) (lib.filter (x: !x.internal) list )
|
map (cleanUpOption attr) (lib.filter (x: !x.internal) list)
|
||||||
) modules;
|
) modules;
|
||||||
in
|
in
|
||||||
lib.flatten (builtins.attrValues options);
|
lib.flatten (builtins.attrValues options);
|
||||||
|
|
|
@ -217,6 +217,14 @@ impl From<import::NixpkgsEntry> for Derivation {
|
||||||
.map(|m| m.name.to_owned().unwrap())
|
.map(|m| m.name.to_owned().unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let position: Option<String> = package.meta.position.map(|p| {
|
||||||
|
if p.starts_with("/nix/store") {
|
||||||
|
p.split("/").skip(4).collect::<Vec<&str>>().join("/")
|
||||||
|
} else {
|
||||||
|
p
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Derivation::Package {
|
Derivation::Package {
|
||||||
package_attr_name: attribute.clone(),
|
package_attr_name: attribute.clone(),
|
||||||
package_attr_name_reverse: Reverse(attribute.clone()),
|
package_attr_name_reverse: Reverse(attribute.clone()),
|
||||||
|
@ -247,7 +255,7 @@ impl From<import::NixpkgsEntry> for Derivation {
|
||||||
.meta
|
.meta
|
||||||
.homepage
|
.homepage
|
||||||
.map_or(Default::default(), OneOrMany::into_list),
|
.map_or(Default::default(), OneOrMany::into_list),
|
||||||
package_position: package.meta.position,
|
package_position: position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
import::NixpkgsEntry::Option(option) => option.into(),
|
import::NixpkgsEntry::Option(option) => option.into(),
|
||||||
|
|
|
@ -7,10 +7,7 @@ use super::Source;
|
||||||
/// Holds general infoamtion about a flake
|
/// Holds general infoamtion about a flake
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Flake {
|
pub struct Flake {
|
||||||
#[serde(
|
#[serde(rename(serialize = "flake_description"))]
|
||||||
rename(serialize = "flake_description"),
|
|
||||||
skip_serializing_if = "Option::is_none"
|
|
||||||
)]
|
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
#[serde(rename(serialize = "flake_path"), skip_serializing)]
|
#[serde(rename(serialize = "flake_path"), skip_serializing)]
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
|
|
@ -2,7 +2,9 @@ use anyhow::{Context, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
|
io::Read,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
ffi::OsStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Hash = String;
|
pub type Hash = String;
|
||||||
|
@ -31,6 +33,12 @@ pub enum Source {
|
||||||
Nixpkgs(Nixpkgs),
|
Nixpkgs(Nixpkgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
struct TomlDocument {
|
||||||
|
sources: Vec<Source>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Source {
|
impl Source {
|
||||||
pub fn to_flake_ref(&self) -> FlakeRef {
|
pub fn to_flake_ref(&self) -> FlakeRef {
|
||||||
match self {
|
match self {
|
||||||
|
@ -68,9 +76,19 @@ impl Source {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_sources_file(path: &Path) -> Result<Vec<Source>> {
|
pub fn read_sources_file(path: &Path) -> Result<Vec<Source>> {
|
||||||
let file = File::open(path).with_context(|| "Failed to open input file")?;
|
|
||||||
|
|
||||||
Ok(serde_json::from_reader(file)?)
|
let mut file = File::open(path).with_context(|| "Failed to open input file")?;
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
file.read_to_string(&mut buf)?;
|
||||||
|
|
||||||
|
if path.extension() == Some(OsStr::new("toml")) {
|
||||||
|
let document: TomlDocument = toml::from_str(&buf)?;
|
||||||
|
Ok(document.sources)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(serde_json::from_str(&buf)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn nixpkgs(channel: String) -> Result<Nixpkgs> {
|
pub async fn nixpkgs(channel: String) -> Result<Nixpkgs> {
|
||||||
|
|
|
@ -34,6 +34,9 @@ lazy_static! {
|
||||||
"repo": {
|
"repo": {
|
||||||
"type": "keyword"
|
"type": "keyword"
|
||||||
},
|
},
|
||||||
|
"url" : {
|
||||||
|
"type": "keyword"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake_source": {
|
"flake_source": {
|
||||||
|
|
12
flake.lock
12
flake.lock
|
@ -17,11 +17,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1616779317,
|
"lastModified": 1629618782,
|
||||||
"narHash": "sha256-+mUTkYguFMNGb57JkwauDgjcq65RnOLUhDo4mhb8qAI=",
|
"narHash": "sha256-2K8SSXu3alo/URI3MClGdDSns6Gb4ZaW4LET53UWyKk=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ad47284f8b01f587e24a4f14e0f93126d8ebecda",
|
"rev": "870959c7fb3a42af1863bed9e1756086a74eb649",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -51,11 +51,11 @@
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1617110670,
|
"lastModified": 1629413283,
|
||||||
"narHash": "sha256-+GoJjy1Hlpvs2dFjxJsWHur4Jb8gT0Lig3y2MwRn3cI=",
|
"narHash": "sha256-DeyaGvtO/Nureis423Vu4s/X7r0I/h6/ELJLr0sHy2w=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "poetry2nix",
|
"repo": "poetry2nix",
|
||||||
"rev": "19bfde46bb9ba980142f2ca99268ef14df8cad5c",
|
"rev": "f0ef3fee8053eb32207761d6d708e217e2aa3d02",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
29
flakes/manual.toml
Normal file
29
flakes/manual.toml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[[sources]]
|
||||||
|
type = "github"
|
||||||
|
owner = "ngi-nix"
|
||||||
|
repo = "offen"
|
||||||
|
|
||||||
|
[[sources]]
|
||||||
|
type = "github"
|
||||||
|
owner = "ngi-nix"
|
||||||
|
repo = "pixelfed"
|
||||||
|
|
||||||
|
[[sources]]
|
||||||
|
type = "github"
|
||||||
|
owner = "ngi-nix"
|
||||||
|
repo = "lightmeter"
|
||||||
|
|
||||||
|
[[sources]]
|
||||||
|
type = "github"
|
||||||
|
owner = "ngi-nix"
|
||||||
|
repo = "openpgp-ca"
|
||||||
|
|
||||||
|
[[sources]]
|
||||||
|
type = "gitlab"
|
||||||
|
owner = "pi-lar"
|
||||||
|
repo = "neuropil"
|
||||||
|
|
||||||
|
[[sources]]
|
||||||
|
type = "github"
|
||||||
|
owner = "tweag"
|
||||||
|
repo = "nickel"
|
67
src/Main.elm
67
src/Main.elm
|
@ -26,13 +26,17 @@ import Html.Attributes
|
||||||
, src
|
, src
|
||||||
, type_
|
, type_
|
||||||
)
|
)
|
||||||
|
import Page.Flakes exposing (Model(..))
|
||||||
import Page.Home
|
import Page.Home
|
||||||
import Page.Options
|
import Page.Options
|
||||||
import Page.Packages
|
import Page.Packages
|
||||||
import Page.Flakes
|
import Route exposing (SearchType(..))
|
||||||
import Route
|
|
||||||
import Search
|
import Search
|
||||||
import Url
|
import Url
|
||||||
|
import Search exposing (defaultFlakeId)
|
||||||
|
import Search exposing (channels)
|
||||||
|
import Html exposing (sup)
|
||||||
|
import Html exposing (small)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,6 +131,7 @@ attemptQuery (( model, _ ) as pair) =
|
||||||
, Cmd.map msg <|
|
, Cmd.map msg <|
|
||||||
makeRequest
|
makeRequest
|
||||||
model.elasticsearch
|
model.elasticsearch
|
||||||
|
searchModel.searchType
|
||||||
searchModel.channel
|
searchModel.channel
|
||||||
(Maybe.withDefault "" searchModel.query)
|
(Maybe.withDefault "" searchModel.query)
|
||||||
searchModel.from
|
searchModel.from
|
||||||
|
@ -140,19 +145,40 @@ attemptQuery (( model, _ ) as pair) =
|
||||||
case model.page of
|
case model.page of
|
||||||
Packages searchModel ->
|
Packages searchModel ->
|
||||||
if Search.shouldLoad searchModel then
|
if Search.shouldLoad searchModel then
|
||||||
submitQuery PackagesMsg Page.Packages.makeRequest searchModel
|
submitQuery PackagesMsg Page.Packages.makeRequest { searchModel | searchType = PackageSearch }
|
||||||
|
|
||||||
else
|
else
|
||||||
noEffects pair
|
noEffects pair
|
||||||
|
|
||||||
Options searchModel ->
|
Options searchModel ->
|
||||||
if Search.shouldLoad searchModel then
|
if Search.shouldLoad searchModel then
|
||||||
submitQuery OptionsMsg Page.Options.makeRequest searchModel
|
submitQuery OptionsMsg Page.Options.makeRequest { searchModel | searchType = OptionSearch }
|
||||||
|
|
||||||
else
|
else
|
||||||
noEffects pair
|
noEffects pair
|
||||||
|
|
||||||
|
Flakes (OptionModel searchModel) ->
|
||||||
|
if Search.shouldLoad searchModel then
|
||||||
|
submitQuery FlakesMsg Page.Flakes.makeRequest {searchModel | channel = defaultFlakeId }
|
||||||
|
|
||||||
|
else
|
||||||
|
noEffects pair
|
||||||
|
|
||||||
|
Flakes (PackagesModel searchModel) ->
|
||||||
|
if Search.shouldLoad searchModel then
|
||||||
|
-- let
|
||||||
|
-- _ = Debug.log "main" "submit flake message"
|
||||||
|
-- in
|
||||||
|
submitQuery FlakesMsg Page.Flakes.makeRequest {searchModel | channel = defaultFlakeId}
|
||||||
|
|
||||||
|
else
|
||||||
|
-- let _ = Debug.log "main" "should not load flakes" in
|
||||||
|
noEffects pair
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
-- let
|
||||||
|
-- _ = Debug.log "pair" <| Debug.toString pair
|
||||||
|
-- in
|
||||||
pair
|
pair
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,6 +197,12 @@ pageMatch m1 m2 =
|
||||||
( Options _, Options _ ) ->
|
( Options _, Options _ ) ->
|
||||||
True
|
True
|
||||||
|
|
||||||
|
( Flakes (OptionModel _), Flakes (OptionModel _) ) ->
|
||||||
|
True
|
||||||
|
|
||||||
|
( Flakes (PackagesModel _), Flakes (PackagesModel _) ) ->
|
||||||
|
True
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
False
|
False
|
||||||
|
|
||||||
|
@ -239,6 +271,7 @@ changeRouteTo currentModel url =
|
||||||
|
|
||||||
Route.Flakes searchArgs ->
|
Route.Flakes searchArgs ->
|
||||||
let
|
let
|
||||||
|
-- _ = Debug.log "changeRouteTo" "flakes"
|
||||||
modelPage =
|
modelPage =
|
||||||
case model.page of
|
case model.page of
|
||||||
Flakes x ->
|
Flakes x ->
|
||||||
|
@ -255,6 +288,8 @@ changeRouteTo currentModel url =
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
|
-- let _ = Debug.log "main" "update"
|
||||||
|
-- in
|
||||||
case ( msg, model.page ) of
|
case ( msg, model.page ) of
|
||||||
( ClickedLink urlRequest, _ ) ->
|
( ClickedLink urlRequest, _ ) ->
|
||||||
case urlRequest of
|
case urlRequest of
|
||||||
|
@ -289,6 +324,10 @@ update msg model =
|
||||||
Page.Options.update model.navKey subMsg subModel
|
Page.Options.update model.navKey subMsg subModel
|
||||||
|> updateWith Options OptionsMsg model
|
|> updateWith Options OptionsMsg model
|
||||||
|
|
||||||
|
( FlakesMsg subMsg, Flakes subModel ) ->
|
||||||
|
Page.Flakes.update model.navKey subMsg subModel
|
||||||
|
|> updateWith Flakes FlakesMsg model
|
||||||
|
|
||||||
( _, _ ) ->
|
( _, _ ) ->
|
||||||
-- Disregard messages that arrived for the wrong page.
|
-- Disregard messages that arrived for the wrong page.
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
@ -330,7 +369,7 @@ view model =
|
||||||
[ a [ class "brand", href "https://nixos.org" ]
|
[ a [ class "brand", href "https://nixos.org" ]
|
||||||
[ img [ src "https://nixos.org/logo/nix-wiki.png", class "logo" ] []
|
[ img [ src "https://nixos.org/logo/nix-wiki.png", class "logo" ] []
|
||||||
]
|
]
|
||||||
, div [ ]
|
, div []
|
||||||
[ ul [ class "nav pull-left" ]
|
[ ul [ class "nav pull-left" ]
|
||||||
(viewNavigation model.route)
|
(viewNavigation model.route)
|
||||||
]
|
]
|
||||||
|
@ -375,26 +414,30 @@ viewNavigation route =
|
||||||
Route.Options searchArgs ->
|
Route.Options searchArgs ->
|
||||||
f searchArgs
|
f searchArgs
|
||||||
|
|
||||||
|
Route.Flakes searchArgs ->
|
||||||
|
f searchArgs
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
f <| Route.SearchArgs Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
f <| Route.SearchArgs Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||||
in
|
in
|
||||||
li [] [ a [ href "https://nixos.org" ] [ text "Back to nixos.org" ] ]
|
li [] [ a [ href "https://nixos.org" ] [ text "Back to nixos.org" ] ]
|
||||||
:: List.map
|
:: List.map
|
||||||
(viewNavigationItem route)
|
(viewNavigationItem route)
|
||||||
[ ( toRoute Route.Packages, "Packages" )
|
[ ( toRoute Route.Packages, text "Packages" )
|
||||||
, ( toRoute Route.Options, "Options" )
|
, ( toRoute Route.Options, text "Options" )
|
||||||
--, ( toRoute Route.Flakes, "Flakes (Experimental)" )
|
, ( toRoute Route.Flakes, span [] [ text "Flakes", sup [] [span [class "label label-info"][small [] [text "Experimental"]]]] )
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
viewNavigationItem :
|
viewNavigationItem :
|
||||||
Route.Route
|
Route.Route
|
||||||
-> ( Route.Route, String )
|
-> ( Route.Route, Html Msg )
|
||||||
-> Html Msg
|
-> Html Msg
|
||||||
viewNavigationItem currentRoute ( route, title ) =
|
viewNavigationItem currentRoute ( route, title ) =
|
||||||
li
|
li
|
||||||
[ classList [ ( "active", currentRoute == route ) ] ]
|
[ classList [ ( "active", currentRoute == route ) ] ]
|
||||||
[ a [ Route.href route ] [ text title ] ]
|
[ a [ Route.href route ] [ title ] ]
|
||||||
|
|
||||||
|
|
||||||
viewPage : Model -> Html Msg
|
viewPage : Model -> Html Msg
|
||||||
|
@ -415,6 +458,8 @@ viewPage model =
|
||||||
Flakes flakesModel ->
|
Flakes flakesModel ->
|
||||||
Html.map (\m -> FlakesMsg m) <| Page.Flakes.view flakesModel
|
Html.map (\m -> FlakesMsg m) <| Page.Flakes.view flakesModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
-- SUBSCRIPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,65 @@
|
||||||
module Page.Flakes exposing (Model, Msg, init, update, view)
|
module Page.Flakes exposing (Model(..), Msg(..), init, makeRequest, update, view)
|
||||||
|
|
||||||
import Browser.Navigation
|
import Browser.Navigation
|
||||||
import Html exposing (Html, a, code, div, li, pre, strong, text, ul)
|
import Html exposing (Html, a, code, div, li, nav, pre, strong, text, ul)
|
||||||
import Html.Attributes exposing (class, classList, href, target)
|
import Html.Attributes exposing (class, classList, href, target)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Html.Parser
|
import Html.Parser
|
||||||
import Html.Parser.Util
|
import Html.Parser.Util
|
||||||
import Json.Decode
|
import Http exposing (Body)
|
||||||
import Route
|
import Json.Decode exposing (Decoder)
|
||||||
|
import Page.Options exposing (Msg(..))
|
||||||
|
import Page.Packages exposing (Msg(..))
|
||||||
|
import Route exposing (Route(..), SearchArgs, SearchType(..))
|
||||||
import Search
|
import Search
|
||||||
|
import View.Components
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MODEL
|
-- MODEL
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type Model
|
||||||
Search.Model ResultItemSource ResultAggregations
|
= OptionModel Page.Options.Model
|
||||||
|
| PackagesModel Page.Packages.Model
|
||||||
|
|
||||||
type alias ResultItemSource =
|
|
||||||
{ name : String
|
|
||||||
, description : Maybe String
|
|
||||||
, type_ : Maybe String
|
|
||||||
, default : Maybe String
|
|
||||||
, example : Maybe String
|
|
||||||
, source : Maybe String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias ResultAggregations =
|
|
||||||
{ all : AggregationsAll
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias AggregationsAll =
|
|
||||||
{ doc_count : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg )
|
init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg )
|
||||||
init searchArgs model =
|
init searchArgs model =
|
||||||
let
|
let
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "Flakes" "init"
|
||||||
|
-- init with respective module or with packages by default
|
||||||
|
searchType =
|
||||||
|
Maybe.withDefault PackageSearch searchArgs.type_
|
||||||
|
|
||||||
|
mapEitherModel m =
|
||||||
|
case ( searchType, m ) of
|
||||||
|
( OptionSearch, OptionModel model_ ) ->
|
||||||
|
Tuple.mapBoth OptionModel (Cmd.map OptionsMsg) <| Page.Options.init searchArgs <| Just model_
|
||||||
|
|
||||||
|
( PackageSearch, PackagesModel model_ ) ->
|
||||||
|
Tuple.mapBoth PackagesModel (Cmd.map PackagesMsg) <| Page.Packages.init searchArgs <| Just model_
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
default
|
||||||
|
|
||||||
|
default =
|
||||||
|
case searchType of
|
||||||
|
PackageSearch ->
|
||||||
|
Tuple.mapBoth PackagesModel (Cmd.map PackagesMsg) <| Page.Packages.init searchArgs Nothing
|
||||||
|
|
||||||
|
OptionSearch ->
|
||||||
|
Tuple.mapBoth OptionModel (Cmd.map OptionsMsg) <| Page.Options.init searchArgs Nothing
|
||||||
|
|
||||||
( newModel, newCmd ) =
|
( newModel, newCmd ) =
|
||||||
Search.init searchArgs model
|
Maybe.withDefault default <| Maybe.map mapEitherModel model
|
||||||
|
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "mapped Model" <| Maybe.map mapEitherModel model
|
||||||
in
|
in
|
||||||
( newModel
|
( newModel
|
||||||
, Cmd.map SearchMsg newCmd
|
, newCmd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,7 +68,8 @@ init searchArgs model =
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= SearchMsg (Search.Msg ResultItemSource ResultAggregations)
|
= OptionsMsg Page.Options.Msg
|
||||||
|
| PackagesMsg Page.Packages.Msg
|
||||||
|
|
||||||
|
|
||||||
update :
|
update :
|
||||||
|
@ -64,17 +78,43 @@ update :
|
||||||
-> Model
|
-> Model
|
||||||
-> ( Model, Cmd Msg )
|
-> ( Model, Cmd Msg )
|
||||||
update navKey msg model =
|
update navKey msg model =
|
||||||
case msg of
|
-- let
|
||||||
SearchMsg subMsg ->
|
-- _ =
|
||||||
|
-- Debug.log "Flake update" ( msg, model )
|
||||||
|
-- in
|
||||||
|
case ( msg, model ) of
|
||||||
|
( OptionsMsg msg_, OptionModel model_ ) ->
|
||||||
|
case msg_ of
|
||||||
|
Page.Options.SearchMsg subMsg ->
|
||||||
let
|
let
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "update - options"
|
||||||
( newModel, newCmd ) =
|
( newModel, newCmd ) =
|
||||||
Search.update
|
Search.update
|
||||||
Route.Options
|
Route.Flakes
|
||||||
navKey
|
navKey
|
||||||
subMsg
|
subMsg
|
||||||
model
|
model_
|
||||||
in
|
in
|
||||||
( newModel, Cmd.map SearchMsg newCmd )
|
( newModel, Cmd.map Page.Options.SearchMsg newCmd ) |> Tuple.mapBoth OptionModel (Cmd.map OptionsMsg)
|
||||||
|
|
||||||
|
( PackagesMsg msg_, PackagesModel model_ ) ->
|
||||||
|
case msg_ of
|
||||||
|
Page.Packages.SearchMsg subMsg ->
|
||||||
|
let
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "Flakes" "update - packages"
|
||||||
|
( newModel, newCmd ) =
|
||||||
|
Search.update
|
||||||
|
Route.Flakes
|
||||||
|
navKey
|
||||||
|
subMsg
|
||||||
|
model_
|
||||||
|
in
|
||||||
|
( newModel, Cmd.map Page.Packages.SearchMsg newCmd ) |> Tuple.mapBoth PackagesModel (Cmd.map PackagesMsg)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,151 +123,26 @@ update navKey msg model =
|
||||||
|
|
||||||
view : Model -> Html Msg
|
view : Model -> Html Msg
|
||||||
view model =
|
view model =
|
||||||
Search.view { toRoute = Route.Options, categoryName = "options" }
|
|
||||||
[ text "Search more than "
|
|
||||||
, strong [] [ text "10 000 options" ]
|
|
||||||
]
|
|
||||||
model
|
|
||||||
viewSuccess
|
|
||||||
viewBuckets
|
|
||||||
SearchMsg
|
|
||||||
|
|
||||||
|
|
||||||
viewBuckets :
|
|
||||||
Maybe String
|
|
||||||
-> Search.SearchResult ResultItemSource ResultAggregations
|
|
||||||
-> List (Html Msg)
|
|
||||||
viewBuckets _ _ =
|
|
||||||
[]
|
|
||||||
|
|
||||||
|
|
||||||
viewSuccess :
|
|
||||||
String
|
|
||||||
-> Bool
|
|
||||||
-> Maybe String
|
|
||||||
-> List (Search.ResultItem ResultItemSource)
|
|
||||||
-> Html Msg
|
|
||||||
viewSuccess channel showNixOSDetails show hits =
|
|
||||||
ul []
|
|
||||||
(List.map
|
|
||||||
(viewResultItem channel showNixOSDetails show)
|
|
||||||
hits
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
viewResultItem :
|
|
||||||
String
|
|
||||||
-> Bool
|
|
||||||
-> Maybe String
|
|
||||||
-> Search.ResultItem ResultItemSource
|
|
||||||
-> Html Msg
|
|
||||||
viewResultItem channel _ show item =
|
|
||||||
let
|
let
|
||||||
showHtml value =
|
mkBody categoryName =
|
||||||
case Html.Parser.run value of
|
View.Components.body { toRoute = Route.Flakes, categoryName = categoryName }
|
||||||
Ok nodes ->
|
[ text "Search packages and options of "
|
||||||
Html.Parser.Util.toVirtualDom nodes
|
, strong []
|
||||||
|
[ a
|
||||||
Err _ ->
|
[ href "https://github.com/NixOS/nixos-search/blob/main/flakes/manual.toml" ]
|
||||||
[]
|
[ text "public flakes" ]
|
||||||
|
|
||||||
default =
|
|
||||||
"Not given"
|
|
||||||
|
|
||||||
asPre value =
|
|
||||||
pre [] [ text value ]
|
|
||||||
|
|
||||||
asPreCode value =
|
|
||||||
div [] [ pre [] [ code [ class "code-block" ] [ text value ] ] ]
|
|
||||||
|
|
||||||
githubUrlPrefix branch =
|
|
||||||
"https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/"
|
|
||||||
|
|
||||||
cleanPosition value =
|
|
||||||
if String.startsWith "source/" value then
|
|
||||||
String.dropLeft 7 value
|
|
||||||
|
|
||||||
else
|
|
||||||
value
|
|
||||||
|
|
||||||
asGithubLink value =
|
|
||||||
case Search.channelDetailsFromId channel of
|
|
||||||
Just channelDetails ->
|
|
||||||
a
|
|
||||||
[ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L")
|
|
||||||
, target "_blank"
|
|
||||||
]
|
]
|
||||||
[ text value ]
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
text <| cleanPosition value
|
|
||||||
|
|
||||||
withEmpty wrapWith maybe =
|
|
||||||
case maybe of
|
|
||||||
Nothing ->
|
|
||||||
asPre default
|
|
||||||
|
|
||||||
Just "" ->
|
|
||||||
asPre default
|
|
||||||
|
|
||||||
Just value ->
|
|
||||||
wrapWith value
|
|
||||||
|
|
||||||
wrapped wrapWith value =
|
|
||||||
case value of
|
|
||||||
"" ->
|
|
||||||
wrapWith <| "\"" ++ value ++ "\""
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
wrapWith value
|
|
||||||
|
|
||||||
showDetails =
|
|
||||||
if Just item.source.name == show then
|
|
||||||
div [ Html.Attributes.map SearchMsg Search.trapClick ]
|
|
||||||
[ div [] [ text "Name" ]
|
|
||||||
, div [] [ wrapped asPreCode item.source.name ]
|
|
||||||
, div [] [ text "Description" ]
|
|
||||||
, div [] <|
|
|
||||||
(item.source.description
|
|
||||||
|> Maybe.map showHtml
|
|
||||||
|> Maybe.withDefault []
|
|
||||||
)
|
|
||||||
, div [] [ text "Default value" ]
|
|
||||||
, div [] [ withEmpty (wrapped asPreCode) item.source.default ]
|
|
||||||
, div [] [ text "Type" ]
|
|
||||||
, div [] [ withEmpty asPre item.source.type_ ]
|
|
||||||
, div [] [ text "Example" ]
|
|
||||||
, div [] [ withEmpty (wrapped asPreCode) item.source.example ]
|
|
||||||
, div [] [ text "Declared in" ]
|
|
||||||
, div [] [ withEmpty asGithubLink item.source.source ]
|
|
||||||
]
|
]
|
||||||
|> Just
|
|
||||||
|
|
||||||
else
|
body =
|
||||||
Nothing
|
case model of
|
||||||
|
OptionModel model_ ->
|
||||||
|
Html.map OptionsMsg <| mkBody "Options" model_ Page.Options.viewSuccess Page.Options.viewBuckets Page.Options.SearchMsg
|
||||||
|
|
||||||
toggle =
|
PackagesModel model_ ->
|
||||||
SearchMsg (Search.ShowDetails item.source.name)
|
Html.map PackagesMsg <| mkBody "Packages" model_ Page.Packages.viewSuccess Page.Packages.viewBuckets Page.Packages.SearchMsg
|
||||||
|
|
||||||
isOpen =
|
|
||||||
Just item.source.name == show
|
|
||||||
in
|
in
|
||||||
li
|
body
|
||||||
[ class "option"
|
|
||||||
, classList [ ( "opened", isOpen ) ]
|
|
||||||
, Search.elementId item.source.name
|
|
||||||
]
|
|
||||||
<|
|
|
||||||
List.filterMap identity
|
|
||||||
[ Just <|
|
|
||||||
Html.a
|
|
||||||
[ class "search-result-button"
|
|
||||||
, onClick toggle
|
|
||||||
, href ""
|
|
||||||
]
|
|
||||||
[ text item.source.name ]
|
|
||||||
, showDetails
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,6 +151,7 @@ viewResultItem channel _ show item =
|
||||||
|
|
||||||
makeRequest :
|
makeRequest :
|
||||||
Search.Options
|
Search.Options
|
||||||
|
-> SearchType
|
||||||
-> String
|
-> String
|
||||||
-> String
|
-> String
|
||||||
-> Int
|
-> Int
|
||||||
|
@ -243,55 +159,50 @@ makeRequest :
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Search.Sort
|
-> Search.Sort
|
||||||
-> Cmd Msg
|
-> Cmd Msg
|
||||||
makeRequest options channel query from size _ sort =
|
makeRequest options searchType index_id query from size maybeBuckets sort =
|
||||||
|
let
|
||||||
|
cmd =
|
||||||
|
case searchType of
|
||||||
|
PackageSearch ->
|
||||||
Search.makeRequest
|
Search.makeRequest
|
||||||
(Search.makeRequestBody
|
(makeRequestBody searchType query from size maybeBuckets sort)
|
||||||
(String.trim query)
|
index_id
|
||||||
from
|
Page.Packages.decodeResultItemSource
|
||||||
size
|
Page.Packages.decodeResultAggregations
|
||||||
sort
|
options
|
||||||
"option"
|
Search.QueryResponse
|
||||||
"option_name"
|
(Just "query-packages")
|
||||||
[]
|
|> Cmd.map Page.Packages.SearchMsg
|
||||||
[]
|
|> Cmd.map PackagesMsg
|
||||||
[]
|
|
||||||
"option_name"
|
OptionSearch ->
|
||||||
[ ( "option_name", 6.0 )
|
Search.makeRequest
|
||||||
, ( "option_name_query", 3.0 )
|
(makeRequestBody searchType query from size maybeBuckets sort)
|
||||||
, ( "option_description", 1.0 )
|
index_id
|
||||||
]
|
Page.Options.decodeResultItemSource
|
||||||
)
|
Page.Options.decodeResultAggregations
|
||||||
channel
|
|
||||||
decodeResultItemSource
|
|
||||||
decodeResultAggregations
|
|
||||||
options
|
options
|
||||||
Search.QueryResponse
|
Search.QueryResponse
|
||||||
(Just "query-options")
|
(Just "query-options")
|
||||||
|> Cmd.map SearchMsg
|
|> Cmd.map Page.Options.SearchMsg
|
||||||
|
|> Cmd.map OptionsMsg
|
||||||
|
|
||||||
|
-- FlakeSearch ->
|
||||||
|
-- Debug.todo "branch 'FlakeSearch' not implemented"
|
||||||
|
in
|
||||||
|
cmd
|
||||||
|
|
||||||
|
|
||||||
|
makeRequestBody : SearchType -> String -> Int -> Int -> Maybe String -> Search.Sort -> Body
|
||||||
|
makeRequestBody searchType query from size maybeBuckets sort =
|
||||||
|
case searchType of
|
||||||
|
OptionSearch ->
|
||||||
|
Page.Options.makeRequestBody query from size sort
|
||||||
|
|
||||||
|
PackageSearch ->
|
||||||
|
Page.Packages.makeRequestBody query from size maybeBuckets sort
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- JSON
|
-- FlakeSearch ->
|
||||||
|
-- Debug.todo "branch 'FlakeSearch' not implemented"
|
||||||
|
|
||||||
decodeResultItemSource : Json.Decode.Decoder ResultItemSource
|
|
||||||
decodeResultItemSource =
|
|
||||||
Json.Decode.map6 ResultItemSource
|
|
||||||
(Json.Decode.field "option_name" Json.Decode.string)
|
|
||||||
(Json.Decode.field "option_description" (Json.Decode.nullable Json.Decode.string))
|
|
||||||
(Json.Decode.field "option_type" (Json.Decode.nullable Json.Decode.string))
|
|
||||||
(Json.Decode.field "option_default" (Json.Decode.nullable Json.Decode.string))
|
|
||||||
(Json.Decode.field "option_example" (Json.Decode.nullable Json.Decode.string))
|
|
||||||
(Json.Decode.field "option_source" (Json.Decode.nullable Json.Decode.string))
|
|
||||||
|
|
||||||
|
|
||||||
decodeResultAggregations : Json.Decode.Decoder ResultAggregations
|
|
||||||
decodeResultAggregations =
|
|
||||||
Json.Decode.map ResultAggregations
|
|
||||||
(Json.Decode.field "all" decodeResultAggregationsAll)
|
|
||||||
|
|
||||||
|
|
||||||
decodeResultAggregationsAll : Json.Decode.Decoder AggregationsAll
|
|
||||||
decodeResultAggregationsAll =
|
|
||||||
Json.Decode.map AggregationsAll
|
|
||||||
(Json.Decode.field "doc_count" Json.Decode.int)
|
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
module Page.Options exposing
|
module Page.Options exposing
|
||||||
( Model
|
( Model
|
||||||
, Msg
|
, Msg(..)
|
||||||
|
, ResultAggregations
|
||||||
|
, ResultItemSource
|
||||||
|
, decodeResultAggregations
|
||||||
, decodeResultItemSource
|
, decodeResultItemSource
|
||||||
, init
|
, init
|
||||||
, makeRequest
|
, makeRequest
|
||||||
|
, makeRequestBody
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, viewBuckets
|
||||||
|
, viewSuccess
|
||||||
)
|
)
|
||||||
|
|
||||||
import Browser.Navigation
|
import Browser.Navigation
|
||||||
|
@ -17,6 +23,8 @@ import Html
|
||||||
, div
|
, div
|
||||||
, li
|
, li
|
||||||
, pre
|
, pre
|
||||||
|
, source
|
||||||
|
, span
|
||||||
, strong
|
, strong
|
||||||
, text
|
, text
|
||||||
, ul
|
, ul
|
||||||
|
@ -34,9 +42,13 @@ import Html.Events
|
||||||
)
|
)
|
||||||
import Html.Parser
|
import Html.Parser
|
||||||
import Html.Parser.Util
|
import Html.Parser.Util
|
||||||
|
import Http exposing (Body)
|
||||||
import Json.Decode
|
import Json.Decode
|
||||||
import Route
|
import Json.Decode.Pipeline
|
||||||
import Search
|
import List exposing (sort)
|
||||||
|
import Route exposing (SearchType)
|
||||||
|
import Search exposing (Details, decodeResolvedFlake)
|
||||||
|
import Url.Parser exposing (query)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,6 +66,12 @@ type alias ResultItemSource =
|
||||||
, default : Maybe String
|
, default : Maybe String
|
||||||
, example : Maybe String
|
, example : Maybe String
|
||||||
, source : Maybe String
|
, source : Maybe String
|
||||||
|
|
||||||
|
-- flake
|
||||||
|
, flake : Maybe ( String, String )
|
||||||
|
, flakeName : Maybe String
|
||||||
|
, flakeDescription : Maybe String
|
||||||
|
, flakeUrl : Maybe String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,6 +137,7 @@ view model =
|
||||||
viewSuccess
|
viewSuccess
|
||||||
viewBuckets
|
viewBuckets
|
||||||
SearchMsg
|
SearchMsg
|
||||||
|
[]
|
||||||
|
|
||||||
|
|
||||||
viewBuckets :
|
viewBuckets :
|
||||||
|
@ -131,21 +150,21 @@ viewBuckets _ _ =
|
||||||
|
|
||||||
viewSuccess :
|
viewSuccess :
|
||||||
String
|
String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> List (Search.ResultItem ResultItemSource)
|
-> List (Search.ResultItem ResultItemSource)
|
||||||
-> Html Msg
|
-> Html Msg
|
||||||
viewSuccess channel showNixOSDetails show hits =
|
viewSuccess channel showInstallDetails show hits =
|
||||||
ul []
|
ul []
|
||||||
(List.map
|
(List.map
|
||||||
(viewResultItem channel showNixOSDetails show)
|
(viewResultItem channel showInstallDetails show)
|
||||||
hits
|
hits
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
viewResultItem :
|
viewResultItem :
|
||||||
String
|
String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Search.ResultItem ResultItemSource
|
-> Search.ResultItem ResultItemSource
|
||||||
-> Html Msg
|
-> Html Msg
|
||||||
|
@ -168,28 +187,6 @@ viewResultItem channel _ show item =
|
||||||
asPreCode value =
|
asPreCode value =
|
||||||
div [] [ pre [] [ code [ class "code-block" ] [ text value ] ] ]
|
div [] [ pre [] [ code [ class "code-block" ] [ text value ] ] ]
|
||||||
|
|
||||||
githubUrlPrefix branch =
|
|
||||||
"https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/"
|
|
||||||
|
|
||||||
cleanPosition value =
|
|
||||||
if String.startsWith "source/" value then
|
|
||||||
String.dropLeft 7 value
|
|
||||||
|
|
||||||
else
|
|
||||||
value
|
|
||||||
|
|
||||||
asGithubLink value =
|
|
||||||
case Search.channelDetailsFromId channel of
|
|
||||||
Just channelDetails ->
|
|
||||||
a
|
|
||||||
[ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L")
|
|
||||||
, target "_blank"
|
|
||||||
]
|
|
||||||
[ text value ]
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
text <| cleanPosition value
|
|
||||||
|
|
||||||
withEmpty wrapWith maybe =
|
withEmpty wrapWith maybe =
|
||||||
case maybe of
|
case maybe of
|
||||||
Nothing ->
|
Nothing ->
|
||||||
|
@ -227,7 +224,7 @@ viewResultItem channel _ show item =
|
||||||
, div [] [ text "Example" ]
|
, div [] [ text "Example" ]
|
||||||
, div [] [ withEmpty (wrapped asPreCode) item.source.example ]
|
, div [] [ withEmpty (wrapped asPreCode) item.source.example ]
|
||||||
, div [] [ text "Declared in" ]
|
, div [] [ text "Declared in" ]
|
||||||
, div [] [ withEmpty asGithubLink item.source.source ]
|
, div [] <| findSource channel item.source
|
||||||
]
|
]
|
||||||
|> Just
|
|> Just
|
||||||
|
|
||||||
|
@ -239,6 +236,44 @@ viewResultItem channel _ show item =
|
||||||
|
|
||||||
isOpen =
|
isOpen =
|
||||||
Just item.source.name == show
|
Just item.source.name == show
|
||||||
|
|
||||||
|
githubUrlPrefix branch =
|
||||||
|
"https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/"
|
||||||
|
|
||||||
|
cleanPosition value =
|
||||||
|
if String.startsWith "source/" value then
|
||||||
|
String.dropLeft 7 value
|
||||||
|
|
||||||
|
else
|
||||||
|
value
|
||||||
|
|
||||||
|
asGithubLink value =
|
||||||
|
case Search.channelDetailsFromId channel of
|
||||||
|
Just channelDetails ->
|
||||||
|
a
|
||||||
|
[ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L")
|
||||||
|
, target "_blank"
|
||||||
|
]
|
||||||
|
[ text value ]
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
text <| cleanPosition value
|
||||||
|
|
||||||
|
sourceFile =
|
||||||
|
Maybe.map asGithubLink item.source.source
|
||||||
|
|
||||||
|
flakeOrNixpkgs =
|
||||||
|
case ( item.source.flakeName, item.source.flake, item.source.flakeUrl ) of
|
||||||
|
-- its a flake
|
||||||
|
( Just name, Just ( _, module_ ), Just flakeUrl_ ) ->
|
||||||
|
Just
|
||||||
|
[ li []
|
||||||
|
[ a [ href flakeUrl_ ] [ text name ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
in
|
in
|
||||||
li
|
li
|
||||||
[ class "option"
|
[ class "option"
|
||||||
|
@ -248,22 +283,78 @@ viewResultItem channel _ show item =
|
||||||
<|
|
<|
|
||||||
List.filterMap identity
|
List.filterMap identity
|
||||||
[ Just <|
|
[ Just <|
|
||||||
Html.a
|
ul [ class "search-result-button" ]
|
||||||
[ class "search-result-button"
|
(List.append
|
||||||
, onClick toggle
|
(flakeOrNixpkgs |> Maybe.withDefault [])
|
||||||
|
[ li []
|
||||||
|
[ a
|
||||||
|
[ onClick toggle
|
||||||
, href ""
|
, href ""
|
||||||
]
|
]
|
||||||
[ text item.source.name ]
|
[ text item.source.name ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
, showDetails
|
, showDetails
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
findSource : String -> ResultItemSource -> List (Html a)
|
||||||
|
findSource channel source =
|
||||||
|
let
|
||||||
|
githubUrlPrefix branch =
|
||||||
|
"https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/"
|
||||||
|
|
||||||
|
cleanPosition value =
|
||||||
|
if String.startsWith "source/" value then
|
||||||
|
String.dropLeft 7 value
|
||||||
|
|
||||||
|
else
|
||||||
|
value
|
||||||
|
|
||||||
|
asGithubLink value =
|
||||||
|
case Search.channelDetailsFromId channel of
|
||||||
|
Just channelDetails ->
|
||||||
|
a
|
||||||
|
[ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L")
|
||||||
|
, target "_blank"
|
||||||
|
]
|
||||||
|
[ text value ]
|
||||||
|
|
||||||
|
Nothing ->
|
||||||
|
text <| cleanPosition value
|
||||||
|
|
||||||
|
sourceFile =
|
||||||
|
Maybe.map asGithubLink source.source
|
||||||
|
|
||||||
|
flakeOrNixpkgs : Maybe (List (Html a))
|
||||||
|
flakeOrNixpkgs =
|
||||||
|
case ( source.flake, source.flakeUrl ) of
|
||||||
|
-- its a flake
|
||||||
|
( Just ( name, module_ ), Just flakeUrl_ ) ->
|
||||||
|
Just <|
|
||||||
|
List.append
|
||||||
|
(Maybe.withDefault [] <| Maybe.map (\sourceFile_ -> [ sourceFile_, span [] [ text " in " ] ]) sourceFile)
|
||||||
|
[ span [] [ text "Flake: " ]
|
||||||
|
, a [ href flakeUrl_ ] [ text <| name ++ "(Module: " ++ module_ ++ ")" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
( Nothing, _ ) ->
|
||||||
|
Maybe.map (\l -> [ l ]) sourceFile
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
Maybe.withDefault [ span [] [ text "Not Found" ] ] flakeOrNixpkgs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- API
|
-- API
|
||||||
|
|
||||||
|
|
||||||
makeRequest :
|
makeRequest :
|
||||||
Search.Options
|
Search.Options
|
||||||
|
-> SearchType
|
||||||
-> String
|
-> String
|
||||||
-> String
|
-> String
|
||||||
-> Int
|
-> Int
|
||||||
|
@ -271,9 +362,21 @@ makeRequest :
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Search.Sort
|
-> Search.Sort
|
||||||
-> Cmd Msg
|
-> Cmd Msg
|
||||||
makeRequest options channel query from size _ sort =
|
makeRequest options _ channel query from size _ sort =
|
||||||
Search.makeRequest
|
Search.makeRequest
|
||||||
(Search.makeRequestBody
|
(makeRequestBody query from size sort)
|
||||||
|
channel
|
||||||
|
decodeResultItemSource
|
||||||
|
decodeResultAggregations
|
||||||
|
options
|
||||||
|
Search.QueryResponse
|
||||||
|
(Just "query-options")
|
||||||
|
|> Cmd.map SearchMsg
|
||||||
|
|
||||||
|
|
||||||
|
makeRequestBody : String -> Int -> Int -> Search.Sort -> Body
|
||||||
|
makeRequestBody query from size sort =
|
||||||
|
Search.makeRequestBody
|
||||||
(String.trim query)
|
(String.trim query)
|
||||||
from
|
from
|
||||||
size
|
size
|
||||||
|
@ -288,14 +391,6 @@ makeRequest options channel query from size _ sort =
|
||||||
, ( "option_name_query", 3.0 )
|
, ( "option_name_query", 3.0 )
|
||||||
, ( "option_description", 1.0 )
|
, ( "option_description", 1.0 )
|
||||||
]
|
]
|
||||||
)
|
|
||||||
channel
|
|
||||||
decodeResultItemSource
|
|
||||||
decodeResultAggregations
|
|
||||||
options
|
|
||||||
Search.QueryResponse
|
|
||||||
(Just "query-options")
|
|
||||||
|> Cmd.map SearchMsg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,13 +399,19 @@ makeRequest options channel query from size _ sort =
|
||||||
|
|
||||||
decodeResultItemSource : Json.Decode.Decoder ResultItemSource
|
decodeResultItemSource : Json.Decode.Decoder ResultItemSource
|
||||||
decodeResultItemSource =
|
decodeResultItemSource =
|
||||||
Json.Decode.map6 ResultItemSource
|
Json.Decode.succeed ResultItemSource
|
||||||
(Json.Decode.field "option_name" Json.Decode.string)
|
|> Json.Decode.Pipeline.required "option_name" Json.Decode.string
|
||||||
(Json.Decode.field "option_description" (Json.Decode.nullable Json.Decode.string))
|
|> Json.Decode.Pipeline.optional "option_description" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
(Json.Decode.field "option_type" (Json.Decode.nullable Json.Decode.string))
|
|> Json.Decode.Pipeline.optional "option_type" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
(Json.Decode.field "option_default" (Json.Decode.nullable Json.Decode.string))
|
|> Json.Decode.Pipeline.optional "option_default" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
(Json.Decode.field "option_example" (Json.Decode.nullable Json.Decode.string))
|
|> Json.Decode.Pipeline.optional "option_example" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
(Json.Decode.field "option_source" (Json.Decode.nullable Json.Decode.string))
|
|> Json.Decode.Pipeline.optional "option_source" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "option_flake"
|
||||||
|
(Json.Decode.map Just <| Json.Decode.map2 Tuple.pair (Json.Decode.index 0 Json.Decode.string) (Json.Decode.index 1 Json.Decode.string))
|
||||||
|
Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_name" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_description" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_resolved" (Json.Decode.map Just decodeResolvedFlake) Nothing
|
||||||
|
|
||||||
|
|
||||||
decodeResultAggregations : Json.Decode.Decoder ResultAggregations
|
decodeResultAggregations : Json.Decode.Decoder ResultAggregations
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
module Page.Packages exposing
|
module Page.Packages exposing
|
||||||
( Model
|
( Model
|
||||||
, Msg
|
, Msg(..)
|
||||||
|
, decodeResultAggregations
|
||||||
, decodeResultItemSource
|
, decodeResultItemSource
|
||||||
|
, encodeBuckets
|
||||||
, init
|
, init
|
||||||
|
, initBuckets
|
||||||
, makeRequest
|
, makeRequest
|
||||||
|
, makeRequestBody
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, viewBuckets
|
||||||
|
, viewSuccess
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import Browser.Events exposing (Visibility(..))
|
||||||
import Browser.Navigation
|
import Browser.Navigation
|
||||||
import Html
|
import Html
|
||||||
exposing
|
exposing
|
||||||
|
@ -31,16 +38,19 @@ import Html.Attributes
|
||||||
, href
|
, href
|
||||||
, id
|
, id
|
||||||
, target
|
, target
|
||||||
|
, type_
|
||||||
)
|
)
|
||||||
import Html.Events exposing (onClick)
|
import Html.Events exposing (onClick)
|
||||||
import Json.Decode
|
import Http exposing (Body)
|
||||||
|
import Json.Decode exposing (Decoder)
|
||||||
import Json.Decode.Pipeline
|
import Json.Decode.Pipeline
|
||||||
import Json.Encode
|
import Json.Encode
|
||||||
|
import Maybe
|
||||||
import Regex
|
import Regex
|
||||||
import Route
|
import Route exposing (Route(..), SearchType)
|
||||||
import Search
|
import Search exposing (Details(..), channelDetailsFromId, decodeResolvedFlake)
|
||||||
import Utils
|
import Utils
|
||||||
import Search exposing (channelDetailsFromId)
|
import View.Components.SearchInput exposing (closeButton, viewBucket)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +74,9 @@ type alias ResultItemSource =
|
||||||
, homepage : List String
|
, homepage : List String
|
||||||
, system : String
|
, system : String
|
||||||
, hydra : Maybe (List ResultPackageHydra)
|
, hydra : Maybe (List ResultPackageHydra)
|
||||||
|
, flakeName : Maybe String
|
||||||
|
, flakeDescription : Maybe String
|
||||||
|
, flakeUrl : Maybe ( String, String )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,13 +161,16 @@ init searchArgs model =
|
||||||
let
|
let
|
||||||
( newModel, newCmd ) =
|
( newModel, newCmd ) =
|
||||||
Search.init searchArgs model
|
Search.init searchArgs model
|
||||||
|
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "New package model" newModel
|
||||||
in
|
in
|
||||||
( newModel
|
( newModel
|
||||||
, Cmd.map SearchMsg newCmd
|
, Cmd.map SearchMsg newCmd
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
platforms: List String
|
platforms : List String
|
||||||
platforms =
|
platforms =
|
||||||
[ "x86_64-linux"
|
[ "x86_64-linux"
|
||||||
, "aarch64-linux"
|
, "aarch64-linux"
|
||||||
|
@ -164,6 +180,7 @@ platforms =
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- UPDATE
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,6 +221,7 @@ view model =
|
||||||
viewSuccess
|
viewSuccess
|
||||||
viewBuckets
|
viewBuckets
|
||||||
SearchMsg
|
SearchMsg
|
||||||
|
[]
|
||||||
|
|
||||||
|
|
||||||
viewBuckets :
|
viewBuckets :
|
||||||
|
@ -255,73 +273,32 @@ viewBuckets bucketsAsString result =
|
||||||
selectedBucket.platforms
|
selectedBucket.platforms
|
||||||
|
|
||||||
|
|
||||||
filterPlatformsBucket : List {a | key : String} -> List {a | key : String}
|
filterPlatformsBucket : List { a | key : String } -> List { a | key : String }
|
||||||
filterPlatformsBucket = List.filter (\a -> List.member a.key platforms)
|
filterPlatformsBucket =
|
||||||
|
List.filter (\a -> List.member a.key platforms)
|
||||||
viewBucket :
|
|
||||||
String
|
|
||||||
-> List Search.AggregationsBucketItem
|
|
||||||
-> (String -> Msg)
|
|
||||||
-> List String
|
|
||||||
-> List (Html Msg)
|
|
||||||
-> List (Html Msg)
|
|
||||||
viewBucket title buckets searchMsgFor selectedBucket sets =
|
|
||||||
List.append
|
|
||||||
sets
|
|
||||||
(if List.isEmpty buckets then
|
|
||||||
[]
|
|
||||||
|
|
||||||
else
|
|
||||||
[ li []
|
|
||||||
[ ul []
|
|
||||||
(List.append
|
|
||||||
[ li [ class "header" ] [ text title ] ]
|
|
||||||
(List.map
|
|
||||||
(\bucket ->
|
|
||||||
li []
|
|
||||||
[ a
|
|
||||||
[ href "#"
|
|
||||||
, onClick <| searchMsgFor bucket.key
|
|
||||||
, classList
|
|
||||||
[ ( "selected"
|
|
||||||
, List.member bucket.key selectedBucket
|
|
||||||
)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
[ span [] [ text bucket.key ]
|
|
||||||
, span [] [ span [ class "badge" ] [ text <| String.fromInt bucket.doc_count ] ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
buckets
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
viewSuccess :
|
viewSuccess :
|
||||||
String
|
String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> List (Search.ResultItem ResultItemSource)
|
-> List (Search.ResultItem ResultItemSource)
|
||||||
-> Html Msg
|
-> Html Msg
|
||||||
viewSuccess channel showNixOSDetails show hits =
|
viewSuccess channel showInstallDetails show hits =
|
||||||
ul []
|
ul []
|
||||||
(List.map
|
(List.map
|
||||||
(viewResultItem channel showNixOSDetails show)
|
(viewResultItem channel showInstallDetails show)
|
||||||
hits
|
hits
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
viewResultItem :
|
viewResultItem :
|
||||||
String
|
String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Search.ResultItem ResultItemSource
|
-> Search.ResultItem ResultItemSource
|
||||||
-> Html Msg
|
-> Html Msg
|
||||||
viewResultItem channel showNixOSDetails show item =
|
viewResultItem channel showInstallDetails show item =
|
||||||
let
|
let
|
||||||
cleanPosition =
|
cleanPosition =
|
||||||
Regex.fromString "^[0-9a-f]+\\.tar\\.gz\\/"
|
Regex.fromString "^[0-9a-f]+\\.tar\\.gz\\/"
|
||||||
|
@ -346,23 +323,7 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
|
|
||||||
shortPackageDetails =
|
shortPackageDetails =
|
||||||
ul []
|
ul []
|
||||||
((item.source.position
|
(renderSource item channel trapClick createShortDetailsItem createGithubUrl
|
||||||
|> Maybe.map
|
|
||||||
(\position ->
|
|
||||||
case Search.channelDetailsFromId channel of
|
|
||||||
Nothing ->
|
|
||||||
[]
|
|
||||||
|
|
||||||
Just channelDetails ->
|
|
||||||
[ li [ trapClick ]
|
|
||||||
[ createShortDetailsItem
|
|
||||||
"Source"
|
|
||||||
(createGithubUrl channelDetails.branch position)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> Maybe.withDefault []
|
|
||||||
)
|
|
||||||
|> List.append
|
|> List.append
|
||||||
(item.source.homepage
|
(item.source.homepage
|
||||||
|> List.head
|
|> List.head
|
||||||
|
@ -420,7 +381,7 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
Nothing ->
|
Nothing ->
|
||||||
"#"
|
"#"
|
||||||
]
|
]
|
||||||
[ text <| Maybe.withDefault "" maintainer.name ++ " <" ++ Maybe.withDefault "" maintainer.email ++ ">" ]
|
[ text <| Maybe.withDefault "" maintainer.name ++ (Maybe.withDefault "" <| Maybe.map (\email -> " <" ++ email ++ ">") maintainer.email) ]
|
||||||
]
|
]
|
||||||
|
|
||||||
showPlatform platform =
|
showPlatform platform =
|
||||||
|
@ -479,10 +440,11 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
, em [] [ text item.source.attr_name ]
|
, em [] [ text item.source.attr_name ]
|
||||||
, text "?"
|
, text "?"
|
||||||
]
|
]
|
||||||
, ul [ class "nav nav-tabs" ]
|
, ul [ class "nav nav-tabs" ] <|
|
||||||
|
Maybe.withDefault
|
||||||
[ li
|
[ li
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "active", showNixOSDetails )
|
[ ( "active", List.member showInstallDetails [ Search.Unset, Search.FromNixOS, Search.FromFlake ] )
|
||||||
, ( "pull-right", True )
|
, ( "pull-right", True )
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -490,13 +452,13 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
[ href "#"
|
[ href "#"
|
||||||
, Search.onClickStop <|
|
, Search.onClickStop <|
|
||||||
SearchMsg <|
|
SearchMsg <|
|
||||||
Search.ShowNixOSDetails True
|
Search.ShowInstallDetails Search.FromNixOS
|
||||||
]
|
]
|
||||||
[ text "On NixOS" ]
|
[ text "On NixOS" ]
|
||||||
]
|
]
|
||||||
, li
|
, li
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "active", not showNixOSDetails )
|
[ ( "active", showInstallDetails == Search.FromNixpkgs )
|
||||||
, ( "pull-right", True )
|
, ( "pull-right", True )
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -504,16 +466,38 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
[ href "#"
|
[ href "#"
|
||||||
, Search.onClickStop <|
|
, Search.onClickStop <|
|
||||||
SearchMsg <|
|
SearchMsg <|
|
||||||
Search.ShowNixOSDetails False
|
Search.ShowInstallDetails Search.FromNixpkgs
|
||||||
]
|
]
|
||||||
[ text "On non-NixOS" ]
|
[ text "On non-NixOS" ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
<|
|
||||||
|
Maybe.map
|
||||||
|
(\_ ->
|
||||||
|
[ li
|
||||||
|
[ classList
|
||||||
|
[ ( "active", True )
|
||||||
|
, ( "pull-right", True )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ a
|
||||||
|
[ href "#"
|
||||||
|
, Search.onClickStop <|
|
||||||
|
SearchMsg <|
|
||||||
|
Search.ShowInstallDetails Search.FromFlake
|
||||||
|
]
|
||||||
|
[ text "Install from flake" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
item.source.flakeUrl
|
||||||
, div
|
, div
|
||||||
[ class "tab-content" ]
|
[ class "tab-content" ]
|
||||||
|
<|
|
||||||
|
Maybe.withDefault
|
||||||
[ div
|
[ div
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "active", not showNixOSDetails )
|
[ ( "active", showInstallDetails == Search.FromNixpkgs )
|
||||||
]
|
]
|
||||||
, class "tab-pane"
|
, class "tab-pane"
|
||||||
, id "package-details-nixpkgs"
|
, id "package-details-nixpkgs"
|
||||||
|
@ -526,7 +510,7 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
, div
|
, div
|
||||||
[ classList
|
[ classList
|
||||||
[ ( "tab-pane", True )
|
[ ( "tab-pane", True )
|
||||||
, ( "active", showNixOSDetails )
|
, ( "active", List.member showInstallDetails [ Search.Unset, Search.FromNixOS, Search.FromFlake ] )
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[ pre [ class "code-block" ]
|
[ pre [ class "code-block" ]
|
||||||
|
@ -535,6 +519,26 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
<|
|
||||||
|
Maybe.map
|
||||||
|
(\url ->
|
||||||
|
[ div
|
||||||
|
[ classList
|
||||||
|
[ ( "tab-pane", True )
|
||||||
|
, ( "active", True )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ pre [ class "code-block" ]
|
||||||
|
[ text "nix build "
|
||||||
|
, strong [] [ text url ]
|
||||||
|
, text "#"
|
||||||
|
, em [] [ text item.source.attr_name ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
<|
|
||||||
|
Maybe.map Tuple.first item.source.flakeUrl
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -551,6 +555,17 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
|
|
||||||
isOpen =
|
isOpen =
|
||||||
Just item.source.attr_name == show
|
Just item.source.attr_name == show
|
||||||
|
|
||||||
|
flakeItem =
|
||||||
|
Maybe.map Tuple.second item.source.flakeUrl
|
||||||
|
|> Maybe.map2
|
||||||
|
(\name resolved ->
|
||||||
|
[ li [ trapClick ]
|
||||||
|
[ createShortDetailsItem name resolved ]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
item.source.flakeName
|
||||||
|
|> Maybe.withDefault []
|
||||||
in
|
in
|
||||||
li
|
li
|
||||||
[ class "package"
|
[ class "package"
|
||||||
|
@ -560,12 +575,18 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
([]
|
([]
|
||||||
|> List.append longerPackageDetails
|
|> List.append longerPackageDetails
|
||||||
|> List.append
|
|> List.append
|
||||||
[ Html.a
|
[ ul [ class "search-result-button" ]
|
||||||
[ class "search-result-button"
|
(List.append
|
||||||
, onClick toggle
|
flakeItem
|
||||||
|
[ li []
|
||||||
|
[ a
|
||||||
|
[ onClick toggle
|
||||||
, href ""
|
, href ""
|
||||||
]
|
]
|
||||||
[ text item.source.attr_name ]
|
[ text item.source.attr_name ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
, div [] [ text <| Maybe.withDefault "" item.source.description ]
|
, div [] [ text <| Maybe.withDefault "" item.source.description ]
|
||||||
, shortPackageDetails
|
, shortPackageDetails
|
||||||
, Search.showMoreButton toggle isOpen
|
, Search.showMoreButton toggle isOpen
|
||||||
|
@ -573,12 +594,50 @@ viewResultItem channel showNixOSDetails show item =
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
renderSource : Search.ResultItem ResultItemSource -> String -> Html.Attribute Msg -> (String -> String -> Html Msg) -> (String -> String -> String) -> List (Html Msg)
|
||||||
|
renderSource item channel trapClick createShortDetailsItem createGithubUrl =
|
||||||
|
let
|
||||||
|
postion =
|
||||||
|
item.source.position
|
||||||
|
|> Maybe.map
|
||||||
|
(\position ->
|
||||||
|
case Search.channelDetailsFromId channel of
|
||||||
|
Nothing ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
Just channelDetails ->
|
||||||
|
[ li [ trapClick ]
|
||||||
|
[ createShortDetailsItem
|
||||||
|
"Source"
|
||||||
|
(createGithubUrl channelDetails.branch position)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
flakeDef =
|
||||||
|
Maybe.map2
|
||||||
|
(\name resolved ->
|
||||||
|
[ li [ trapClick ]
|
||||||
|
[ createShortDetailsItem
|
||||||
|
("Flake: " ++ name)
|
||||||
|
resolved
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
item.source.flakeName
|
||||||
|
<|
|
||||||
|
Maybe.map Tuple.second item.source.flakeUrl
|
||||||
|
in
|
||||||
|
Maybe.withDefault (Maybe.withDefault [] flakeDef) postion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- API
|
-- API
|
||||||
|
|
||||||
|
|
||||||
makeRequest :
|
makeRequest :
|
||||||
Search.Options
|
Search.Options
|
||||||
|
-> SearchType
|
||||||
-> String
|
-> String
|
||||||
-> String
|
-> String
|
||||||
-> Int
|
-> Int
|
||||||
|
@ -586,7 +645,20 @@ makeRequest :
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Search.Sort
|
-> Search.Sort
|
||||||
-> Cmd Msg
|
-> Cmd Msg
|
||||||
makeRequest options channel query from size maybeBuckets sort =
|
makeRequest options _ channel query from size maybeBuckets sort =
|
||||||
|
Search.makeRequest
|
||||||
|
(makeRequestBody query from size maybeBuckets sort)
|
||||||
|
channel
|
||||||
|
decodeResultItemSource
|
||||||
|
decodeResultAggregations
|
||||||
|
options
|
||||||
|
Search.QueryResponse
|
||||||
|
(Just "query-packages")
|
||||||
|
|> Cmd.map SearchMsg
|
||||||
|
|
||||||
|
|
||||||
|
makeRequestBody : String -> Int -> Int -> Maybe String -> Search.Sort -> Body
|
||||||
|
makeRequestBody query from size maybeBuckets sort =
|
||||||
let
|
let
|
||||||
currentBuckets =
|
currentBuckets =
|
||||||
initBuckets maybeBuckets
|
initBuckets maybeBuckets
|
||||||
|
@ -637,8 +709,7 @@ makeRequest options channel query from size maybeBuckets sort =
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
Search.makeRequest
|
Search.makeRequestBody
|
||||||
(Search.makeRequestBody
|
|
||||||
(String.trim query)
|
(String.trim query)
|
||||||
from
|
from
|
||||||
size
|
size
|
||||||
|
@ -659,14 +730,6 @@ makeRequest options channel query from size maybeBuckets sort =
|
||||||
, ( "package_description", 1.3 )
|
, ( "package_description", 1.3 )
|
||||||
, ( "package_longDescription", 1.0 )
|
, ( "package_longDescription", 1.0 )
|
||||||
]
|
]
|
||||||
)
|
|
||||||
channel
|
|
||||||
decodeResultItemSource
|
|
||||||
decodeResultAggregations
|
|
||||||
options
|
|
||||||
Search.QueryResponse
|
|
||||||
(Just "query-packages")
|
|
||||||
|> Cmd.map SearchMsg
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -707,6 +770,56 @@ decodeResultItemSource =
|
||||||
|> Json.Decode.Pipeline.required "package_homepage" decodeHomepage
|
|> Json.Decode.Pipeline.required "package_homepage" decodeHomepage
|
||||||
|> Json.Decode.Pipeline.required "package_system" Json.Decode.string
|
|> Json.Decode.Pipeline.required "package_system" Json.Decode.string
|
||||||
|> Json.Decode.Pipeline.required "package_hydra" (Json.Decode.nullable (Json.Decode.list decodeResultPackageHydra))
|
|> Json.Decode.Pipeline.required "package_hydra" (Json.Decode.nullable (Json.Decode.list decodeResultPackageHydra))
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_name" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_description" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "flake_resolved" (Json.Decode.map Just decodeResolvedFlake) Nothing
|
||||||
|
|
||||||
|
|
||||||
|
type alias ResolvedFlake =
|
||||||
|
{ type_ : String, owner : Maybe String, repo : Maybe String, url : Maybe String }
|
||||||
|
|
||||||
|
|
||||||
|
decodeResolvedFlake : Json.Decode.Decoder ( String, String )
|
||||||
|
decodeResolvedFlake =
|
||||||
|
let
|
||||||
|
resolved =
|
||||||
|
Json.Decode.succeed ResolvedFlake
|
||||||
|
|> Json.Decode.Pipeline.required "type" Json.Decode.string
|
||||||
|
|> Json.Decode.Pipeline.optional "owner" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "repo" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "url" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
in
|
||||||
|
Json.Decode.map
|
||||||
|
(\resolved_ ->
|
||||||
|
let
|
||||||
|
repoPath =
|
||||||
|
case ( resolved_.owner, resolved_.repo ) of
|
||||||
|
( Just owner, Just repo ) ->
|
||||||
|
Just <| owner ++ "/" ++ repo
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
url =
|
||||||
|
resolved_.url
|
||||||
|
|
||||||
|
result =
|
||||||
|
case resolved_.type_ of
|
||||||
|
"github" ->
|
||||||
|
Maybe.map (\repoPath_ -> ( "github:" ++ repoPath_, "https://github.com/" ++ repoPath_ )) repoPath
|
||||||
|
|
||||||
|
"gitlab" ->
|
||||||
|
Maybe.map (\repoPath_ -> ( "gitlab:" ++ repoPath_, "https://gitlab.com/" ++ repoPath_ )) repoPath
|
||||||
|
|
||||||
|
"git" ->
|
||||||
|
Maybe.map (\url_ -> ( url_, url_ )) url
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
Maybe.withDefault ( "INVALID FLAKE ORIGIN", "INVALID FLAKE ORIGIN" ) result
|
||||||
|
)
|
||||||
|
resolved
|
||||||
|
|
||||||
|
|
||||||
filterPlatforms : List String -> List String
|
filterPlatforms : List String -> List String
|
||||||
|
@ -743,7 +856,13 @@ decodeResultPackageLicense =
|
||||||
decodeResultPackageMaintainer : Json.Decode.Decoder ResultPackageMaintainer
|
decodeResultPackageMaintainer : Json.Decode.Decoder ResultPackageMaintainer
|
||||||
decodeResultPackageMaintainer =
|
decodeResultPackageMaintainer =
|
||||||
Json.Decode.map3 ResultPackageMaintainer
|
Json.Decode.map3 ResultPackageMaintainer
|
||||||
(Json.Decode.field "name" (Json.Decode.nullable Json.Decode.string))
|
(Json.Decode.oneOf
|
||||||
|
[ Json.Decode.field "name" (Json.Decode.map Just Json.Decode.string)
|
||||||
|
, Json.Decode.field "email" (Json.Decode.map Just Json.Decode.string)
|
||||||
|
, Json.Decode.field "github" (Json.Decode.map Just Json.Decode.string)
|
||||||
|
, Json.Decode.succeed Nothing
|
||||||
|
]
|
||||||
|
)
|
||||||
(Json.Decode.field "email" (Json.Decode.nullable Json.Decode.string))
|
(Json.Decode.field "email" (Json.Decode.nullable Json.Decode.string))
|
||||||
(Json.Decode.field "github" (Json.Decode.nullable Json.Decode.string))
|
(Json.Decode.field "github" (Json.Decode.nullable Json.Decode.string))
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,17 @@ module Route exposing
|
||||||
( Route(..)
|
( Route(..)
|
||||||
, SearchArgs
|
, SearchArgs
|
||||||
, SearchRoute
|
, SearchRoute
|
||||||
|
, SearchType(..)
|
||||||
|
, allTypes
|
||||||
, fromUrl
|
, fromUrl
|
||||||
, href
|
, href
|
||||||
, replaceUrl
|
, replaceUrl
|
||||||
, routeToString
|
, routeToString
|
||||||
|
, searchTypeToString, searchTypeToTitle
|
||||||
)
|
)
|
||||||
|
|
||||||
import Browser.Navigation
|
import Browser.Navigation
|
||||||
|
import Dict
|
||||||
import Html
|
import Html
|
||||||
import Html.Attributes
|
import Html.Attributes
|
||||||
import Route.SearchQuery exposing (SearchQuery)
|
import Route.SearchQuery exposing (SearchQuery)
|
||||||
|
@ -32,9 +36,61 @@ type alias SearchArgs =
|
||||||
|
|
||||||
-- TODO: embed sort type
|
-- TODO: embed sort type
|
||||||
, sort : Maybe String
|
, sort : Maybe String
|
||||||
|
, type_ : Maybe SearchType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type SearchType
|
||||||
|
= OptionSearch
|
||||||
|
| PackageSearch
|
||||||
|
-- | FlakeSearch
|
||||||
|
|
||||||
|
|
||||||
|
allTypes : List SearchType
|
||||||
|
allTypes =
|
||||||
|
[ PackageSearch, OptionSearch ]
|
||||||
|
|
||||||
|
|
||||||
|
searchTypeFromString : String -> Maybe SearchType
|
||||||
|
searchTypeFromString string =
|
||||||
|
case string of
|
||||||
|
"options" ->
|
||||||
|
Just OptionSearch
|
||||||
|
|
||||||
|
"packages" ->
|
||||||
|
Just PackageSearch
|
||||||
|
|
||||||
|
-- "flakes" ->
|
||||||
|
-- Just FlakeSearch
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
|
searchTypeToString : SearchType -> String
|
||||||
|
searchTypeToString stype =
|
||||||
|
case stype of
|
||||||
|
OptionSearch ->
|
||||||
|
"options"
|
||||||
|
|
||||||
|
PackageSearch ->
|
||||||
|
"packages"
|
||||||
|
|
||||||
|
-- FlakeSearch ->
|
||||||
|
-- "flakes"
|
||||||
|
|
||||||
|
searchTypeToTitle : SearchType -> String
|
||||||
|
searchTypeToTitle stype =
|
||||||
|
case stype of
|
||||||
|
OptionSearch ->
|
||||||
|
"Options"
|
||||||
|
|
||||||
|
PackageSearch ->
|
||||||
|
"Packages"
|
||||||
|
|
||||||
|
-- FlakeSearch ->
|
||||||
|
-- "flakes"
|
||||||
|
|
||||||
type alias SearchRoute =
|
type alias SearchRoute =
|
||||||
SearchArgs -> Route
|
SearchArgs -> Route
|
||||||
|
|
||||||
|
@ -56,6 +112,7 @@ searchQueryParser url =
|
||||||
<?> Url.Parser.Query.int "size"
|
<?> Url.Parser.Query.int "size"
|
||||||
<?> Url.Parser.Query.string "buckets"
|
<?> Url.Parser.Query.string "buckets"
|
||||||
<?> Url.Parser.Query.string "sort"
|
<?> Url.Parser.Query.string "sort"
|
||||||
|
<?> Url.Parser.Query.map (Maybe.andThen searchTypeFromString) (Url.Parser.Query.string "type")
|
||||||
|
|
||||||
|
|
||||||
searchArgsToUrl : SearchArgs -> ( List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) )
|
searchArgsToUrl : SearchArgs -> ( List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) )
|
||||||
|
@ -67,6 +124,7 @@ searchArgsToUrl args =
|
||||||
, Maybe.map (Url.Builder.int "size") args.size
|
, Maybe.map (Url.Builder.int "size") args.size
|
||||||
, Maybe.map (Url.Builder.string "buckets") args.buckets
|
, Maybe.map (Url.Builder.string "buckets") args.buckets
|
||||||
, Maybe.map (Url.Builder.string "sort") args.sort
|
, Maybe.map (Url.Builder.string "sort") args.sort
|
||||||
|
, Maybe.map (Url.Builder.string "type") <| Maybe.map searchTypeToString args.type_
|
||||||
]
|
]
|
||||||
, Maybe.map (Tuple.pair "query") args.query
|
, Maybe.map (Tuple.pair "query") args.query
|
||||||
)
|
)
|
||||||
|
|
227
src/Search.elm
227
src/Search.elm
|
@ -4,14 +4,19 @@ module Search exposing
|
||||||
, Model
|
, Model
|
||||||
, Msg(..)
|
, Msg(..)
|
||||||
, Options
|
, Options
|
||||||
|
, ResultHits
|
||||||
, ResultItem
|
, ResultItem
|
||||||
, SearchResult
|
, SearchResult
|
||||||
, Sort(..)
|
, Sort(..)
|
||||||
, channelDetailsFromId
|
, channelDetailsFromId
|
||||||
, channels
|
, channels
|
||||||
, decodeAggregation
|
, decodeAggregation
|
||||||
|
, decodeResolvedFlake
|
||||||
, decodeResult
|
, decodeResult
|
||||||
|
, defaultFlakeId
|
||||||
, elementId
|
, elementId
|
||||||
|
, flakeFromId
|
||||||
|
, flakes
|
||||||
, fromSortId
|
, fromSortId
|
||||||
, init
|
, init
|
||||||
, makeRequest
|
, makeRequest
|
||||||
|
@ -22,6 +27,7 @@ module Search exposing
|
||||||
, trapClick
|
, trapClick
|
||||||
, update
|
, update
|
||||||
, view
|
, view
|
||||||
|
, viewResult, Details(..)
|
||||||
)
|
)
|
||||||
|
|
||||||
import Base64
|
import Base64
|
||||||
|
@ -65,16 +71,19 @@ import Html.Events
|
||||||
)
|
)
|
||||||
import Http
|
import Http
|
||||||
import Json.Decode
|
import Json.Decode
|
||||||
|
import Json.Decode.Pipeline
|
||||||
import Json.Encode
|
import Json.Encode
|
||||||
import RemoteData
|
import RemoteData
|
||||||
import Route
|
import Route exposing (SearchArgs, SearchType)
|
||||||
import Route.SearchQuery
|
import Route.SearchQuery
|
||||||
import Set
|
import Set
|
||||||
import Task
|
import Task
|
||||||
|
import Browser.Events exposing (Visibility(..))
|
||||||
|
|
||||||
|
|
||||||
type alias Model a b =
|
type alias Model a b =
|
||||||
{ channel : String
|
{ channel : String
|
||||||
|
, flake : String
|
||||||
, query : Maybe String
|
, query : Maybe String
|
||||||
, result : RemoteData.WebData (SearchResult a b)
|
, result : RemoteData.WebData (SearchResult a b)
|
||||||
, show : Maybe String
|
, show : Maybe String
|
||||||
|
@ -83,7 +92,8 @@ type alias Model a b =
|
||||||
, buckets : Maybe String
|
, buckets : Maybe String
|
||||||
, sort : Sort
|
, sort : Sort
|
||||||
, showSort : Bool
|
, showSort : Bool
|
||||||
, showNixOSDetails : Bool
|
, showInstallDetails : Details
|
||||||
|
, searchType : Route.SearchType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,12 +145,75 @@ type Sort
|
||||||
| AlphabeticallyDesc
|
| AlphabeticallyDesc
|
||||||
|
|
||||||
|
|
||||||
|
type alias ResolvedFlake =
|
||||||
|
{ type_ : String, owner : Maybe String, repo : Maybe String, url : Maybe String }
|
||||||
|
|
||||||
|
|
||||||
|
decodeResolvedFlake : Json.Decode.Decoder String
|
||||||
|
decodeResolvedFlake =
|
||||||
|
let
|
||||||
|
resolved =
|
||||||
|
Json.Decode.succeed ResolvedFlake
|
||||||
|
|> Json.Decode.Pipeline.required "type" Json.Decode.string
|
||||||
|
|> Json.Decode.Pipeline.optional "owner" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "repo" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
|> Json.Decode.Pipeline.optional "url" (Json.Decode.map Just Json.Decode.string) Nothing
|
||||||
|
in
|
||||||
|
Json.Decode.map
|
||||||
|
(\resolved_ ->
|
||||||
|
let
|
||||||
|
repoPath =
|
||||||
|
case ( resolved_.owner, resolved_.repo ) of
|
||||||
|
( Just owner, Just repo ) ->
|
||||||
|
Just <| owner ++ "/" ++ repo
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
url =
|
||||||
|
resolved_.url
|
||||||
|
|
||||||
|
result =
|
||||||
|
case resolved_.type_ of
|
||||||
|
"github" ->
|
||||||
|
Maybe.map (\repoPath_ -> "https://github.com/" ++ repoPath_) repoPath
|
||||||
|
|
||||||
|
"gitlab" ->
|
||||||
|
Maybe.map (\repoPath_ -> "https://gitlab.com/" ++ repoPath_) repoPath
|
||||||
|
|
||||||
|
"git" ->
|
||||||
|
url
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
Maybe.withDefault "INVALID FLAKE ORIGIN" result
|
||||||
|
)
|
||||||
|
resolved
|
||||||
|
|
||||||
|
|
||||||
init :
|
init :
|
||||||
Route.SearchArgs
|
Route.SearchArgs
|
||||||
-> Maybe (Model a b)
|
-> Maybe (Model a b)
|
||||||
-> ( Model a b, Cmd (Msg a b) )
|
-> ( Model a b, Cmd (Msg a b) )
|
||||||
init args maybeModel =
|
init args maybeModel =
|
||||||
let
|
let
|
||||||
|
emptyRoute : Route.SearchArgs
|
||||||
|
emptyRoute =
|
||||||
|
{ query = Nothing
|
||||||
|
, channel = Nothing
|
||||||
|
, show = Nothing
|
||||||
|
, from = Nothing
|
||||||
|
, size = Nothing
|
||||||
|
, buckets = Nothing
|
||||||
|
|
||||||
|
-- TODO= Nothing type
|
||||||
|
, sort = Nothing
|
||||||
|
, type_ = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
-- args =
|
||||||
|
-- Maybe.withDefault emptyRoute maybeArgs
|
||||||
getField getFn default =
|
getField getFn default =
|
||||||
maybeModel
|
maybeModel
|
||||||
|> Maybe.map getFn
|
|> Maybe.map getFn
|
||||||
|
@ -158,6 +231,7 @@ init args maybeModel =
|
||||||
( { channel =
|
( { channel =
|
||||||
args.channel
|
args.channel
|
||||||
|> Maybe.withDefault modelChannel
|
|> Maybe.withDefault modelChannel
|
||||||
|
, flake = defaultFlakeId
|
||||||
, query =
|
, query =
|
||||||
args.query
|
args.query
|
||||||
|> Maybe.andThen Route.SearchQuery.searchQueryToString
|
|> Maybe.andThen Route.SearchQuery.searchQueryToString
|
||||||
|
@ -176,7 +250,8 @@ init args maybeModel =
|
||||||
|> fromSortId
|
|> fromSortId
|
||||||
|> Maybe.withDefault Relevance
|
|> Maybe.withDefault Relevance
|
||||||
, showSort = False
|
, showSort = False
|
||||||
, showNixOSDetails = False
|
, showInstallDetails = Unset
|
||||||
|
, searchType = Maybe.withDefault Route.PackageSearch args.type_
|
||||||
}
|
}
|
||||||
|> ensureLoading
|
|> ensureLoading
|
||||||
, Browser.Dom.focus "search-query-input" |> Task.attempt (\_ -> NoOp)
|
, Browser.Dom.focus "search-query-input" |> Task.attempt (\_ -> NoOp)
|
||||||
|
@ -194,7 +269,7 @@ ensureLoading :
|
||||||
Model a b
|
Model a b
|
||||||
-> Model a b
|
-> Model a b
|
||||||
ensureLoading model =
|
ensureLoading model =
|
||||||
if model.query /= Nothing && model.query /= Just "" && List.member model.channel channels then
|
if model.query /= Nothing && model.query /= Just "" && (List.member model.channel channels || List.member model.channel flakeIds) then
|
||||||
{ model | result = RemoteData.Loading }
|
{ model | result = RemoteData.Loading }
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@ -218,14 +293,20 @@ type Msg a b
|
||||||
| ToggleSort
|
| ToggleSort
|
||||||
| BucketsChange String
|
| BucketsChange String
|
||||||
| ChannelChange String
|
| ChannelChange String
|
||||||
|
| FlakeChange String
|
||||||
|
| SubjectChange SearchType
|
||||||
| QueryInput String
|
| QueryInput String
|
||||||
| QueryInputSubmit
|
| QueryInputSubmit
|
||||||
| QueryResponse (RemoteData.WebData (SearchResult a b))
|
| QueryResponse (RemoteData.WebData (SearchResult a b))
|
||||||
| ShowDetails String
|
| ShowDetails String
|
||||||
| ChangePage Int
|
| ChangePage Int
|
||||||
| ShowNixOSDetails Bool
|
| ShowInstallDetails Details
|
||||||
|
|
||||||
|
|
||||||
|
type Details
|
||||||
|
= FromNixpkgs
|
||||||
|
| FromNixOS
|
||||||
|
| FromFlake
|
||||||
|
| Unset
|
||||||
scrollToEntry :
|
scrollToEntry :
|
||||||
Maybe String
|
Maybe String
|
||||||
-> Cmd (Msg a b)
|
-> Cmd (Msg a b)
|
||||||
|
@ -292,6 +373,26 @@ update toRoute navKey msg model =
|
||||||
|> ensureLoading
|
|> ensureLoading
|
||||||
|> pushUrl toRoute navKey
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
|
FlakeChange flake ->
|
||||||
|
{ model
|
||||||
|
| channel = flake
|
||||||
|
, show = Nothing
|
||||||
|
, buckets = Nothing
|
||||||
|
, from = 0
|
||||||
|
}
|
||||||
|
|> ensureLoading
|
||||||
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
|
SubjectChange subject ->
|
||||||
|
{ model
|
||||||
|
| searchType = subject
|
||||||
|
, show = Nothing
|
||||||
|
, buckets = Nothing
|
||||||
|
, from = 0
|
||||||
|
}
|
||||||
|
|> ensureLoading
|
||||||
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
QueryInput query ->
|
QueryInput query ->
|
||||||
( { model | query = Just query }
|
( { model | query = Just query }
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
|
@ -307,6 +408,10 @@ update toRoute navKey msg model =
|
||||||
|> pushUrl toRoute navKey
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
QueryResponse result ->
|
QueryResponse result ->
|
||||||
|
-- let
|
||||||
|
-- _ =
|
||||||
|
-- Debug.log "got query result" result
|
||||||
|
-- in
|
||||||
( { model
|
( { model
|
||||||
| result = result
|
| result = result
|
||||||
}
|
}
|
||||||
|
@ -329,8 +434,8 @@ update toRoute navKey msg model =
|
||||||
|> ensureLoading
|
|> ensureLoading
|
||||||
|> pushUrl toRoute navKey
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
ShowNixOSDetails show ->
|
ShowInstallDetails details ->
|
||||||
{ model | showNixOSDetails = show }
|
{ model | showInstallDetails = details }
|
||||||
|> pushUrl toRoute navKey
|
|> pushUrl toRoute navKey
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,6 +467,7 @@ createUrl toRoute model =
|
||||||
, size = Just model.size
|
, size = Just model.size
|
||||||
, buckets = model.buckets
|
, buckets = model.buckets
|
||||||
, sort = Just <| toSortId model.sort
|
, sort = Just <| toSortId model.sort
|
||||||
|
, type_ = Just model.searchType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -395,13 +501,14 @@ channelDetails : Channel -> ChannelDetails
|
||||||
channelDetails channel =
|
channelDetails channel =
|
||||||
case channel of
|
case channel of
|
||||||
Unstable ->
|
Unstable ->
|
||||||
ChannelDetails "unstable" "unstable" "nixos/trunk-combined" "nixpkgs-unstable"
|
ChannelDetails "unstable" "unstable" "nixos/trunk-combined" "nixos-unstable"
|
||||||
|
|
||||||
Release_20_09 ->
|
Release_20_09 ->
|
||||||
ChannelDetails "20.09" "20.09" "nixos/release-20.09" "nixpkgs-20.09"
|
ChannelDetails "20.09" "20.09" "nixos/release-20.09" "nixos-20.09"
|
||||||
|
|
||||||
Release_21_05 ->
|
Release_21_05 ->
|
||||||
ChannelDetails "21.05" "21.05" "nixos/release-21.05" "nixpkgs-21.05"
|
ChannelDetails "21.05" "21.05" "nixos/release-21.05" "nixos-21.05"
|
||||||
|
|
||||||
|
|
||||||
channelFromId : String -> Maybe Channel
|
channelFromId : String -> Maybe Channel
|
||||||
channelFromId channel_id =
|
channelFromId channel_id =
|
||||||
|
@ -433,6 +540,68 @@ channels =
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
type alias Flake =
|
||||||
|
{ id : String
|
||||||
|
, isNixpkgs : Bool
|
||||||
|
, title : String
|
||||||
|
, source : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defaultFlakeId : String
|
||||||
|
defaultFlakeId =
|
||||||
|
"group-01"
|
||||||
|
|
||||||
|
|
||||||
|
flakeFromId : String -> Maybe Flake
|
||||||
|
flakeFromId flake_id =
|
||||||
|
let
|
||||||
|
find : String -> List Flake -> Maybe Flake
|
||||||
|
find id_ list =
|
||||||
|
case list of
|
||||||
|
flake :: rest ->
|
||||||
|
if flake.id == id_ then
|
||||||
|
Just flake
|
||||||
|
|
||||||
|
else
|
||||||
|
find id_ rest
|
||||||
|
|
||||||
|
[] ->
|
||||||
|
Nothing
|
||||||
|
in
|
||||||
|
find flake_id flakes
|
||||||
|
|
||||||
|
|
||||||
|
flakeIds : List String
|
||||||
|
flakeIds =
|
||||||
|
List.map .id flakes
|
||||||
|
|
||||||
|
|
||||||
|
flakes : List Flake
|
||||||
|
flakes =
|
||||||
|
[ { id = "latest-nixos-20.09-latest"
|
||||||
|
, isNixpkgs = True
|
||||||
|
, title = "Nixpkgs 20.09"
|
||||||
|
, source = ""
|
||||||
|
}
|
||||||
|
, { id = "nixos-21.05-latest"
|
||||||
|
, isNixpkgs = True
|
||||||
|
, title = "Nixpkgs 21.05"
|
||||||
|
, source = ""
|
||||||
|
}
|
||||||
|
, { id = "latest-nixos-unstable"
|
||||||
|
, isNixpkgs = True
|
||||||
|
, title = "Nixpkgs Unstable"
|
||||||
|
, source = ""
|
||||||
|
}
|
||||||
|
, { id = "flakes"
|
||||||
|
, isNixpkgs = False
|
||||||
|
, title = "Public Flakes"
|
||||||
|
, source = ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
sortBy : List Sort
|
sortBy : List Sort
|
||||||
sortBy =
|
sortBy =
|
||||||
[ Relevance
|
[ Relevance
|
||||||
|
@ -579,7 +748,7 @@ view :
|
||||||
-> Model a b
|
-> Model a b
|
||||||
->
|
->
|
||||||
(String
|
(String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> List (ResultItem a)
|
-> List (ResultItem a)
|
||||||
-> Html c
|
-> Html c
|
||||||
|
@ -590,8 +759,9 @@ view :
|
||||||
-> List (Html c)
|
-> List (Html c)
|
||||||
)
|
)
|
||||||
-> (Msg a b -> c)
|
-> (Msg a b -> c)
|
||||||
|
-> List (Html c)
|
||||||
-> Html c
|
-> Html c
|
||||||
view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg =
|
view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg searchBuckets =
|
||||||
let
|
let
|
||||||
resultStatus =
|
resultStatus =
|
||||||
case model.result of
|
case model.result of
|
||||||
|
@ -619,7 +789,7 @@ view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg =
|
||||||
)
|
)
|
||||||
[ h1 [] title
|
[ h1 [] title
|
||||||
, viewSearchInput outMsg categoryName model.channel model.query
|
, viewSearchInput outMsg categoryName model.channel model.query
|
||||||
, viewResult outMsg toRoute categoryName model viewSuccess viewBuckets
|
, viewResult outMsg toRoute categoryName model viewSuccess viewBuckets searchBuckets
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -630,7 +800,7 @@ viewResult :
|
||||||
-> Model a b
|
-> Model a b
|
||||||
->
|
->
|
||||||
(String
|
(String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> List (ResultItem a)
|
-> List (ResultItem a)
|
||||||
-> Html c
|
-> Html c
|
||||||
|
@ -640,15 +810,17 @@ viewResult :
|
||||||
-> SearchResult a b
|
-> SearchResult a b
|
||||||
-> List (Html c)
|
-> List (Html c)
|
||||||
)
|
)
|
||||||
|
-> List (Html c)
|
||||||
-> Html c
|
-> Html c
|
||||||
viewResult outMsg toRoute categoryName model viewSuccess viewBuckets =
|
viewResult outMsg toRoute categoryName model viewSuccess viewBuckets searchBuckets =
|
||||||
case model.result of
|
case model.result of
|
||||||
RemoteData.NotAsked ->
|
RemoteData.NotAsked ->
|
||||||
div [] [ text "" ]
|
div [] [ ]
|
||||||
|
|
||||||
RemoteData.Loading ->
|
RemoteData.Loading ->
|
||||||
div [ class "loader-wrapper" ]
|
div [ class "loader-wrapper" ]
|
||||||
[ div [ class "loader" ] [ text "Loading..." ]
|
[ ul [ class "search-sidebar" ] searchBuckets
|
||||||
|
, div [ class "loader" ] [ text "Loading..." ]
|
||||||
, h2 [] [ text "Searching..." ]
|
, h2 [] [ text "Searching..." ]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -658,18 +830,22 @@ viewResult outMsg toRoute categoryName model viewSuccess viewBuckets =
|
||||||
viewBuckets model.buckets result
|
viewBuckets model.buckets result
|
||||||
in
|
in
|
||||||
if result.hits.total.value == 0 && List.length buckets == 0 then
|
if result.hits.total.value == 0 && List.length buckets == 0 then
|
||||||
viewNoResults categoryName
|
div [ class "search-results" ]
|
||||||
|
[ ul [ class "search-sidebar" ] searchBuckets
|
||||||
|
, viewNoResults categoryName
|
||||||
|
]
|
||||||
|
|
||||||
else if List.length buckets > 0 then
|
else if List.length buckets > 0 then
|
||||||
div [ class "search-results" ]
|
div [ class "search-results" ]
|
||||||
[ ul [] buckets
|
[ ul [ class "search-sidebar" ] <| List.append searchBuckets buckets
|
||||||
, div []
|
, div []
|
||||||
(viewResults model result viewSuccess toRoute outMsg categoryName)
|
(viewResults model result viewSuccess toRoute outMsg categoryName)
|
||||||
]
|
]
|
||||||
|
|
||||||
else
|
else
|
||||||
div [ class "search-results" ]
|
div [ class "search-results" ]
|
||||||
[ div []
|
[ ul [ class "search-sidebar" ] searchBuckets
|
||||||
|
, div []
|
||||||
(viewResults model result viewSuccess toRoute outMsg categoryName)
|
(viewResults model result viewSuccess toRoute outMsg categoryName)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -694,7 +870,8 @@ viewResult outMsg toRoute categoryName model viewSuccess viewBuckets =
|
||||||
in
|
in
|
||||||
div []
|
div []
|
||||||
[ div [ class "alert alert-error" ]
|
[ div [ class "alert alert-error" ]
|
||||||
[ h4 [] [ text errorTitle ]
|
[ ul [ class "search-sidebar" ] searchBuckets
|
||||||
|
, h4 [] [ text errorTitle ]
|
||||||
, text errorMessage
|
, text errorMessage
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -793,7 +970,7 @@ viewResults :
|
||||||
-> SearchResult a b
|
-> SearchResult a b
|
||||||
->
|
->
|
||||||
(String
|
(String
|
||||||
-> Bool
|
-> Details
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> List (ResultItem a)
|
-> List (ResultItem a)
|
||||||
-> Html c
|
-> Html c
|
||||||
|
@ -845,7 +1022,7 @@ viewResults model result viewSuccess toRoute outMsg categoryName =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
, viewSuccess model.channel model.showNixOSDetails model.show result.hits.hits
|
, viewSuccess model.channel model.showInstallDetails model.show result.hits.hits
|
||||||
, Html.map outMsg <| viewPager model result.hits.total.value
|
, Html.map outMsg <| viewPager model result.hits.total.value
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1139,7 +1316,7 @@ makeRequest :
|
||||||
-> Maybe String
|
-> Maybe String
|
||||||
-> Cmd (Msg a b)
|
-> Cmd (Msg a b)
|
||||||
makeRequest body channel decodeResultItemSource decodeResultAggregations options responseMsg tracker =
|
makeRequest body channel decodeResultItemSource decodeResultAggregations options responseMsg tracker =
|
||||||
let branch = Maybe.map (\details -> details.branch) (channelDetailsFromId channel) |> Maybe.withDefault ""
|
let branch = Maybe.map (\details -> details.branch) (channelDetailsFromId channel) |> Maybe.withDefault channel
|
||||||
index = "latest-" ++ String.fromInt options.mappingSchemaVersion ++ "-" ++ branch
|
index = "latest-" ++ String.fromInt options.mappingSchemaVersion ++ "-" ++ branch
|
||||||
in
|
in
|
||||||
Http.riskyRequest
|
Http.riskyRequest
|
||||||
|
|
29
src/View/Components.elm
Normal file
29
src/View/Components.elm
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module View.Components exposing (..)
|
||||||
|
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Route exposing (SearchRoute)
|
||||||
|
import Search exposing (Model, Msg, ResultItem, SearchResult)
|
||||||
|
import View.Components.Body
|
||||||
|
import Search exposing (Details)
|
||||||
|
|
||||||
|
|
||||||
|
body :
|
||||||
|
{ toRoute : SearchRoute, categoryName : String }
|
||||||
|
-> List (Html c)
|
||||||
|
-> Model a b
|
||||||
|
->
|
||||||
|
(String
|
||||||
|
-> Details
|
||||||
|
-> Maybe String
|
||||||
|
-> List (ResultItem a)
|
||||||
|
-> Html c
|
||||||
|
)
|
||||||
|
->
|
||||||
|
(Maybe String
|
||||||
|
-> SearchResult a b
|
||||||
|
-> List (Html c)
|
||||||
|
)
|
||||||
|
-> (Msg a b -> c)
|
||||||
|
-> Html c
|
||||||
|
body =
|
||||||
|
View.Components.Body.view
|
65
src/View/Components/Body.elm
Normal file
65
src/View/Components/Body.elm
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
module View.Components.Body exposing (..)
|
||||||
|
|
||||||
|
import Html exposing (Html, div, h1)
|
||||||
|
import Html.Attributes exposing (class)
|
||||||
|
import Html.Events exposing (onClick)
|
||||||
|
import RemoteData exposing (RemoteData(..))
|
||||||
|
import Route
|
||||||
|
import Search exposing (Model, Msg(..), ResultItem, SearchResult, viewResult)
|
||||||
|
import View.Components.SearchInput exposing (viewSearchInput)
|
||||||
|
import View.Components.SearchInput exposing (viewFlakes)
|
||||||
|
import Html exposing (details)
|
||||||
|
import Search exposing (Details)
|
||||||
|
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ toRoute : Route.SearchRoute
|
||||||
|
, categoryName : String
|
||||||
|
}
|
||||||
|
-> List (Html c)
|
||||||
|
-> Model a b
|
||||||
|
->
|
||||||
|
(String
|
||||||
|
-> Details
|
||||||
|
-> Maybe String
|
||||||
|
-> List (ResultItem a)
|
||||||
|
-> Html c
|
||||||
|
)
|
||||||
|
->
|
||||||
|
(Maybe String
|
||||||
|
-> SearchResult a b
|
||||||
|
-> List (Html c)
|
||||||
|
)
|
||||||
|
-> (Msg a b -> c)
|
||||||
|
-> Html c
|
||||||
|
view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg =
|
||||||
|
let
|
||||||
|
resultStatus =
|
||||||
|
case model.result of
|
||||||
|
RemoteData.NotAsked ->
|
||||||
|
"not-asked"
|
||||||
|
|
||||||
|
RemoteData.Loading ->
|
||||||
|
"loading"
|
||||||
|
|
||||||
|
RemoteData.Success _ ->
|
||||||
|
"success"
|
||||||
|
|
||||||
|
RemoteData.Failure _ ->
|
||||||
|
"failure"
|
||||||
|
|
||||||
|
in
|
||||||
|
div
|
||||||
|
(List.append
|
||||||
|
[ class <| "search-page " ++ resultStatus ]
|
||||||
|
(if model.showSort then
|
||||||
|
[ onClick (outMsg ToggleSort) ]
|
||||||
|
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
[ h1 [] title
|
||||||
|
, viewSearchInput outMsg model.searchType model.query
|
||||||
|
, viewResult outMsg toRoute categoryName model viewSuccess viewBuckets <| viewFlakes outMsg model.channel model.searchType
|
||||||
|
]
|
118
src/View/Components/SearchInput.elm
Normal file
118
src/View/Components/SearchInput.elm
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
module View.Components.SearchInput exposing (..)
|
||||||
|
|
||||||
|
import Html exposing (Html, a, button, div, form, h4, input, li, p, span, text, th, ul)
|
||||||
|
import Html.Attributes exposing (attribute, autofocus, class, classList, href, id, placeholder, selected, type_, value)
|
||||||
|
import Html.Events exposing (onClick, onInput, onSubmit)
|
||||||
|
import Route exposing (SearchType, allTypes, searchTypeToString, searchTypeToTitle)
|
||||||
|
import Search exposing (Msg(..))
|
||||||
|
|
||||||
|
|
||||||
|
viewSearchInput :
|
||||||
|
(Msg a b -> c)
|
||||||
|
-> SearchType
|
||||||
|
-> Maybe String
|
||||||
|
-> Html c
|
||||||
|
viewSearchInput outMsg category searchQuery =
|
||||||
|
let
|
||||||
|
searchHint =
|
||||||
|
Maybe.withDefault "Packages and Options" <| Maybe.map (\_ -> searchTypeToString category) searchQuery
|
||||||
|
in
|
||||||
|
form
|
||||||
|
[ onSubmit (outMsg QueryInputSubmit)
|
||||||
|
, class "search-input"
|
||||||
|
]
|
||||||
|
[ div []
|
||||||
|
[ div []
|
||||||
|
[ input
|
||||||
|
[ type_ "text"
|
||||||
|
, id "search-query-input"
|
||||||
|
, autofocus True
|
||||||
|
, placeholder <| "Search for " ++ searchHint
|
||||||
|
, onInput (outMsg << QueryInput)
|
||||||
|
, value <| Maybe.withDefault "" searchQuery
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
]
|
||||||
|
, button [ class "btn", type_ "submit" ]
|
||||||
|
[ text "Search" ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
viewFlakes : (Msg a b -> msg) -> String -> SearchType -> List (Html msg)
|
||||||
|
viewFlakes outMsg selectedFlake selectedCategory =
|
||||||
|
[ li []
|
||||||
|
[ ul []
|
||||||
|
(List.map
|
||||||
|
(\category ->
|
||||||
|
li []
|
||||||
|
[ a
|
||||||
|
[ href "#"
|
||||||
|
, onClick <| outMsg (SubjectChange category)
|
||||||
|
, classList
|
||||||
|
[ ( "selected"
|
||||||
|
, category == selectedCategory
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ span [] [ text <| searchTypeToTitle category ]
|
||||||
|
, closeButton
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
allTypes
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
closeButton : Html a
|
||||||
|
closeButton =
|
||||||
|
span [] []
|
||||||
|
|
||||||
|
|
||||||
|
viewBucket :
|
||||||
|
String
|
||||||
|
-> List Search.AggregationsBucketItem
|
||||||
|
-> (String -> a)
|
||||||
|
-> List String
|
||||||
|
-> List (Html a)
|
||||||
|
-> List (Html a)
|
||||||
|
viewBucket title buckets searchMsgFor selectedBucket sets =
|
||||||
|
List.append
|
||||||
|
sets
|
||||||
|
(if List.isEmpty buckets then
|
||||||
|
[]
|
||||||
|
|
||||||
|
else
|
||||||
|
[ li []
|
||||||
|
[ ul []
|
||||||
|
(List.append
|
||||||
|
[ li [ class "header" ] [ text title ] ]
|
||||||
|
(List.map
|
||||||
|
(\bucket ->
|
||||||
|
li []
|
||||||
|
[ a
|
||||||
|
[ href "#"
|
||||||
|
, onClick <| searchMsgFor bucket.key
|
||||||
|
, classList
|
||||||
|
[ ( "selected"
|
||||||
|
, List.member bucket.key selectedBucket
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ span [] [ text bucket.key ]
|
||||||
|
, if List.member bucket.key selectedBucket then
|
||||||
|
closeButton
|
||||||
|
|
||||||
|
else
|
||||||
|
span [] [ span [ class "badge" ] [ text <| String.fromInt bucket.doc_count ] ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
buckets
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
|
@ -86,6 +86,9 @@ header .navbar.navbar-static-top {
|
||||||
}
|
}
|
||||||
ul.nav > li {
|
ul.nav > li {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
sup {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,17 +176,28 @@ header .navbar.navbar-static-top {
|
||||||
|
|
||||||
|
|
||||||
.search-result-button {
|
.search-result-button {
|
||||||
&:hover {
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:nth-child(1):after {
|
||||||
|
content: "→";
|
||||||
|
margin: 0 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
& > .search-results {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
// Buckets
|
// Buckets
|
||||||
& > ul {
|
ul.search-sidebar {
|
||||||
|
width: 25em;
|
||||||
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0 1em 0 0;
|
margin: 0 1em 0 0;
|
||||||
|
|
||||||
|
@ -208,7 +222,7 @@ header .navbar.navbar-static-top {
|
||||||
|
|
||||||
& > a {
|
& > a {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto max-content;
|
||||||
color: #333;
|
color: #333;
|
||||||
padding: 0.5em 0.5em 0.5em 1em;
|
padding: 0.5em 0.5em 0.5em 1em;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -232,30 +246,30 @@ header .navbar.navbar-static-top {
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "\2715";
|
|
||||||
font-size: 1.7em;
|
|
||||||
color: #fff;
|
|
||||||
position: absolute;
|
|
||||||
top: 4px;
|
|
||||||
right: 4px;
|
|
||||||
background: #0081c2;
|
|
||||||
bottom: 4px;
|
|
||||||
width: 1em;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > span:last-child {
|
& > span:last-child {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .close {
|
||||||
|
opacity: 1;
|
||||||
|
text-shadow: none;
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding-left: .5em;
|
||||||
|
padding-right: .5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
& > .search-results {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
// Results section
|
// Results section
|
||||||
& > div {
|
& > div {
|
||||||
|
|
Loading…
Reference in a new issue