Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(lib/rockspec): use Deserialize instances for parsing #6

Merged
merged 1 commit into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions rocks-lib/src/rocks/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::str::FromStr;
use eyre::{eyre, Result};
use html_escape::decode_html_entities;
use semver::{Version, VersionReq};
use serde::Deserialize;
use serde::{de, Deserialize, Deserializer};

#[derive(Debug)]
pub struct LuaDependency {
Expand Down Expand Up @@ -48,6 +48,16 @@ impl LuaDependency {
}
}

impl<'de> Deserialize<'de> for LuaDependency {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(de::Error::custom)
}
}

/// Can be defined in a [platform-agnostic](https://github.com/luarocks/luarocks/wiki/platform-agnostic-external-dependencies) manner
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
Expand All @@ -73,16 +83,6 @@ impl LuaRock {
}
}

pub fn parse_lua_dependencies_from_vec_str(
dependencies: &Vec<String>,
) -> Result<Vec<LuaDependency>> {
let mut lua_dependencies: Vec<LuaDependency> = vec![];
for dep in dependencies {
lua_dependencies.push(LuaDependency::parse(&dep)?);
}
Ok(lua_dependencies)
}

/// Transform LuaRocks constraints into constraints that can be parsed by the semver crate.
fn parse_version_req(version_constraints: &str) -> Result<VersionReq> {
let unescaped = decode_html_entities(version_constraints)
Expand Down
30 changes: 20 additions & 10 deletions rocks-lib/src/rocks/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{collections::HashMap, str::FromStr};
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString};

use serde::{Deserialize, Serialize};
use serde::{de, Deserialize, Deserializer, Serialize};

#[derive(
Serialize, Deserialize, PartialEq, Eq, Hash, Debug, Clone, Display, EnumString, EnumIter,
Expand Down Expand Up @@ -101,8 +101,18 @@ impl Default for PlatformSupport {
}
}

impl<'de> Deserialize<'de> for PlatformSupport {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let platforms: Vec<String> = Vec::deserialize(deserializer)?;
Self::parse(&platforms).map_err(de::Error::custom)
}
}

impl PlatformSupport {
pub fn new(platforms: &Vec<String>) -> Result<Self> {
fn parse(platforms: &Vec<String>) -> Result<Self> {
let mut platform_map = HashMap::default();
if platforms.is_empty() {
return Ok(Self::default());
Expand Down Expand Up @@ -202,7 +212,7 @@ mod tests {
fn supported_platforms(identifier in platform_identifier_strategy()) {
let identifier_str = identifier.to_string();
let platforms = vec![identifier_str];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
prop_assert!(platform_support.is_supported(&identifier))
}

Expand All @@ -214,7 +224,7 @@ mod tests {
}
let identifier_str = format!("!{}", unsupported);
let platforms = vec![identifier_str];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
prop_assert!(!platform_support.is_supported(&unsupported));
prop_assert!(platform_support.is_supported(&supported))
}
Expand All @@ -228,15 +238,15 @@ mod tests {
let supported_str = unspecified.to_string();
let unsupported_str = format!("!{}", unsupported);
let platforms = vec![supported_str, unsupported_str];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
prop_assert!(platform_support.is_supported(&unspecified));
prop_assert!(!platform_support.is_supported(&unsupported));
}

#[test]
fn all_platforms_supported_if_none_are_specified(identifier in platform_identifier_strategy()) {
let platforms = vec![];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
prop_assert!(platform_support.is_supported(&identifier))
}

Expand All @@ -245,14 +255,14 @@ mod tests {
let identifier_str = identifier.to_string();
let identifier_str_negated = format!("!{}", identifier);
let platforms = vec![identifier_str, identifier_str_negated];
let _ = PlatformSupport::new(&platforms).unwrap_err();
let _ = PlatformSupport::parse(&platforms).unwrap_err();
}

#[test]
fn extended_platforms_supported_if_supported(identifier in platform_identifier_strategy()) {
let identifier_str = identifier.to_string();
let platforms = vec![identifier_str];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
for identifier in identifier.get_extended_platforms() {
prop_assert!(platform_support.is_supported(&identifier))
}
Expand All @@ -262,7 +272,7 @@ mod tests {
fn sub_platforms_unsupported_if_unsupported(identifier in platform_identifier_strategy()) {
let identifier_str = format!("!{}", identifier);
let platforms = vec![identifier_str];
let platform_support = PlatformSupport::new(&platforms).unwrap();
let platform_support = PlatformSupport::parse(&platforms).unwrap();
for identifier in identifier.get_subsets() {
prop_assert!(!platform_support.is_supported(&identifier))
}
Expand All @@ -277,7 +287,7 @@ mod tests {
let supported_str = identifier.to_string();
let mut platforms: Vec<String> = extended_platforms.into_iter().map(|ident| format!("!{}", ident)).collect();
platforms.push(supported_str);
let _ = PlatformSupport::new(&platforms).unwrap_err();
let _ = PlatformSupport::parse(&platforms).unwrap_err();
}
}
}
71 changes: 11 additions & 60 deletions rocks-lib/src/rocks/rockspec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;

use eyre::{eyre, Result};
use mlua::{Lua, LuaSerdeExt, Value};
use serde::Deserialize;
use serde::{de::DeserializeOwned, Deserialize};

use crate::rocks::PlatformSupport;

Expand All @@ -29,28 +29,19 @@ impl Rockspec {
pub fn new(rockspec_content: &String) -> Result<Self> {
let lua = Lua::new();
lua.load(rockspec_content).exec()?;

let rockspec = Rockspec {
rockspec_format: lua.from_value(lua.globals().get("rockspec_format")?)?,
package: lua.from_value(lua.globals().get("package")?)?,
version: lua.from_value(lua.globals().get("version")?)?,
description: RockDescription::from_lua(&lua)?,
supported_platforms: match lua.globals().get("supported_platforms")? {
Value::Nil => PlatformSupport::default(),
value @ Value::Table(_) => PlatformSupport::new(&lua.from_value(value)?)?,
value => Err(eyre!(format!(
"Could not parse supported_platforms. Expected list, but got {}",
value.type_name()
)))?,
},
description: parse_lua_tbl_or_default(&lua, "description")?,
supported_platforms: parse_lua_tbl_or_default(&lua, "supported_platforms")?,
// TODO(mrcjkb): support per-platform overrides: https://github.com/luarocks/luarocks/wiki/platform-overrides
dependencies: parse_lua_dependencies(&lua, "dependencies")?,
build_dependencies: parse_lua_dependencies(&lua, "build_dependencies")?,
test_dependencies: parse_lua_dependencies(&lua, "test_dependencies")?,
external_dependencies: parse_external_dependencies(&lua)?,
dependencies: parse_lua_tbl_or_default(&lua, "dependencies")?,
build_dependencies: parse_lua_tbl_or_default(&lua, "build_dependencies")?,
test_dependencies: parse_lua_tbl_or_default(&lua, "test_dependencies")?,
external_dependencies: parse_lua_tbl_or_default(&lua, "external_dependencies")?,
source: lua.from_value(lua.globals().get("source")?)?,
};

Ok(rockspec)
}
}
Expand All @@ -70,58 +61,18 @@ pub struct RockDescription {
/// Contact information for the rockspec maintainer.
pub maintainer: Option<String>,
/// A list of short strings that specify labels for categorization of this rock.
#[serde(default)]
pub labels: Vec<String>,
}

impl RockDescription {
fn from_lua(lua: &Lua) -> Result<RockDescription> {
match lua.globals().get("description")? {
Value::Nil => Ok(RockDescription::default()),
Value::Table(tbl) => {
let labels = if tbl.contains_key("labels")? {
lua.from_value(tbl.get("labels")?)?
} else {
Vec::new()
};
Ok(RockDescription {
summary: lua.from_value(tbl.get("summary")?)?,
detailed: lua.from_value(tbl.get("detailed")?)?,
license: lua.from_value(tbl.get("license")?)?,
homepage: lua.from_value(tbl.get("homepage")?)?,
issues_url: lua.from_value(tbl.get("issues_url")?)?,
maintainer: lua.from_value(tbl.get("maintainer")?)?,
labels,
})
}
value => Err(eyre!(format!(
"Could not parse rockspec description. Expected table, but got {}",
value.type_name()
))),
}
}
}

fn parse_lua_dependencies(lua: &Lua, lua_var_name: &str) -> Result<Vec<LuaDependency>> {
parse_lua_tbl(&lua, lua_var_name, |value| {
parse_lua_dependencies_from_vec_str(&lua.from_value(value)?)
})
}

fn parse_external_dependencies(lua: &Lua) -> Result<HashMap<String, ExternalDependency>> {
parse_lua_tbl(&lua, "external_dependencies", |value| {
let ret = lua.from_value(value)?;
Ok(ret)
})
}

fn parse_lua_tbl<T, Parser>(lua: &Lua, lua_var_name: &str, parser: Parser) -> Result<T>
fn parse_lua_tbl_or_default<T>(lua: &Lua, lua_var_name: &str) -> Result<T>
where
T: Default,
Parser: Fn(Value) -> Result<T>,
T: DeserializeOwned,
{
let ret = match lua.globals().get(lua_var_name)? {
Value::Nil => T::default(),
value @ Value::Table(_) => parser(value)?,
value @ Value::Table(_) => lua.from_value(value)?,
value => Err(eyre!(format!(
"Could not parse {}. Expected list, but got {}",
lua_var_name,
Expand Down