From 57164bcfd3170f523eeec0a1aef11c99dd0cc0f1 Mon Sep 17 00:00:00 2001 From: Guillaume Bouvignies Date: Thu, 4 Jul 2024 11:22:15 +0100 Subject: [PATCH] Add `buffrs new` command to init a package in a non-existing folder --- src/command.rs | 46 ++++++++++++++++++++++++++++++++++++++++++---- src/main.rs | 40 +++++++++++++++++++++++++++++++++------- src/manifest.rs | 10 ++++++---- 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/command.rs b/src/command.rs index 3c6398eb..888331d6 100644 --- a/src/command.rs +++ b/src/command.rs @@ -25,7 +25,11 @@ use crate::{ use async_recursion::async_recursion; use miette::{bail, ensure, miette, Context as _, IntoDiagnostic}; use semver::{Version, VersionReq}; -use std::{env, path::Path, str::FromStr}; +use std::{ + env, + path::{Path, PathBuf}, + str::FromStr, +}; use tokio::{ fs, io::{self, AsyncBufReadExt, BufReader}, @@ -68,7 +72,7 @@ pub async fn init(kind: Option, name: Option) -> miett let manifest = Manifest::new(package, vec![]); - manifest.write().await?; + manifest.write(None).await?; PackageStore::open(std::env::current_dir().unwrap_or_else(|_| ".".into())) .await @@ -77,6 +81,40 @@ pub async fn init(kind: Option, name: Option) -> miett Ok(()) } +/// Initializes a project with the given name in the current directory +pub async fn new(kind: Option, name: PackageName) -> miette::Result<()> { + let mut package_dir: PathBuf = ".".into(); + package_dir.push::(name.clone().into()); + // create_dir fails if the folder already exists + fs::create_dir(&package_dir) + .await + .into_diagnostic() + .wrap_err(miette!( + "failed to create {} directory", + package_dir.display() + ))?; + + let package = kind + .map(|kind| -> miette::Result { + Ok(PackageManifest { + kind, + name, + version: INITIAL_VERSION, + description: None, + }) + }) + .transpose()?; + + let manifest = Manifest::new(package, vec![]); + manifest.write(Some(package_dir.clone())).await?; + + PackageStore::open(&package_dir) + .await + .wrap_err(miette!("failed to create buffrs `proto` directories"))?; + + Ok(()) +} + struct DependencyLocator { repository: String, package: PackageName, @@ -139,7 +177,7 @@ pub async fn add(registry: RegistryUri, dependency: &str) -> miette::Result<()> .push(Dependency::new(registry, repository, package, version)); manifest - .write() + .write(None) .await .wrap_err(miette!("failed to write `{MANIFEST_FILE}`")) } @@ -159,7 +197,7 @@ pub async fn remove(package: PackageName) -> miette::Result<()> { store.uninstall(&dependency.package).await.ok(); - manifest.write().await + manifest.write(None).await } /// Packages the api and writes it to the filesystem diff --git a/src/main.rs b/src/main.rs index 2c792d5f..53644651 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,21 @@ enum Command { package: Option, }, + /// Creates a new buffrs package in the current directory + New { + /// Sets up the package as lib + #[clap(long, conflicts_with = "api")] + #[arg(group = "pkg")] + lib: bool, + /// Sets up the package as api + #[clap(long, conflicts_with = "lib")] + #[arg(group = "pkg")] + api: bool, + /// The package name + #[clap(requires = "pkg")] + package: PackageName, + }, + /// Check rule violations for this package. Lint, @@ -177,13 +192,7 @@ async fn main() -> miette::Result<()> { match cli.command { Command::Init { lib, api, package } => { - let kind = if lib { - Some(PackageType::Lib) - } else if api { - Some(PackageType::Api) - } else { - None - }; + let kind = infer_package_type(lib, api); command::init(kind, package.to_owned()) .await @@ -192,6 +201,13 @@ async fn main() -> miette::Result<()> { package.map(|p| format!("`{p}`")).unwrap_or_default() )) } + Command::New { lib, api, package } => { + let kind = infer_package_type(lib, api); + + command::new(kind, package.to_owned()) + .await + .wrap_err(miette!("failed to initialize {}", format!("`{package}`"))) + } Command::Login { registry } => command::login(registry.to_owned()) .await .wrap_err(miette!("failed to login to `{registry}`")), @@ -255,3 +271,13 @@ async fn main() -> miette::Result<()> { }, } } + +fn infer_package_type(lib: bool, api: bool) -> Option { + if lib { + Some(PackageType::Lib) + } else if api { + Some(PackageType::Api) + } else { + None + } +} diff --git a/src/manifest.rs b/src/manifest.rs index 49128dda..95bf7228 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, fmt::{self, Display}, - path::Path, + path::{Path, PathBuf}, str::FromStr, }; use tokio::fs; @@ -313,16 +313,18 @@ impl Manifest { Ok(Some(raw.into())) } - /// Persists the manifest into the current directory - pub async fn write(&self) -> miette::Result<()> { + /// Persists the manifest into the provided directory, which must exist (or current directory if None) + pub async fn write(&self, dir_path: Option) -> miette::Result<()> { // hint: create a canary manifest from the current one let raw = RawManifest::from(Manifest::new( self.package.clone(), self.dependencies.clone(), )); + let mut manifest_file_path = dir_path.unwrap_or_else(|| ".".into()); + manifest_file_path.push(MANIFEST_FILE); fs::write( - MANIFEST_FILE, + manifest_file_path, toml::to_string(&raw) .into_diagnostic() .wrap_err(SerializationError(ManagedFile::Manifest))?