-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
commit-id:a905a9c2
- Loading branch information
Showing
6 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
#![allow(dead_code)] | ||
|
||
use anyhow::{anyhow, Context, Result}; | ||
use camino::Utf8Path; | ||
use semver::Version; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_repr::{Deserialize_repr, Serialize_repr}; | ||
use std::collections::BTreeSet; | ||
use toml_edit::Document; | ||
|
||
use crate::core::{PackageId, PackageName, Resolve, SourceId}; | ||
use crate::internal::fsx; | ||
|
||
const HEADER: &str = "# Code generated by scarb DO NOT EDIT."; | ||
|
||
#[derive( | ||
Default, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord, Serialize_repr, Deserialize_repr, | ||
)] | ||
#[repr(u8)] | ||
pub enum LockVersion { | ||
#[default] | ||
V1 = 1, | ||
} | ||
|
||
#[derive(Debug, Eq, PartialEq, Default, Serialize, Deserialize)] | ||
#[serde(rename_all = "kebab-case")] | ||
pub struct Lockfile { | ||
pub version: LockVersion, | ||
#[serde(rename = "package")] | ||
#[serde(default = "BTreeSet::new")] | ||
#[serde(skip_serializing_if = "BTreeSet::is_empty")] | ||
pub packages: BTreeSet<PackageLock>, | ||
} | ||
|
||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] | ||
#[serde(rename_all = "kebab-case")] | ||
pub struct PackageLock { | ||
pub name: PackageName, | ||
pub version: Version, | ||
#[serde(skip_serializing_if = "skip_path_source_id")] | ||
pub source: Option<SourceId>, | ||
#[serde(default = "BTreeSet::new")] | ||
#[serde(skip_serializing_if = "BTreeSet::is_empty")] | ||
pub dependencies: BTreeSet<PackageName>, | ||
} | ||
|
||
fn skip_path_source_id(sid: &Option<SourceId>) -> bool { | ||
sid.map(|sid| sid.is_path()).unwrap_or(true) | ||
} | ||
|
||
impl Lockfile { | ||
pub fn new(packages: impl IntoIterator<Item = PackageLock>) -> Self { | ||
Self { | ||
version: Default::default(), | ||
packages: packages.into_iter().collect(), | ||
} | ||
} | ||
|
||
pub fn from_resolve(resolve: &Resolve) -> Self { | ||
let packages = resolve.package_ids().map(|package| { | ||
let deps = resolve | ||
.package_dependencies(package) | ||
.map(|dep| dep.name.clone()); | ||
PackageLock::new(&package, deps) | ||
}); | ||
Self::new(packages) | ||
} | ||
|
||
pub fn from_path(path: impl AsRef<Utf8Path>) -> Result<Self> { | ||
if path.as_ref().is_file() { | ||
let content = fsx::read_to_string(path.as_ref()) | ||
.with_context(|| format!("Failed to read lockfile at {}", path.as_ref()))?; | ||
if content.is_empty() { | ||
Ok(Self::default()) | ||
} else { | ||
content | ||
.try_into() | ||
.with_context(|| format!("Failed to parse lockfile at {}", path.as_ref())) | ||
} | ||
} else { | ||
Ok(Self::default()) | ||
} | ||
} | ||
|
||
fn body(&self) -> Result<Document> { | ||
let doc = toml_edit::ser::to_string_pretty(self)?; | ||
let mut doc = doc.parse::<Document>()?; | ||
|
||
for packages in doc["package"].as_array_of_tables_mut().iter_mut() { | ||
for pkg in packages.iter_mut() { | ||
if let Some(deps) = pkg.get_mut("dependencies") { | ||
if let Some(deps) = deps.as_array_mut() { | ||
deps.iter_mut().for_each(|dep| { | ||
dep.decor_mut().set_prefix("\n "); | ||
}); | ||
if deps.len() > 1 { | ||
deps.set_trailing("\n"); | ||
} else { | ||
deps.set_trailing(",\n"); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(doc) | ||
} | ||
|
||
pub fn render(&self) -> Result<String> { | ||
Ok(format!("{HEADER}\n{}", self.body()?)) | ||
} | ||
} | ||
|
||
impl TryFrom<String> for Lockfile { | ||
type Error = anyhow::Error; | ||
|
||
fn try_from(value: String) -> Result<Self> { | ||
Ok(toml::from_str(&value)?) | ||
} | ||
} | ||
|
||
impl PackageLock { | ||
pub fn new(package: &PackageId, dependencies: impl Iterator<Item = PackageName>) -> Self { | ||
Self { | ||
name: package.name.clone(), | ||
version: package.version.clone(), | ||
source: Some(package.source_id), | ||
dependencies: dependencies.collect(), | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<PackageLock> for PackageId { | ||
type Error = anyhow::Error; | ||
|
||
fn try_from(value: PackageLock) -> Result<Self> { | ||
let source_id = value.source.ok_or_else(|| { | ||
anyhow!( | ||
"missing source id in package lock for package {}", | ||
value.name | ||
) | ||
})?; | ||
Ok(Self::new(value.name, value.version, source_id)) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::core::lockfile::{Lockfile, PackageLock}; | ||
use crate::core::{PackageId, PackageName, SourceId}; | ||
|
||
use core::default::Default; | ||
use indoc::indoc; | ||
use semver::Version; | ||
use snapbox::assert_eq; | ||
|
||
#[test] | ||
fn simple() { | ||
let pkg1 = PackageLock::new( | ||
&PackageId::new( | ||
PackageName::CORE, | ||
Version::parse("1.0.0").unwrap(), | ||
Default::default(), | ||
), | ||
vec![PackageName::STARKNET, PackageName::new("locker")].into_iter(), | ||
); | ||
|
||
let pkg2 = PackageLock { | ||
name: PackageName::STARKNET, | ||
version: Version::parse("1.0.0").unwrap(), | ||
source: None, | ||
dependencies: vec![PackageName::CORE].into_iter().collect(), | ||
}; | ||
|
||
let pkg3 = PackageLock::new( | ||
&PackageId::new( | ||
PackageName::new("third"), | ||
Version::parse("2.1.0").unwrap(), | ||
SourceId::mock_git(), | ||
), | ||
vec![].into_iter(), | ||
); | ||
|
||
let pkg4 = PackageLock::new( | ||
&PackageId::new( | ||
PackageName::new("fourth"), | ||
Version::parse("80.0.85").unwrap(), | ||
SourceId::for_std(), | ||
), | ||
vec![].into_iter(), | ||
); | ||
|
||
let lock = Lockfile::new(vec![pkg1, pkg2, pkg3, pkg4]); | ||
|
||
let serialized = indoc! {r#" | ||
# Code generated by scarb DO NOT EDIT. | ||
version = 1 | ||
[[package]] | ||
name = "core" | ||
version = "1.0.0" | ||
source = "registry+https://there-is-no-default-registry-yet.com/" | ||
dependencies = [ | ||
"locker", | ||
"starknet", | ||
] | ||
[[package]] | ||
name = "fourth" | ||
version = "80.0.85" | ||
source = "std" | ||
[[package]] | ||
name = "starknet" | ||
version = "1.0.0" | ||
dependencies = [ | ||
"core", | ||
] | ||
[[package]] | ||
name = "third" | ||
version = "2.1.0" | ||
source = "git+https://github.com/starkware-libs/cairo.git?tag=test" | ||
"#}; | ||
|
||
assert_eq(serialized, lock.render().unwrap()); | ||
let deserialized: Lockfile = serialized.to_string().try_into().unwrap(); | ||
assert_eq!(lock, deserialized); | ||
} | ||
|
||
#[test] | ||
fn empty() { | ||
let lock = Lockfile { | ||
version: Default::default(), | ||
packages: Default::default(), | ||
}; | ||
|
||
let serialized = "# Code generated by scarb DO NOT EDIT.\nversion = 1\n"; | ||
assert_eq!(serialized, lock.render().unwrap()); | ||
|
||
let deserialized: Lockfile = serialized.to_string().try_into().unwrap(); | ||
assert_eq!(lock, deserialized); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters