Skip to content

Commit

Permalink
refactor(lib/rockspec): use Deserialize instances for parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Jan 29, 2024
1 parent 0c87e53 commit fdf21ed
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 81 deletions.
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

0 comments on commit fdf21ed

Please sign in to comment.