2021-08-17 08:55:08 +00:00
|
|
|
/// This module defines the unified putput format as expected by the elastic search
|
|
|
|
/// Additionally, we implement converseions from the two possible input formats, i.e.
|
|
|
|
/// Flakes, or Nixpkgs.
|
2021-12-26 15:03:09 +00:00
|
|
|
use std::{convert::TryInto, path::PathBuf};
|
2021-08-17 08:55:08 +00:00
|
|
|
|
2022-01-06 14:42:31 +00:00
|
|
|
use super::{import::{DocValue, ModulePath}, pandoc::PandocExt};
|
2021-08-17 08:55:08 +00:00
|
|
|
use crate::data::import::NixOption;
|
2021-12-06 17:23:10 +00:00
|
|
|
use log::error;
|
2021-08-17 08:55:08 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-12-26 15:03:09 +00:00
|
|
|
use serde_json::Value;
|
2021-08-17 08:55:08 +00:00
|
|
|
|
|
|
|
use super::{
|
|
|
|
import,
|
2021-11-11 19:29:44 +00:00
|
|
|
prettyprint::print_value,
|
2021-08-17 08:55:08 +00:00
|
|
|
system::System,
|
|
|
|
utility::{AttributeQuery, Flatten, OneOrMany, Reverse},
|
|
|
|
};
|
2021-09-14 07:49:33 +00:00
|
|
|
use lazy_static::lazy_static;
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
static ref FILTERS_PATH: PathBuf = std::env::var("NIXPKGS_PANDOC_FILTERS_PATH")
|
|
|
|
.unwrap_or("".into())
|
|
|
|
.into();
|
|
|
|
}
|
2021-08-17 08:55:08 +00:00
|
|
|
|
|
|
|
type Flake = super::Flake;
|
|
|
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct License {
|
|
|
|
url: Option<String>,
|
|
|
|
fullName: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<import::License> for License {
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
fn from(license: import::License) -> Self {
|
|
|
|
match license {
|
|
|
|
import::License::None { .. } => License {
|
|
|
|
url: None,
|
|
|
|
fullName: "No License Specified".to_string(),
|
|
|
|
},
|
|
|
|
import::License::Simple { license } => License {
|
|
|
|
url: None,
|
|
|
|
fullName: license,
|
|
|
|
},
|
|
|
|
import::License::Full { fullName, url, .. } => License { url, fullName },
|
|
|
|
import::License::Url { url } => License {
|
|
|
|
url: Some(url),
|
|
|
|
fullName: "No Name".into(),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----- Unified derivation representation
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
#[serde(tag = "type")]
|
|
|
|
pub enum Derivation {
|
|
|
|
#[serde(rename = "package")]
|
|
|
|
Package {
|
|
|
|
package_attr_name: String,
|
|
|
|
package_attr_name_reverse: Reverse<String>,
|
|
|
|
package_attr_name_query: AttributeQuery,
|
|
|
|
package_attr_name_query_reverse: Reverse<AttributeQuery>,
|
|
|
|
package_attr_set: String,
|
|
|
|
package_attr_set_reverse: Reverse<String>,
|
|
|
|
package_pname: String,
|
|
|
|
package_pname_reverse: Reverse<String>,
|
|
|
|
package_pversion: String,
|
|
|
|
package_platforms: Vec<System>,
|
|
|
|
package_outputs: Vec<String>,
|
2022-03-27 13:52:54 +00:00
|
|
|
package_default_output: Option<String>,
|
2021-08-17 08:55:08 +00:00
|
|
|
package_license: Vec<License>,
|
|
|
|
package_license_set: Vec<String>,
|
|
|
|
package_maintainers: Vec<Maintainer>,
|
|
|
|
package_maintainers_set: Vec<String>,
|
|
|
|
package_description: Option<String>,
|
|
|
|
package_description_reverse: Option<Reverse<String>>,
|
|
|
|
package_longDescription: Option<String>,
|
|
|
|
package_longDescription_reverse: Option<Reverse<String>>,
|
|
|
|
package_hydra: (),
|
|
|
|
package_system: String,
|
|
|
|
package_homepage: Vec<String>,
|
|
|
|
package_position: Option<String>,
|
|
|
|
},
|
|
|
|
#[serde(rename = "app")]
|
|
|
|
App {
|
|
|
|
app_attr_name: String,
|
|
|
|
app_platforms: Vec<System>,
|
|
|
|
|
|
|
|
app_type: Option<String>,
|
|
|
|
|
|
|
|
app_bin: Option<PathBuf>,
|
|
|
|
},
|
|
|
|
#[serde(rename = "option")]
|
|
|
|
Option {
|
|
|
|
option_source: Option<String>,
|
|
|
|
option_name: String,
|
|
|
|
option_name_reverse: Reverse<String>,
|
|
|
|
option_name_query: AttributeQuery,
|
|
|
|
option_name_query_reverse: Reverse<AttributeQuery>,
|
|
|
|
|
|
|
|
option_description: Option<String>,
|
|
|
|
option_description_reverse: Option<Reverse<String>>,
|
|
|
|
|
|
|
|
option_type: Option<String>,
|
|
|
|
|
2021-12-26 15:03:09 +00:00
|
|
|
option_default: Option<DocValue>,
|
2021-08-17 08:55:08 +00:00
|
|
|
|
2021-12-26 15:03:09 +00:00
|
|
|
option_example: Option<DocValue>,
|
2021-08-17 08:55:08 +00:00
|
|
|
|
2022-01-06 14:42:31 +00:00
|
|
|
option_flake: Option<ModulePath>,
|
2021-08-17 08:55:08 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----- Conversions
|
|
|
|
|
|
|
|
impl From<(import::FlakeEntry, super::Flake)> for Derivation {
|
|
|
|
fn from((d, f): (import::FlakeEntry, super::Flake)) -> Self {
|
|
|
|
match d {
|
|
|
|
import::FlakeEntry::Package {
|
|
|
|
attribute_name,
|
|
|
|
name,
|
|
|
|
version,
|
|
|
|
platforms,
|
|
|
|
outputs,
|
2022-03-27 13:52:54 +00:00
|
|
|
default_output,
|
2021-08-17 08:55:08 +00:00
|
|
|
description,
|
|
|
|
license,
|
|
|
|
} => {
|
|
|
|
let package_attr_set: Vec<_> = attribute_name.split(".").collect();
|
|
|
|
let package_attr_set: String = (if package_attr_set.len() > 1 {
|
|
|
|
package_attr_set[0]
|
|
|
|
} else {
|
|
|
|
"No package set"
|
|
|
|
})
|
|
|
|
.into();
|
|
|
|
|
|
|
|
let package_attr_set_reverse = Reverse(package_attr_set.clone());
|
|
|
|
|
|
|
|
let package_license: Vec<License> = vec![license.into()];
|
|
|
|
let package_license_set: Vec<String> = package_license
|
|
|
|
.iter()
|
|
|
|
.clone()
|
|
|
|
.map(|l| l.fullName.to_owned())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let maintainer: Maintainer = f.into();
|
|
|
|
|
|
|
|
Derivation::Package {
|
|
|
|
package_attr_name_query: AttributeQuery::new(&attribute_name),
|
|
|
|
package_attr_name_query_reverse: Reverse(AttributeQuery::new(&attribute_name)),
|
|
|
|
package_attr_name: attribute_name.clone(),
|
|
|
|
package_attr_name_reverse: Reverse(attribute_name),
|
|
|
|
package_attr_set,
|
|
|
|
package_attr_set_reverse,
|
|
|
|
package_pname: name.clone(),
|
|
|
|
package_pname_reverse: Reverse(name),
|
|
|
|
package_pversion: version,
|
|
|
|
package_platforms: platforms,
|
|
|
|
package_outputs: outputs,
|
2022-03-27 13:52:54 +00:00
|
|
|
package_default_output: Some(default_output),
|
2021-08-17 08:55:08 +00:00
|
|
|
package_license,
|
|
|
|
package_license_set,
|
|
|
|
package_description: description.clone(),
|
|
|
|
package_maintainers: vec![maintainer.clone()],
|
|
|
|
package_maintainers_set: maintainer.name.map_or(vec![], |n| vec![n]),
|
|
|
|
package_description_reverse: description.map(Reverse),
|
|
|
|
package_longDescription: None,
|
|
|
|
package_longDescription_reverse: None,
|
|
|
|
package_hydra: (),
|
|
|
|
package_system: String::new(),
|
|
|
|
package_homepage: Vec::new(),
|
|
|
|
package_position: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
import::FlakeEntry::App {
|
|
|
|
bin,
|
|
|
|
attribute_name,
|
|
|
|
platforms,
|
|
|
|
app_type,
|
|
|
|
} => Derivation::App {
|
|
|
|
app_attr_name: attribute_name,
|
|
|
|
app_platforms: platforms,
|
|
|
|
app_bin: bin,
|
|
|
|
app_type,
|
|
|
|
},
|
|
|
|
import::FlakeEntry::Option(option) => option.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<import::NixpkgsEntry> for Derivation {
|
|
|
|
fn from(entry: import::NixpkgsEntry) -> Self {
|
|
|
|
match entry {
|
|
|
|
import::NixpkgsEntry::Derivation { attribute, package } => {
|
|
|
|
let package_attr_set: Vec<_> = attribute.split(".").collect();
|
|
|
|
let package_attr_set: String = (if package_attr_set.len() > 1 {
|
|
|
|
package_attr_set[0]
|
|
|
|
} else {
|
|
|
|
"No package set"
|
|
|
|
})
|
|
|
|
.into();
|
|
|
|
|
|
|
|
let package_attr_set_reverse = Reverse(package_attr_set.clone());
|
|
|
|
|
|
|
|
let package_license: Vec<_> = package
|
|
|
|
.meta
|
|
|
|
.license
|
|
|
|
.map(OneOrMany::into_list)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.into_iter()
|
|
|
|
.map(|sos| sos.0.into())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let package_license_set = package_license
|
|
|
|
.iter()
|
|
|
|
.map(|l: &License| l.fullName.to_owned())
|
|
|
|
.collect();
|
|
|
|
|
2021-12-18 13:45:08 +00:00
|
|
|
let package_maintainers: Vec<Maintainer> = package
|
2021-08-17 08:55:08 +00:00
|
|
|
.meta
|
|
|
|
.maintainers
|
2021-12-18 13:45:08 +00:00
|
|
|
.map_or(Default::default(), Flatten::flatten)
|
|
|
|
.into_iter()
|
|
|
|
.map(Into::into)
|
|
|
|
.collect();
|
2021-08-17 08:55:08 +00:00
|
|
|
|
|
|
|
let package_maintainers_set = package_maintainers
|
|
|
|
.iter()
|
|
|
|
.filter(|m| m.name.is_some())
|
|
|
|
.map(|m| m.name.to_owned().unwrap())
|
|
|
|
.collect();
|
|
|
|
|
2021-08-25 22:40:42 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-08-17 08:55:08 +00:00
|
|
|
Derivation::Package {
|
|
|
|
package_attr_name: attribute.clone(),
|
|
|
|
package_attr_name_reverse: Reverse(attribute.clone()),
|
|
|
|
package_attr_name_query: AttributeQuery::new(&attribute),
|
|
|
|
package_attr_name_query_reverse: Reverse(AttributeQuery::new(&attribute)),
|
|
|
|
package_attr_set,
|
|
|
|
package_attr_set_reverse,
|
|
|
|
package_pname: package.pname.clone(),
|
|
|
|
package_pname_reverse: Reverse(package.pname),
|
|
|
|
package_pversion: package.version,
|
|
|
|
package_platforms: package
|
|
|
|
.meta
|
|
|
|
.platforms
|
|
|
|
.map(Flatten::flatten)
|
|
|
|
.unwrap_or_default(),
|
2022-03-16 09:43:20 +00:00
|
|
|
package_outputs: package.outputs.into_keys().collect(),
|
2022-03-27 13:52:54 +00:00
|
|
|
package_default_output: package.default_output,
|
2021-08-17 08:55:08 +00:00
|
|
|
package_license,
|
|
|
|
package_license_set,
|
|
|
|
package_maintainers,
|
|
|
|
package_maintainers_set,
|
|
|
|
package_description: package.meta.description.clone(),
|
|
|
|
package_description_reverse: package.meta.description.map(Reverse),
|
|
|
|
package_longDescription: package.meta.long_description.clone(),
|
|
|
|
package_longDescription_reverse: package.meta.long_description.map(Reverse),
|
|
|
|
package_hydra: (),
|
|
|
|
package_system: package.system,
|
|
|
|
package_homepage: package
|
|
|
|
.meta
|
|
|
|
.homepage
|
|
|
|
.map_or(Default::default(), OneOrMany::into_list),
|
2021-08-25 22:40:42 +00:00
|
|
|
package_position: position,
|
2021-08-17 08:55:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
import::NixpkgsEntry::Option(option) => option.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<import::NixOption> for Derivation {
|
|
|
|
fn from(
|
|
|
|
NixOption {
|
|
|
|
declarations,
|
|
|
|
description,
|
|
|
|
name,
|
|
|
|
option_type,
|
|
|
|
default,
|
|
|
|
example,
|
|
|
|
flake,
|
|
|
|
}: import::NixOption,
|
|
|
|
) -> Self {
|
2021-12-26 15:03:09 +00:00
|
|
|
let description = description
|
|
|
|
.as_ref()
|
|
|
|
.map(PandocExt::render)
|
|
|
|
.transpose()
|
|
|
|
.expect(&format!("Could not render descript of `{}`", name));
|
|
|
|
let option_default = default;
|
|
|
|
// .map(TryInto::try_into)
|
|
|
|
// .transpose()
|
|
|
|
// .expect(&format!("Could not render option_default of `{}`", name));
|
|
|
|
let option_example = example;
|
|
|
|
// .map(TryInto::try_into)
|
|
|
|
// .transpose()
|
|
|
|
// .expect(&format!("Could not render option_example of `{}`", name));
|
|
|
|
let option_type = option_type;
|
|
|
|
// .map(TryInto::try_into)
|
|
|
|
// .transpose()
|
|
|
|
// .expect(&format!("Could not render option_type of `{}`", name));
|
2021-09-14 07:49:33 +00:00
|
|
|
|
2021-08-17 08:55:08 +00:00
|
|
|
Derivation::Option {
|
|
|
|
option_source: declarations.get(0).map(Clone::clone),
|
|
|
|
option_name: name.clone(),
|
|
|
|
option_name_reverse: Reverse(name.clone()),
|
|
|
|
option_description: description.clone(),
|
|
|
|
option_description_reverse: description.map(Reverse),
|
2021-12-26 15:03:09 +00:00
|
|
|
option_default,
|
|
|
|
option_example,
|
2021-08-17 08:55:08 +00:00
|
|
|
option_flake: flake,
|
|
|
|
option_type,
|
|
|
|
option_name_query: AttributeQuery::new(&name),
|
|
|
|
option_name_query_reverse: Reverse(AttributeQuery::new(&name)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-18 13:45:08 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct Maintainer {
|
|
|
|
name: Option<String>,
|
|
|
|
github: Option<String>,
|
|
|
|
email: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<import::Maintainer> for Maintainer {
|
|
|
|
fn from(import: import::Maintainer) -> Self {
|
|
|
|
match import {
|
|
|
|
import::Maintainer::Full {
|
|
|
|
name,
|
|
|
|
github,
|
|
|
|
email,
|
|
|
|
} => Maintainer {
|
|
|
|
name,
|
|
|
|
github,
|
|
|
|
email,
|
|
|
|
},
|
|
|
|
import::Maintainer::Simple(name) => Maintainer {
|
|
|
|
name: Some(name),
|
|
|
|
github: None,
|
|
|
|
email: None,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-08-17 08:55:08 +00:00
|
|
|
|
|
|
|
impl From<super::Flake> for Maintainer {
|
|
|
|
fn from(flake: super::Flake) -> Self {
|
|
|
|
let github = flake
|
|
|
|
.source
|
|
|
|
.and_then(|source| match source {
|
|
|
|
super::Source::Github { owner, .. } => Some(owner),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| "Maintainer Unknown".to_string());
|
|
|
|
|
|
|
|
Maintainer {
|
|
|
|
github: Some(github),
|
|
|
|
email: None,
|
|
|
|
name: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ----- output type
|
|
|
|
|
|
|
|
/// Export type that brings together derivation and optional flake info
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
|
|
|
pub struct Export {
|
|
|
|
#[serde(flatten)]
|
|
|
|
flake: Option<Flake>,
|
|
|
|
|
|
|
|
#[serde(flatten)]
|
|
|
|
item: Derivation,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Export {
|
|
|
|
/// Construct Export from Flake and Flake entry
|
|
|
|
pub fn flake(flake: Flake, item: import::FlakeEntry) -> Self {
|
|
|
|
Self {
|
|
|
|
flake: Some(flake.clone()),
|
|
|
|
item: Derivation::from((item, flake)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct Export from NixpkgsEntry
|
|
|
|
pub fn nixpkgs(item: import::NixpkgsEntry) -> Self {
|
|
|
|
Self {
|
|
|
|
flake: None,
|
|
|
|
item: Derivation::from(item),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_option() {
|
|
|
|
let option: NixOption = serde_json::from_str(r#"
|
|
|
|
{
|
|
|
|
"declarations":["/nix/store/s1q1238ahiks5a4g6j6qhhfb3rlmamvz-source/nixos/modules/system/boot/luksroot.nix"],
|
2021-12-26 15:03:09 +00:00
|
|
|
"default": {"one": 1, "two" : { "three": "tree", "four": []}},
|
2021-08-17 08:55:08 +00:00
|
|
|
"description":"Commands that should be run right after we have mounted our LUKS device.\n",
|
2021-12-26 15:03:09 +00:00
|
|
|
"example":null,
|
2021-08-17 08:55:08 +00:00
|
|
|
"internal":false,
|
|
|
|
"loc":["boot","initrd","luks","devices","<name>","postOpenCommands"],
|
|
|
|
"name":"boot.initrd.luks.devices.<name>.postOpenCommands",
|
2021-12-26 15:03:09 +00:00
|
|
|
"readOnly":false,
|
|
|
|
"type": "boolean",
|
|
|
|
"visible":true
|
2021-08-17 08:55:08 +00:00
|
|
|
}"#).unwrap();
|
|
|
|
|
|
|
|
let option: Derivation = option.into();
|
|
|
|
|
|
|
|
println!("{}", serde_json::to_string_pretty(&option).unwrap());
|
|
|
|
}
|
|
|
|
}
|