aux-search/flake-info/src/data/utility.rs

238 lines
6.3 KiB
Rust
Raw Normal View History

2021-08-17 08:55:08 +00:00
use fancy_regex::Regex;
use lazy_static::lazy_static;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq)]
pub struct Reverse<T: Reversable + Serialize>(pub T);
pub trait Reversable {
fn reverse(&self) -> Self;
}
impl Reversable for String {
fn reverse(&self) -> Self {
self.chars().rev().collect::<String>()
}
}
impl<T: Reversable + Clone> Reversable for Vec<T> {
fn reverse(&self) -> Self {
self.iter().cloned().map(|item| item.reverse()).collect()
}
}
impl<T> Reversable for Reverse<T>
where
T: Reversable + Serialize,
{
fn reverse(&self) -> Self {
Reverse(self.0.reverse())
}
}
impl<T> Serialize for Reverse<T>
where
T: Reversable + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.reverse().serialize(serializer)
}
}
impl<'de, T> Deserialize<'de> for Reverse<T>
where
T: Reversable + Serialize + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Reverse<T>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Reverse(T::deserialize(deserializer)?.reverse()))
}
}
/// A utility type that can represent the presence of either a single associated
/// value or a list of those. Adding absence can be achieved by wrapping the type
/// in an [Option]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OneOrMany<T> {
#[serde(serialize_with = "list")]
One(T),
Many(Vec<T>),
}
impl<T> OneOrMany<T> {
pub fn into_list(self) -> Vec<T> {
match self {
OneOrMany::One(one) => vec![one],
OneOrMany::Many(many) => many,
}
}
}
/// A utility type that flattens lists of lists as seen with `maintainers` and `platforms` on selected packages
/// in an [Option]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Flatten<T> {
#[serde(serialize_with = "list")]
Single(T),
Deep(Vec<Flatten<T>>),
}
impl<T: Clone> Flatten<T> {
pub fn flatten(self) -> Vec<T> {
match self {
Flatten::Single(s) => vec![s],
Flatten::Deep(v) => v.into_iter().map(Flatten::flatten).flatten().collect(),
}
}
}
// TODO: use this or a to_ist function?
/// Serialization helper that serializes single elements as a list with a single
/// item
pub fn list<T, S>(item: &T, s: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer,
{
s.collect_seq(vec![item].iter())
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AttributeQuery(Vec<String>);
lazy_static! {
static ref QUERY: Regex =
Regex::new(".+?(?:(?<=[a-z])(?=[1-9A-Z])|(?<=[1-9A-Z])(?=[A-Z][a-z])|[._-]|$)").unwrap();
}
impl AttributeQuery {
pub fn new(attribute_name: &str) -> Self {
const SUFFIX: &[char] = &['-', '.', '_'];
let matches = QUERY
.find_iter(attribute_name)
.map(|found| found.unwrap().as_str())
.collect::<Vec<_>>();
let tokens = (0..matches.len())
.flat_map(|index| {
let (_, tokens) = matches.iter().skip(index).fold(
(String::new(), Vec::new()),
|(prev_parts, mut tokens), part| {
let token: String = prev_parts + part;
tokens.push(token.trim_end_matches(SUFFIX).to_owned());
(token, tokens)
},
);
tokens
})
.collect::<Vec<_>>();
AttributeQuery(tokens)
}
pub fn query(&self) -> &[String] {
&self.0
}
}
impl Reversable for AttributeQuery {
fn reverse(&self) -> Self {
AttributeQuery(self.query().to_owned().reverse())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attr_query_test() {
assert_eq!(
{
let mut q = AttributeQuery::new("services.nginx.extraConfig")
.query()
.to_owned();
q.sort();
q
},
{
let mut ex = [
"services.nginx.extraConfig",
"services.nginx.extra",
"services.nginx",
"services",
"nginx.extraConfig",
"nginx.extra",
"nginx",
"extraConfig",
"extra",
"Config",
];
ex.sort_unstable();
ex
},
);
assert_eq!(
{
let mut q = AttributeQuery::new("python37Packages.test1_name-test2")
.query()
.to_owned();
q.sort();
q
},
{
let mut ex = [
"python37Packages.test1_name-test2",
"python37Packages.test1_name-test",
"python37Packages.test1_name",
"python37Packages.test1",
"python37Packages.test",
"python37Packages",
"python37",
"python",
"37Packages.test1_name-test2",
"37Packages.test1_name-test",
"37Packages.test1_name",
"37Packages.test1",
"37Packages.test",
"37Packages",
"37",
"Packages.test1_name-test2",
"Packages.test1_name-test",
"Packages.test1_name",
"Packages.test1",
"Packages.test",
"Packages",
"test1_name-test2",
"test1_name-test",
"test1_name",
"test1",
"test",
"1_name-test2",
"1_name-test",
"1_name",
"1",
"name-test2",
"name-test",
"name",
"test2",
"test",
"2",
];
ex.sort_unstable();
ex
}
);
}
}