Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

feat: add dist-workspace.toml and factor some code #136

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
134 changes: 102 additions & 32 deletions src/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,26 @@ use serde::Deserialize;

use crate::{PackageInfo, Result, Version, WorkspaceInfo, WorkspaceSearch};

#[derive(Deserialize)]
struct Manifest {
const DIST_PACKAGE_TOML: &str = "dist.toml";
const DIST_WORKSPACE_TOML: &str = "dist-workspace.toml";
const DIST_TARGET_DIR: &str = "target";

#[derive(Deserialize, Debug)]
struct WorkspaceManifest {
workspace: Workspace,
}

#[derive(Deserialize, Debug)]
struct Workspace {
members: Vec<Utf8PathBuf>,
}

#[derive(Deserialize, Debug)]
struct PackageManifest {
package: Package,
}

#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
#[serde(rename_all = "kebab-case")]
struct Package {
name: String,
Expand Down Expand Up @@ -39,33 +53,91 @@ struct Package {
///
/// See [`crate::get_workspaces`][] for the semantics.
pub fn get_workspace(start_dir: &Utf8Path, clamp_to_dir: Option<&Utf8Path>) -> WorkspaceSearch {
let manifest_path = match crate::find_file("dist.toml", start_dir, clamp_to_dir) {
Ok(path) => path,
Err(e) => return WorkspaceSearch::Missing(e),
};

match workspace_from(&manifest_path) {
Ok(info) => WorkspaceSearch::Found(info),
Err(e) => WorkspaceSearch::Broken {
manifest_path,
cause: e,
// First search for a workspace file
match crate::find_file(DIST_WORKSPACE_TOML, start_dir, clamp_to_dir) {
// Found a workspace file, read it
Ok(manifest_path) => match workspace_from(&manifest_path) {
Ok(info) => WorkspaceSearch::Found(info),
Err(e) => WorkspaceSearch::Broken {
manifest_path,
cause: e,
},
},
// No workspace file, maybe there's just a package?
Err(_) => match crate::find_file(DIST_PACKAGE_TOML, start_dir, clamp_to_dir) {
// Ok, make a faux-workspace from that
Ok(manifest_path) => match single_package_workspace_from(&manifest_path) {
Ok(info) => WorkspaceSearch::Found(info),
Err(e) => WorkspaceSearch::Broken {
manifest_path,
cause: e,
},
},
Err(e) => WorkspaceSearch::Missing(e),
},
}
}

// Load and process a dist-workspace.toml, and its child packages
fn workspace_from(manifest_path: &Utf8Path) -> Result<WorkspaceInfo> {
let manifest = load_workspace_dist_toml(manifest_path)?;
let workspace_dir = manifest_path.parent().unwrap().to_path_buf();
let root_auto_includes = crate::find_auto_includes(&workspace_dir)?;

let manifest: Manifest = load_root_dist_toml(manifest_path)?;
let mut package_info = vec![];
for member_reldir in &manifest.workspace.members {
let member_dir = workspace_dir.join(member_reldir);
let member_manifest_path = member_dir.join(DIST_PACKAGE_TOML);
let mut package = package_from(&member_manifest_path)?;
crate::merge_auto_includes(&mut package, &root_auto_includes);
package_info.push(package);
}

Ok(WorkspaceInfo {
kind: crate::WorkspaceKind::Generic,
target_dir: workspace_dir.join(DIST_TARGET_DIR),
workspace_dir,
package_info,
manifest_path: manifest_path.to_owned(),
root_auto_includes,
warnings: vec![],
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_profiles: crate::rust::CargoProfiles::new(),
})
}

// Load and process a dist.toml, and treat it as an entire workspace
fn single_package_workspace_from(manifest_path: &Utf8Path) -> Result<WorkspaceInfo> {
let package = package_from(manifest_path)?;
let root_auto_includes = crate::find_auto_includes(&package.package_root)?;
Ok(WorkspaceInfo {
kind: crate::WorkspaceKind::Generic,
target_dir: package.package_root.join(DIST_TARGET_DIR),
workspace_dir: package.package_root.clone(),
manifest_path: package.manifest_path.clone(),
root_auto_includes,
package_info: vec![package],
warnings: vec![],
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_profiles: Default::default(),
})
}

// Load and process a dist.toml
fn package_from(manifest_path: &Utf8Path) -> Result<PackageInfo> {
let manifest = load_package_dist_toml(manifest_path)?;
let package = manifest.package;
let version = package.version.map(Version::Generic);

let manifest_path = manifest_path.to_path_buf();

let package_info = PackageInfo {
let mut info = PackageInfo {
manifest_path: manifest_path.clone(),
package_root: manifest_path.clone(),
package_root: manifest_path.parent().unwrap().to_owned(),
name: package.name,
version,
description: package.description,
Expand All @@ -82,31 +154,29 @@ fn workspace_from(manifest_path: &Utf8Path) -> Result<WorkspaceInfo> {
binaries: package.binaries,
cstaticlibs: package.cstaticlibs,
cdylibs: package.cdylibs,
build_command: Some(package.build_command),
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_package_id: None,
};

Ok(WorkspaceInfo {
kind: crate::WorkspaceKind::Generic,
target_dir: workspace_dir.join("target"),
workspace_dir,
package_info: vec![package_info],
manifest_path,
repository_url: package.repository,
root_auto_includes,
warnings: vec![],
build_command: Some(package.build_command),
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_profiles: crate::rust::CargoProfiles::new(),
})
// Load and apply auto-includes
let auto_includes = crate::find_auto_includes(&info.package_root)?;
crate::merge_auto_includes(&mut info, &auto_includes);

Ok(info)
}

/// Load the root workspace toml
fn load_root_dist_toml(manifest_path: &Utf8Path) -> Result<Manifest> {
fn load_workspace_dist_toml(manifest_path: &Utf8Path) -> Result<WorkspaceManifest> {
let manifest_src = SourceFile::load_local(manifest_path)?;
let manifest = manifest_src.deserialize_toml()?;
Ok(manifest)
}

/// Load the a package toml
fn load_package_dist_toml(manifest_path: &Utf8Path) -> Result<PackageManifest> {
let manifest_src = SourceFile::load_local(manifest_path)?;
let manifest = manifest_src.deserialize_toml()?;
Ok(manifest)
Expand Down
4 changes: 2 additions & 2 deletions src/javascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ fn read_workspace(manifest_path: &Utf8Path) -> Result<WorkspaceInfo> {
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
cargo_package_id: None,
#[cfg(feature = "generic-projects")]
build_command: None,
};
crate::merge_auto_includes(&mut info, &root_auto_includes);

Expand All @@ -145,10 +147,8 @@ fn read_workspace(manifest_path: &Utf8Path) -> Result<WorkspaceInfo> {
workspace_dir: root,
package_info,
manifest_path: manifest_path.to_owned(),
repository_url,
root_auto_includes,
warnings: vec![],
build_command: None,
#[cfg(feature = "cargo-projects")]
cargo_metadata_table: None,
#[cfg(feature = "cargo-projects")]
Expand Down
58 changes: 47 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,12 @@ pub struct WorkspaceInfo {
/// may or may not represent a "real" package. Both systems have some notion of
/// "virtual" manifest which exists only to list the actual packages in the workspace.
pub manifest_path: Utf8PathBuf,
/// A consensus URL for the repo according to the packages in the workspace
///
/// If there are multiple packages in the workspace that specify a repository
/// but they disagree, this will be None.
pub repository_url: Option<String>,
/// If the workspace root has some auto-includeable files, here they are!
///
/// This is currently what is use for top-level Announcement contents.
pub root_auto_includes: AutoIncludes,
/// Non-fatal issues that were encountered and should probably be reported
pub warnings: Vec<AxoprojectError>,
/// Build command to run for this workspace; not required for cargo
pub build_command: Option<Vec<String>>,
/// Raw cargo `[workspace.metadata]` table
#[cfg(feature = "cargo-projects")]
pub cargo_metadata_table: Option<serde_json::Value>,
Expand All @@ -176,17 +169,57 @@ impl WorkspaceInfo {
}

/// Returns a struct which contains the repository's owner and name.
pub fn github_repo(&self) -> Result<Option<GithubRepo>> {
match self.repository_url.clone() {
pub fn github_repo(&self, packages: Option<&[PackageIdx]>) -> Result<Option<GithubRepo>> {
match self.repository_url(packages)? {
None => Ok(None),
Some(url) => Ok(Some(GithubRepoInput::new(url)?.parse()?)),
}
}

/// Returns a web version of the repository URL,
/// converted from SSH if necessary, with .git suffix trimmed.
pub fn web_url(&self) -> Result<Option<String>> {
Ok(self.github_repo()?.map(|repo| repo.web_url()))
pub fn web_url(&self, packages: Option<&[PackageIdx]>) -> Result<Option<String>> {
Ok(self.github_repo(packages)?.map(|repo| repo.web_url()))
}

/// Returns a consensus package URL for the given packages, if any exists
pub fn repository_url(&self, packages: Option<&[PackageIdx]>) -> Result<Option<String>> {
let mut repo_url = None::<String>;
let mut repo_url_origin = None::<Utf8PathBuf>;

let package_list = if let Some(packages) = packages {
packages
.iter()
.map(|idx| self.package(*idx))
.collect::<Vec<_>>()
} else {
self.package_info.iter().collect::<Vec<_>>()
};
for info in package_list {
if let Some(new_url) = &info.repository_url {
// Normalize away trailing `/` stuff before comparing
let mut normalized_new_url = new_url.clone();
if normalized_new_url.ends_with('/') {
normalized_new_url.pop();
}
if let Some(cur_url) = &repo_url {
if &normalized_new_url == cur_url {
// great! consensus!
} else {
return Err(AxoprojectError::InconsistentRepositoryKey {
file1: repo_url_origin.as_ref().unwrap().to_owned(),
url1: cur_url.clone(),
file2: info.manifest_path.clone(),
url2: normalized_new_url,
});
}
} else {
repo_url = Some(normalized_new_url);
repo_url_origin = Some(info.manifest_path.clone());
}
}
}
Ok(repo_url)
}
}

Expand Down Expand Up @@ -300,6 +333,9 @@ pub struct PackageInfo {
/// A unique id used by Cargo to refer to the package
#[cfg(feature = "cargo-projects")]
pub cargo_package_id: Option<PackageId>,
/// Command to run to build this package
#[cfg(feature = "generic-projects")]
pub build_command: Option<Vec<String>>,
}

impl PackageInfo {
Expand Down
47 changes: 4 additions & 43 deletions src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

use std::collections::BTreeMap;

use crate::{
errors::AxoprojectError, PackageInfo, Result, Version, WorkspaceInfo, WorkspaceKind,
WorkspaceSearch,
};
use crate::{PackageInfo, Result, Version, WorkspaceInfo, WorkspaceKind, WorkspaceSearch};
use axoasset::SourceFile;
use camino::{Utf8Path, Utf8PathBuf};
use guppy::{
Expand Down Expand Up @@ -63,7 +60,7 @@ fn package_graph(start_dir: &Utf8Path) -> Result<PackageGraph> {
fn workspace_info(pkg_graph: &PackageGraph) -> Result<WorkspaceInfo> {
let workspace = pkg_graph.workspace();
let members = pkg_graph.resolve_workspace();
let mut warnings = vec![];
let warnings = vec![];

let manifest_path = workspace.root().join("Cargo.toml");
// I originally had this as a proper Error but honestly this would be MADNESS and
Expand All @@ -78,45 +75,10 @@ fn workspace_info(pkg_graph: &PackageGraph) -> Result<WorkspaceInfo> {
let cargo_metadata_table = Some(workspace.metadata_table().clone());
let workspace_root = workspace.root();
let root_auto_includes = crate::find_auto_includes(workspace_root)?;

let mut repo_url_conflicted = false;
let mut repo_url = None::<String>;
let mut repo_url_origin = None::<Utf8PathBuf>;
let mut all_package_info = vec![];
for package in members.packages(DependencyDirection::Forward) {
let mut info = package_info(workspace_root, &package)?;

// Apply root workspace's auto-includes
crate::merge_auto_includes(&mut info, &root_auto_includes);

// Try to find repo URL consensus
if !repo_url_conflicted {
if let Some(new_url) = &info.repository_url {
// Normalize away trailing `/` stuff before comparing
let mut normalized_new_url = new_url.clone();
if normalized_new_url.ends_with('/') {
normalized_new_url.pop();
}
if let Some(cur_url) = &repo_url {
if &normalized_new_url == cur_url {
// great! consensus!
} else {
warnings.push(AxoprojectError::InconsistentRepositoryKey {
file1: repo_url_origin.as_ref().unwrap().to_owned(),
url1: cur_url.clone(),
file2: info.manifest_path.clone(),
url2: normalized_new_url,
});
repo_url_conflicted = true;
repo_url = None;
}
} else {
repo_url = Some(normalized_new_url);
repo_url_origin = Some(info.manifest_path.clone());
}
}
}

all_package_info.push(info);
}

Expand All @@ -129,14 +91,11 @@ fn workspace_info(pkg_graph: &PackageGraph) -> Result<WorkspaceInfo> {
workspace_dir,
package_info: all_package_info,
manifest_path,

repository_url: repo_url,
root_auto_includes,
cargo_metadata_table,
cargo_profiles,

warnings,
build_command: None,
})
}

Expand Down Expand Up @@ -259,6 +218,8 @@ fn package_info(_workspace_root: &Utf8Path, package: &PackageMetadata) -> Result
cstaticlibs,
cargo_metadata_table,
cargo_package_id,
#[cfg(feature = "generic-projects")]
build_command: None,
};

// Find files we might want to auto-include
Expand Down
Loading
Loading