From 3252b8567230bbd75de1d3685568cb165a63ec82 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 13:33:11 +0700 Subject: [PATCH 01/17] :recycle: improve version dependency check --- frb_codegen/src/commands.rs | 63 +++++----- frb_codegen/src/error.rs | 12 +- frb_codegen/src/utils.rs | 234 ++++++++++++++++++++++++++++++------ 3 files changed, 231 insertions(+), 78 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index b3e526d563..691dec34a5 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -2,14 +2,16 @@ use std::fmt::Write; use std::path::Path; use std::process::Command; use std::process::Output; +use std::str::FromStr; use crate::error::{Error, Result}; +use crate::utils::DartRepository; use crate::utils::PackageManager; -use crate::utils::{ensure_dependencies, guess_toolchain, DartToolchain}; +use cargo_metadata::VersionReq; use log::{debug, info, warn}; #[must_use] -fn call_shell(cmd: &str) -> Output { +pub(crate) fn call_shell(cmd: &str) -> Output { #[cfg(windows)] return execute_command("powershell", &["-noprofile", "-c", cmd], None); @@ -18,32 +20,31 @@ fn call_shell(cmd: &str) -> Output { } pub fn ensure_tools_available(dart_root: &str) -> Result { - let toolchain = guess_toolchain(dart_root).unwrap(); - if toolchain == DartToolchain::Dart && !call_shell("dart --version").status.success() { - return Err(Error::MissingExe("dart".to_string())); - } else if toolchain == DartToolchain::Flutter - && !call_shell("flutter --version").status.success() - { - return Err(Error::MissingExe("flutter".to_string())); + let repo = + DartRepository::from_str(dart_root).map_err(|e| Error::StringError(e.to_string()))?; + if !repo.toolchain_available() { + return Err(Error::MissingExe(repo.toolchain.to_string())); } - let ffi = ensure_dependencies(dart_root, "ffi", PackageManager::Dependencies).unwrap(); - if ffi.is_none() { - return Err(Error::MissingDep { - name: "ffi".into(), - context: PackageManager::Dependencies, - version: "^2.0.1".into(), - }); - } + let requirement = VersionReq::parse(">= 2.0.1, < 3.0.0").unwrap(); + if repo + .has_specified("ffi", PackageManager::Dependencies, &requirement) + .is_err() + {} + if repo + .has_installed("ffi", PackageManager::Dependencies, &requirement) + .is_err() + {} - let ffigen = ensure_dependencies(dart_root, "ffigen", PackageManager::DevDependencies).unwrap(); - if ffigen.is_none() { - return Err(Error::MissingDep { - name: "ffigen".into(), - context: PackageManager::DevDependencies, - version: "^6.0.1".into(), - }); - } + let requirement = VersionReq::parse(">= 6.0.1, < 7.0.0").unwrap(); + if repo + .has_specified("ffigen", PackageManager::DevDependencies, &requirement) + .is_err() + {} + if repo + .has_installed("ffigen", PackageManager::DevDependencies, &requirement) + .is_err() + {} Ok(()) } @@ -233,10 +234,8 @@ fn ffigen( std::io::Write::write_all(&mut config_file, config.as_bytes())?; debug!("ffigen config_file: {:?}", config_file); - let cmd = format!( - "{} run", - guess_toolchain(dart_root).unwrap().as_run_command() - ); + let repo = DartRepository::from_str(dart_root).unwrap(); + let cmd = format!("{} run", repo.toolchain.as_run_command()); let res = call_shell(&format!( "cd {}{}{} ffigen --config \"{}\"", dart_root, @@ -288,18 +287,18 @@ pub fn format_dart(path: &str, line_length: i32) -> Result { pub fn build_runner(dart_root: &str) -> Result { info!("Running build_runner at {}", dart_root); - let toolchain = guess_toolchain(dart_root).unwrap(); + let repo = DartRepository::from_str(dart_root).unwrap(); let out = if cfg!(windows) { call_shell(&format!( "cd \"{}\"; {} run build_runner build --delete-conflicting-outputs", dart_root, - toolchain.as_run_command() + repo.toolchain.as_run_command() )) } else { call_shell(&format!( "cd \"{}\" && {} run build_runner build --delete-conflicting-outputs", dart_root, - toolchain.as_run_command() + repo.toolchain.as_run_command() )) }; if !out.status.success() { diff --git a/frb_codegen/src/error.rs b/frb_codegen/src/error.rs index cbda74bce2..fa08efdec1 100644 --- a/frb_codegen/src/error.rs +++ b/frb_codegen/src/error.rs @@ -21,17 +21,17 @@ pub enum Error { MissingExe(String), #[error("{0}")] StringError(String), - #[error("please add {name} to your {context}. (version {version})")] + #[error("please add {name} to your {manager}. (version {requirement})")] MissingDep { name: String, - context: PackageManager, - version: String, + manager: PackageManager, + requirement: String, }, - #[error("please update version of {name} in your {context}. (version {version})")] + #[error("please update version of {name} in your {manager}. (version {requirement})")] InvalidDep { name: String, - context: PackageManager, - version: String, + manager: PackageManager, + requirement: String, }, } diff --git a/frb_codegen/src/utils.rs b/frb_codegen/src/utils.rs index 3002888b83..3d52574320 100644 --- a/frb_codegen/src/utils.rs +++ b/frb_codegen/src/utils.rs @@ -10,6 +10,9 @@ use cargo_metadata::{Version, VersionReq}; use log::debug; use serde::Deserialize; +use crate::commands::call_shell; +use crate::error::Error; + pub fn mod_from_rust_path(code_path: &str, crate_path: &str) -> String { Path::new(code_path) .strip_prefix(Path::new(crate_path).join("src")) @@ -96,6 +99,27 @@ pub(crate) enum DartToolchain { Flutter, } +impl ToString for DartToolchain { + fn to_string(&self) -> String { + match self { + DartToolchain::Dart => "dart", + DartToolchain::Flutter => "flutter", + } + .to_string() + } +} + +impl DartToolchain { + #[inline] + pub fn manifest_filename() -> &'static str { + "pubspec.yaml" + } + #[inline] + pub fn lock_filename() -> &'static str { + "pubspec.lock" + } +} + impl DartToolchain { pub(crate) fn as_run_command(&self) -> &'static str { match self { @@ -103,11 +127,164 @@ impl DartToolchain { DartToolchain::Flutter => "flutter pub", } } + pub(crate) fn available(&self) -> bool { + match self { + DartToolchain::Dart => call_shell("dart --version").status.success(), + DartToolchain::Flutter => call_shell("flutter --version").status.success(), + } + } +} + +#[derive(Debug)] +pub(crate) struct DartRepository { + pub(crate) at: PathBuf, + pub(crate) toolchain: DartToolchain, +} + +impl FromStr for DartRepository { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + debug!("Guessing toolchain the runner is run into"); + let filename = DartToolchain::lock_filename(); + let lock_file = read_file(s, filename)?; + let lock_file: PubspecLock = serde_yaml::from_str(&lock_file) + .map_err(|_| anyhow::Error::msg(format!("unable to parse {} in {}", filename, s)))?; + if lock_file + .packages + .contains_key(&DartToolchain::Flutter.to_string()) + { + return Ok(DartRepository { + at: PathBuf::from(s), + toolchain: DartToolchain::Flutter, + }); + } + Ok(DartRepository { + at: PathBuf::from(s), + toolchain: DartToolchain::Dart, + }) + } +} + +impl DartRepository { + pub(crate) fn toolchain_available(&self) -> bool { + self.toolchain.available() + } + pub(crate) fn has_specified( + &self, + package: &str, + manager: PackageManager, + requirement: &VersionReq, + ) -> anyhow::Result<()> { + let at = self.at.to_str().unwrap(); + debug!("Checking presence of {} in {} at {}", package, manager, at); + let manifest_file = read_file(at, DartToolchain::manifest_filename())?; + let manifest_file: Pubspec = serde_yaml::from_str(&manifest_file).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} in {}", + DartToolchain::manifest_filename(), + at + )) + })?; + let deps = if manager == PackageManager::DevDependencies { + manifest_file.dev_dependencies.unwrap_or_default() + } else { + manifest_file.dependencies.unwrap_or_default() + }; + let version = deps.get(package); + let version = match version { + Some(v) => PackageVersionKind::try_from(v).ok(), + None => None, + }; + match version { + // if user specifies an exact version, let's see if it matches + Some(PackageVersionKind::Exact(ref v)) if requirement.matches(v) => Ok(()), + // if user specifies a range of versions, we cannot check if it matches yet, but let's not fail early + Some(PackageVersionKind::Range(_)) => Ok(()), + None => Err(anyhow::Error::new(Error::MissingDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + Some(PackageVersionKind::Exact(_)) => Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + } + } + pub(crate) fn has_installed( + &self, + package: &str, + manager: PackageManager, + requirement: &VersionReq, + ) -> anyhow::Result<()> { + let at = self.at.to_str().unwrap(); + debug!("Checking presence of {} in {} at {}", package, manager, at); + let lock_file = read_file(at, DartToolchain::lock_filename())?; + let lock_file: PubspecLock = serde_yaml::from_str(&lock_file).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} in {}", + DartToolchain::lock_filename(), + at + )) + })?; + let dependency = lock_file.packages.get(package); + let version = match dependency { + Some(dependency) => { + if (manager == PackageManager::Dependencies + && dependency.dependency != "direct main") + || (manager == PackageManager::DevDependencies + && dependency.dependency != "direct dev") + { + return Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })); + } + PackageVersionKind::try_from(dependency).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} version in {}", + package, + DartToolchain::lock_filename(), + )) + })? + } + None => { + return Err(anyhow::Error::new(Error::MissingDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })) + } + }; + + match version { + PackageVersionKind::Exact(ref v) if requirement.matches(v) => Ok(()), + PackageVersionKind::Range(_) => Err(anyhow::Error::new(Error::StringError(format!( + "unexpected version range for {} in {}", + package, + DartToolchain::lock_filename() + )))), + _ => Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + } + } } #[derive(Debug, Deserialize)] struct PubspecLock { - pub packages: HashMap, + pub packages: HashMap, +} + +#[derive(Debug, Deserialize)] +struct PubspecLockDependency { + pub dependency: String, + pub version: String, } #[derive(Debug, Deserialize)] @@ -142,6 +319,13 @@ impl TryFrom<&PackageVersion> for PackageVersionKind { } } +impl TryFrom<&PubspecLockDependency> for PackageVersionKind { + type Error = anyhow::Error; + fn try_from(dependency: &PubspecLockDependency) -> Result { + Self::from_str(&dependency.version) + } +} + impl FromStr for PackageVersionKind { type Err = anyhow::Error; @@ -201,46 +385,16 @@ fn read_file(at: &str, filename: &str) -> anyhow::Result { Ok(content) } -pub(crate) fn guess_toolchain(dart_root: &str) -> anyhow::Result { - debug!("Guessing toolchain the runner is run into"); - let lock_file = read_file(dart_root, "pubspec.lock")?; - let lock_file: PubspecLock = serde_yaml::from_str(&lock_file).map_err(|_| { - anyhow::Error::msg(format!("unable to parse pubspec.lock in {}", dart_root)) - })?; - if lock_file.packages.contains_key("flutter") { - return Ok(DartToolchain::Flutter); - } - Ok(DartToolchain::Dart) -} - -pub(crate) fn ensure_dependencies( - dart_root: &str, - package: &str, - manager: PackageManager, -) -> anyhow::Result> { - debug!( - "Checking presence of {} in {} at {}", - package, manager, dart_root - ); - let manifest_file = read_file(dart_root, "pubspec.yaml")?; - let manifest_file: Pubspec = serde_yaml::from_str(&manifest_file).map_err(|_| { - anyhow::Error::msg(format!("unable to parse pubspec.yaml in {}", dart_root)) - })?; - let deps = if manager == PackageManager::DevDependencies { - manifest_file.dev_dependencies.unwrap_or_default() - } else { - manifest_file.dependencies.unwrap_or_default() - }; - let version = deps.get(package); - let version = version.map(|v| PackageVersionKind::try_from(v).unwrap()); - Ok(version) -} - #[cfg(test)] mod tests { - use std::path::{Path, PathBuf}; + use std::{ + path::{Path, PathBuf}, + str::FromStr, + }; + + use crate::utils::DartRepository; - use super::{guess_toolchain, DartToolchain}; + use super::DartToolchain; use lazy_static::lazy_static; lazy_static! { @@ -252,11 +406,11 @@ mod tests { } fn guess_toolchain_base(path: &Path, expect_toolchain: DartToolchain) { - let toolchain = guess_toolchain(&path.to_string_lossy()).expect(&format!( + let repo = DartRepository::from_str(&path.to_string_lossy()).expect(&format!( "can get toolchain from {}", path.to_string_lossy() )); - assert_eq!(toolchain, expect_toolchain); + assert_eq!(repo.toolchain, expect_toolchain); } #[test] From 873175cc4e8c0869505ff03a69662696588ade73 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 14:14:56 +0700 Subject: [PATCH 02/17] :truck: move code to mod tools --- frb_codegen/src/commands.rs | 4 +- frb_codegen/src/error.rs | 2 +- frb_codegen/src/lib.rs | 1 + frb_codegen/src/tools.rs | 350 ++++++++++++++++++++++++++++++++++++ frb_codegen/src/utils.rs | 344 ----------------------------------- 5 files changed, 354 insertions(+), 347 deletions(-) create mode 100644 frb_codegen/src/tools.rs diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index 691dec34a5..226a299512 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -5,8 +5,8 @@ use std::process::Output; use std::str::FromStr; use crate::error::{Error, Result}; -use crate::utils::DartRepository; -use crate::utils::PackageManager; +use crate::tools::DartRepository; +use crate::tools::PackageManager; use cargo_metadata::VersionReq; use log::{debug, info, warn}; diff --git a/frb_codegen/src/error.rs b/frb_codegen/src/error.rs index fa08efdec1..e6928b27d3 100644 --- a/frb_codegen/src/error.rs +++ b/frb_codegen/src/error.rs @@ -1,6 +1,6 @@ use thiserror::Error; -use crate::utils::PackageManager; +use crate::tools::PackageManager; pub type Result = std::result::Result<(), Error>; diff --git a/frb_codegen/src/lib.rs b/frb_codegen/src/lib.rs index c15f5bd19c..adc376539c 100644 --- a/frb_codegen/src/lib.rs +++ b/frb_codegen/src/lib.rs @@ -11,6 +11,7 @@ use crate::others::*; use crate::utils::*; mod config; +mod tools; pub use crate::commands::ensure_tools_available; pub use crate::config::parse as config_parse; diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs new file mode 100644 index 0000000000..1262dba864 --- /dev/null +++ b/frb_codegen/src/tools.rs @@ -0,0 +1,350 @@ +use std::{collections::HashMap, convert::TryFrom, path::PathBuf, str::FromStr}; + +use cargo_metadata::{Version, VersionReq}; +use log::debug; +use serde::Deserialize; + +use crate::{commands::call_shell, error::Error}; + +#[derive(Debug, PartialEq)] +pub(crate) enum DartToolchain { + Dart, + Flutter, +} + +impl ToString for DartToolchain { + fn to_string(&self) -> String { + match self { + DartToolchain::Dart => "dart", + DartToolchain::Flutter => "flutter", + } + .to_string() + } +} + +impl DartToolchain { + #[inline] + pub fn manifest_filename() -> &'static str { + "pubspec.yaml" + } + #[inline] + pub fn lock_filename() -> &'static str { + "pubspec.lock" + } +} + +impl DartToolchain { + pub(crate) fn as_run_command(&self) -> &'static str { + match self { + DartToolchain::Dart => "dart", + DartToolchain::Flutter => "flutter pub", + } + } + pub(crate) fn available(&self) -> bool { + match self { + DartToolchain::Dart => call_shell("dart --version").status.success(), + DartToolchain::Flutter => call_shell("flutter --version").status.success(), + } + } +} + +#[derive(Debug)] +pub(crate) struct DartRepository { + pub(crate) at: PathBuf, + pub(crate) toolchain: DartToolchain, +} + +impl FromStr for DartRepository { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + debug!("Guessing toolchain the runner is run into"); + let filename = DartToolchain::lock_filename(); + let lock_file = read_file(s, filename)?; + let lock_file: PubspecLock = serde_yaml::from_str(&lock_file) + .map_err(|_| anyhow::Error::msg(format!("unable to parse {} in {}", filename, s)))?; + if lock_file + .packages + .contains_key(&DartToolchain::Flutter.to_string()) + { + return Ok(DartRepository { + at: PathBuf::from(s), + toolchain: DartToolchain::Flutter, + }); + } + Ok(DartRepository { + at: PathBuf::from(s), + toolchain: DartToolchain::Dart, + }) + } +} + +impl DartRepository { + pub(crate) fn toolchain_available(&self) -> bool { + self.toolchain.available() + } + pub(crate) fn has_specified( + &self, + package: &str, + manager: PackageManager, + requirement: &VersionReq, + ) -> anyhow::Result<()> { + let at = self.at.to_str().unwrap(); + debug!("Checking presence of {} in {} at {}", package, manager, at); + let manifest_file = read_file(at, DartToolchain::manifest_filename())?; + let manifest_file: Pubspec = serde_yaml::from_str(&manifest_file).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} in {}", + DartToolchain::manifest_filename(), + at + )) + })?; + let deps = if manager == PackageManager::DevDependencies { + manifest_file.dev_dependencies.unwrap_or_default() + } else { + manifest_file.dependencies.unwrap_or_default() + }; + let version = deps.get(package); + let version = match version { + Some(v) => PackageVersionKind::try_from(v).ok(), + None => None, + }; + match version { + // if user specifies an exact version, let's see if it matches + Some(PackageVersionKind::Exact(ref v)) if requirement.matches(v) => Ok(()), + // if user specifies a range of versions, we cannot check if it matches yet, but let's not fail early + Some(PackageVersionKind::Range(_)) => Ok(()), + None => Err(anyhow::Error::new(Error::MissingDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + Some(PackageVersionKind::Exact(_)) => Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + } + } + pub(crate) fn has_installed( + &self, + package: &str, + manager: PackageManager, + requirement: &VersionReq, + ) -> anyhow::Result<()> { + let at = self.at.to_str().unwrap(); + debug!("Checking presence of {} in {} at {}", package, manager, at); + let lock_file = read_file(at, DartToolchain::lock_filename())?; + let lock_file: PubspecLock = serde_yaml::from_str(&lock_file).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} in {}", + DartToolchain::lock_filename(), + at + )) + })?; + let dependency = lock_file.packages.get(package); + let version = match dependency { + Some(dependency) => { + if (manager == PackageManager::Dependencies + && dependency.dependency != "direct main") + || (manager == PackageManager::DevDependencies + && dependency.dependency != "direct dev") + { + return Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })); + } + PackageVersionKind::try_from(dependency).map_err(|_| { + anyhow::Error::msg(format!( + "unable to parse {} version in {}", + package, + DartToolchain::lock_filename(), + )) + })? + } + None => { + return Err(anyhow::Error::new(Error::MissingDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })) + } + }; + + match version { + PackageVersionKind::Exact(ref v) if requirement.matches(v) => Ok(()), + PackageVersionKind::Range(_) => Err(anyhow::Error::new(Error::StringError(format!( + "unexpected version range for {} in {}", + package, + DartToolchain::lock_filename() + )))), + _ => Err(anyhow::Error::new(Error::InvalidDep { + name: package.to_string(), + manager, + requirement: requirement.to_string(), + })), + } + } +} + +#[derive(Debug, Deserialize)] +struct PubspecLock { + pub packages: HashMap, +} + +#[derive(Debug, Deserialize)] +struct PubspecLockDependency { + pub dependency: String, + pub version: String, +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum PackageVersion { + Inline(String), + Multiline { version: Option }, +} + +impl PackageVersion { + pub(crate) fn version(&self) -> Option { + match self { + PackageVersion::Inline(v) => Some(v.clone()), + PackageVersion::Multiline { version } => version.clone(), + } + } +} + +#[derive(Debug)] +pub enum PackageVersionKind { + Exact(Version), + Range(VersionReq), +} + +impl TryFrom<&PackageVersion> for PackageVersionKind { + type Error = anyhow::Error; + fn try_from(version: &PackageVersion) -> Result { + if let Some(ref version) = version.version() { + return Self::from_str(version); + } + Err(anyhow::anyhow!("no version found")) + } +} + +impl TryFrom<&PubspecLockDependency> for PackageVersionKind { + type Error = anyhow::Error; + fn try_from(dependency: &PubspecLockDependency) -> Result { + Self::from_str(&dependency.version) + } +} + +impl FromStr for PackageVersionKind { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let range: [char; 4] = ['>', '<', '=', '^']; + if s.contains(range) { + let version_req = VersionReq::parse(s)?; + Ok(PackageVersionKind::Range(version_req)) + } else { + let version = Version::parse(s)?; + Ok(PackageVersionKind::Exact(version)) + } + } +} + +impl ToString for PackageVersionKind { + fn to_string(&self) -> String { + match self { + PackageVersionKind::Exact(v) => v.to_string(), + PackageVersionKind::Range(v) => v.to_string(), + } + } +} + +#[derive(Debug, PartialEq)] +pub enum PackageManager { + Dependencies, + DevDependencies, +} + +impl std::fmt::Display for PackageManager { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PackageManager::Dependencies => write!(f, "dependencies"), + PackageManager::DevDependencies => write!(f, "dev_dependencies"), + } + } +} + +#[derive(Debug, Deserialize)] +struct Pubspec { + pub dependencies: Option>, + pub dev_dependencies: Option>, +} + +#[inline] +fn read_file(at: &str, filename: &str) -> anyhow::Result { + let file = PathBuf::from(at).join(filename); + if !file.exists() { + return Err(anyhow::Error::msg(format!( + "missing {} in {}", + filename, at + ))); + } + let content = std::fs::read_to_string(file) + .map_err(|_| anyhow::Error::msg(format!("unable to read {} in {}", filename, at)))?; + Ok(content) +} + +#[cfg(test)] +mod tests { + use std::{ + path::{Path, PathBuf}, + str::FromStr, + }; + + use super::DartRepository; + use super::DartToolchain; + use lazy_static::lazy_static; + + lazy_static! { + static ref FRB_EXAMPLES_FOLDER: PathBuf = { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join("frb_example") + }; + } + + fn guess_toolchain_base(path: &Path, expect_toolchain: DartToolchain) { + let repo = DartRepository::from_str(&path.to_string_lossy()).expect(&format!( + "can get toolchain from {}", + path.to_string_lossy() + )); + assert_eq!(repo.toolchain, expect_toolchain); + } + + #[test] + fn guess_dart_toolchain() { + guess_toolchain_base( + FRB_EXAMPLES_FOLDER.join("pure_dart").join("dart").as_path(), + DartToolchain::Dart, + ); + guess_toolchain_base( + FRB_EXAMPLES_FOLDER + .join("pure_dart_multi") + .join("dart") + .as_path(), + DartToolchain::Dart, + ); + } + + #[test] + fn guess_flutter_toolchain() { + guess_toolchain_base( + FRB_EXAMPLES_FOLDER.join("with_flutter").as_path(), + DartToolchain::Flutter, + ); + } +} diff --git a/frb_codegen/src/utils.rs b/frb_codegen/src/utils.rs index 3d52574320..50ddae712f 100644 --- a/frb_codegen/src/utils.rs +++ b/frb_codegen/src/utils.rs @@ -92,347 +92,3 @@ impl Display for BlockIndex { write!(f, "{}", self.0) } } - -#[derive(Debug, PartialEq)] -pub(crate) enum DartToolchain { - Dart, - Flutter, -} - -impl ToString for DartToolchain { - fn to_string(&self) -> String { - match self { - DartToolchain::Dart => "dart", - DartToolchain::Flutter => "flutter", - } - .to_string() - } -} - -impl DartToolchain { - #[inline] - pub fn manifest_filename() -> &'static str { - "pubspec.yaml" - } - #[inline] - pub fn lock_filename() -> &'static str { - "pubspec.lock" - } -} - -impl DartToolchain { - pub(crate) fn as_run_command(&self) -> &'static str { - match self { - DartToolchain::Dart => "dart", - DartToolchain::Flutter => "flutter pub", - } - } - pub(crate) fn available(&self) -> bool { - match self { - DartToolchain::Dart => call_shell("dart --version").status.success(), - DartToolchain::Flutter => call_shell("flutter --version").status.success(), - } - } -} - -#[derive(Debug)] -pub(crate) struct DartRepository { - pub(crate) at: PathBuf, - pub(crate) toolchain: DartToolchain, -} - -impl FromStr for DartRepository { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - debug!("Guessing toolchain the runner is run into"); - let filename = DartToolchain::lock_filename(); - let lock_file = read_file(s, filename)?; - let lock_file: PubspecLock = serde_yaml::from_str(&lock_file) - .map_err(|_| anyhow::Error::msg(format!("unable to parse {} in {}", filename, s)))?; - if lock_file - .packages - .contains_key(&DartToolchain::Flutter.to_string()) - { - return Ok(DartRepository { - at: PathBuf::from(s), - toolchain: DartToolchain::Flutter, - }); - } - Ok(DartRepository { - at: PathBuf::from(s), - toolchain: DartToolchain::Dart, - }) - } -} - -impl DartRepository { - pub(crate) fn toolchain_available(&self) -> bool { - self.toolchain.available() - } - pub(crate) fn has_specified( - &self, - package: &str, - manager: PackageManager, - requirement: &VersionReq, - ) -> anyhow::Result<()> { - let at = self.at.to_str().unwrap(); - debug!("Checking presence of {} in {} at {}", package, manager, at); - let manifest_file = read_file(at, DartToolchain::manifest_filename())?; - let manifest_file: Pubspec = serde_yaml::from_str(&manifest_file).map_err(|_| { - anyhow::Error::msg(format!( - "unable to parse {} in {}", - DartToolchain::manifest_filename(), - at - )) - })?; - let deps = if manager == PackageManager::DevDependencies { - manifest_file.dev_dependencies.unwrap_or_default() - } else { - manifest_file.dependencies.unwrap_or_default() - }; - let version = deps.get(package); - let version = match version { - Some(v) => PackageVersionKind::try_from(v).ok(), - None => None, - }; - match version { - // if user specifies an exact version, let's see if it matches - Some(PackageVersionKind::Exact(ref v)) if requirement.matches(v) => Ok(()), - // if user specifies a range of versions, we cannot check if it matches yet, but let's not fail early - Some(PackageVersionKind::Range(_)) => Ok(()), - None => Err(anyhow::Error::new(Error::MissingDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })), - Some(PackageVersionKind::Exact(_)) => Err(anyhow::Error::new(Error::InvalidDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })), - } - } - pub(crate) fn has_installed( - &self, - package: &str, - manager: PackageManager, - requirement: &VersionReq, - ) -> anyhow::Result<()> { - let at = self.at.to_str().unwrap(); - debug!("Checking presence of {} in {} at {}", package, manager, at); - let lock_file = read_file(at, DartToolchain::lock_filename())?; - let lock_file: PubspecLock = serde_yaml::from_str(&lock_file).map_err(|_| { - anyhow::Error::msg(format!( - "unable to parse {} in {}", - DartToolchain::lock_filename(), - at - )) - })?; - let dependency = lock_file.packages.get(package); - let version = match dependency { - Some(dependency) => { - if (manager == PackageManager::Dependencies - && dependency.dependency != "direct main") - || (manager == PackageManager::DevDependencies - && dependency.dependency != "direct dev") - { - return Err(anyhow::Error::new(Error::InvalidDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })); - } - PackageVersionKind::try_from(dependency).map_err(|_| { - anyhow::Error::msg(format!( - "unable to parse {} version in {}", - package, - DartToolchain::lock_filename(), - )) - })? - } - None => { - return Err(anyhow::Error::new(Error::MissingDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })) - } - }; - - match version { - PackageVersionKind::Exact(ref v) if requirement.matches(v) => Ok(()), - PackageVersionKind::Range(_) => Err(anyhow::Error::new(Error::StringError(format!( - "unexpected version range for {} in {}", - package, - DartToolchain::lock_filename() - )))), - _ => Err(anyhow::Error::new(Error::InvalidDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })), - } - } -} - -#[derive(Debug, Deserialize)] -struct PubspecLock { - pub packages: HashMap, -} - -#[derive(Debug, Deserialize)] -struct PubspecLockDependency { - pub dependency: String, - pub version: String, -} - -#[derive(Debug, Deserialize)] -#[serde(untagged)] -pub enum PackageVersion { - Inline(String), - Multiline { version: Option }, -} - -impl PackageVersion { - pub(crate) fn version(&self) -> Option { - match self { - PackageVersion::Inline(v) => Some(v.clone()), - PackageVersion::Multiline { version } => version.clone(), - } - } -} - -#[derive(Debug)] -pub enum PackageVersionKind { - Exact(Version), - Range(VersionReq), -} - -impl TryFrom<&PackageVersion> for PackageVersionKind { - type Error = anyhow::Error; - fn try_from(version: &PackageVersion) -> Result { - if let Some(ref version) = version.version() { - return Self::from_str(version); - } - Err(anyhow::anyhow!("no version found")) - } -} - -impl TryFrom<&PubspecLockDependency> for PackageVersionKind { - type Error = anyhow::Error; - fn try_from(dependency: &PubspecLockDependency) -> Result { - Self::from_str(&dependency.version) - } -} - -impl FromStr for PackageVersionKind { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let range: [char; 4] = ['>', '<', '=', '^']; - if s.contains(range) { - let version_req = VersionReq::parse(s)?; - Ok(PackageVersionKind::Range(version_req)) - } else { - let version = Version::parse(s)?; - Ok(PackageVersionKind::Exact(version)) - } - } -} - -impl ToString for PackageVersionKind { - fn to_string(&self) -> String { - match self { - PackageVersionKind::Exact(v) => v.to_string(), - PackageVersionKind::Range(v) => v.to_string(), - } - } -} - -#[derive(Debug, PartialEq)] -pub enum PackageManager { - Dependencies, - DevDependencies, -} - -impl std::fmt::Display for PackageManager { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PackageManager::Dependencies => write!(f, "dependencies"), - PackageManager::DevDependencies => write!(f, "dev_dependencies"), - } - } -} - -#[derive(Debug, Deserialize)] -struct Pubspec { - pub dependencies: Option>, - pub dev_dependencies: Option>, -} - -#[inline] -fn read_file(at: &str, filename: &str) -> anyhow::Result { - let file = PathBuf::from(at).join(filename); - if !file.exists() { - return Err(anyhow::Error::msg(format!( - "missing {} in {}", - filename, at - ))); - } - let content = std::fs::read_to_string(file) - .map_err(|_| anyhow::Error::msg(format!("unable to read {} in {}", filename, at)))?; - Ok(content) -} - -#[cfg(test)] -mod tests { - use std::{ - path::{Path, PathBuf}, - str::FromStr, - }; - - use crate::utils::DartRepository; - - use super::DartToolchain; - use lazy_static::lazy_static; - - lazy_static! { - static ref FRB_EXAMPLES_FOLDER: PathBuf = { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("..") - .join("frb_example") - }; - } - - fn guess_toolchain_base(path: &Path, expect_toolchain: DartToolchain) { - let repo = DartRepository::from_str(&path.to_string_lossy()).expect(&format!( - "can get toolchain from {}", - path.to_string_lossy() - )); - assert_eq!(repo.toolchain, expect_toolchain); - } - - #[test] - fn guess_dart_toolchain() { - guess_toolchain_base( - FRB_EXAMPLES_FOLDER.join("pure_dart").join("dart").as_path(), - DartToolchain::Dart, - ); - guess_toolchain_base( - FRB_EXAMPLES_FOLDER - .join("pure_dart_multi") - .join("dart") - .as_path(), - DartToolchain::Dart, - ); - } - - #[test] - fn guess_flutter_toolchain() { - guess_toolchain_base( - FRB_EXAMPLES_FOLDER.join("with_flutter").as_path(), - DartToolchain::Flutter, - ); - } -} From 7e09a7928a6f016ff16a467306f5ab52d872baaf Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 14:23:29 +0700 Subject: [PATCH 03/17] :recycle: refactor dependencies requirement --- frb_codegen/src/commands.rs | 21 ++++++++++++++------- frb_codegen/src/tools.rs | 8 ++++++++ frb_codegen/src/utils.rs | 13 ++----------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index 226a299512..ab3279d38a 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -7,7 +7,8 @@ use std::str::FromStr; use crate::error::{Error, Result}; use crate::tools::DartRepository; use crate::tools::PackageManager; -use cargo_metadata::VersionReq; +use crate::tools::FFIGEN_REQUIREMENT; +use crate::tools::FFI_REQUIREMENT; use log::{debug, info, warn}; #[must_use] @@ -26,23 +27,29 @@ pub fn ensure_tools_available(dart_root: &str) -> Result { return Err(Error::MissingExe(repo.toolchain.to_string())); } - let requirement = VersionReq::parse(">= 2.0.1, < 3.0.0").unwrap(); if repo - .has_specified("ffi", PackageManager::Dependencies, &requirement) + .has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) .is_err() {} if repo - .has_installed("ffi", PackageManager::Dependencies, &requirement) + .has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) .is_err() {} - let requirement = VersionReq::parse(">= 6.0.1, < 7.0.0").unwrap(); if repo - .has_specified("ffigen", PackageManager::DevDependencies, &requirement) + .has_specified( + "ffigen", + PackageManager::DevDependencies, + &FFIGEN_REQUIREMENT, + ) .is_err() {} if repo - .has_installed("ffigen", PackageManager::DevDependencies, &requirement) + .has_installed( + "ffigen", + PackageManager::DevDependencies, + &FFIGEN_REQUIREMENT, + ) .is_err() {} diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index 1262dba864..a7f1020b80 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -1,11 +1,19 @@ use std::{collections::HashMap, convert::TryFrom, path::PathBuf, str::FromStr}; use cargo_metadata::{Version, VersionReq}; +use lazy_static::lazy_static; use log::debug; use serde::Deserialize; use crate::{commands::call_shell, error::Error}; +lazy_static! { + pub(crate) static ref FFI_REQUIREMENT: VersionReq = + VersionReq::from_str(">= 2.0.1, < 3.0.0").unwrap(); + pub(crate) static ref FFIGEN_REQUIREMENT: VersionReq = + VersionReq::from_str(">= 6.0.1, < 7.0.0").unwrap(); +} + #[derive(Debug, PartialEq)] pub(crate) enum DartToolchain { Dart, diff --git a/frb_codegen/src/utils.rs b/frb_codegen/src/utils.rs index 50ddae712f..89a7a48689 100644 --- a/frb_codegen/src/utils.rs +++ b/frb_codegen/src/utils.rs @@ -1,17 +1,8 @@ -use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; +use std::collections::HashSet; use std::fmt::Display; use std::fs; use std::hash::Hash; -use std::path::{Path, PathBuf}; -use std::str::FromStr; - -use cargo_metadata::{Version, VersionReq}; -use log::debug; -use serde::Deserialize; - -use crate::commands::call_shell; -use crate::error::Error; +use std::path::Path; pub fn mod_from_rust_path(code_path: &str, crate_path: &str) -> String { Path::new(code_path) From d44744bba0445d60f5995b8fbe48d12445937d46 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 14:25:58 +0700 Subject: [PATCH 04/17] :recycle: refactor tools for readability --- frb_codegen/src/tools.rs | 112 +++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index a7f1020b80..b212d9f23d 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -20,6 +20,62 @@ pub(crate) enum DartToolchain { Flutter, } +#[derive(Debug)] +pub(crate) struct DartRepository { + pub(crate) at: PathBuf, + pub(crate) toolchain: DartToolchain, +} + +#[derive(Debug, Deserialize)] +struct PubspecLock { + pub packages: HashMap, +} + +#[derive(Debug, Deserialize)] +struct PubspecLockDependency { + pub dependency: String, + pub version: String, +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum PackageVersion { + Inline(String), + Multiline { version: Option }, +} + +#[derive(Debug)] +pub enum PackageVersionKind { + Exact(Version), + Range(VersionReq), +} + +#[derive(Debug, PartialEq)] +pub enum PackageManager { + Dependencies, + DevDependencies, +} + +#[derive(Debug, Deserialize)] +struct Pubspec { + pub dependencies: Option>, + pub dev_dependencies: Option>, +} + +#[inline] +fn read_file(at: &str, filename: &str) -> anyhow::Result { + let file = PathBuf::from(at).join(filename); + if !file.exists() { + return Err(anyhow::Error::msg(format!( + "missing {} in {}", + filename, at + ))); + } + let content = std::fs::read_to_string(file) + .map_err(|_| anyhow::Error::msg(format!("unable to read {} in {}", filename, at)))?; + Ok(content) +} + impl ToString for DartToolchain { fn to_string(&self) -> String { match self { @@ -56,12 +112,6 @@ impl DartToolchain { } } -#[derive(Debug)] -pub(crate) struct DartRepository { - pub(crate) at: PathBuf, - pub(crate) toolchain: DartToolchain, -} - impl FromStr for DartRepository { type Err = anyhow::Error; @@ -197,24 +247,6 @@ impl DartRepository { } } -#[derive(Debug, Deserialize)] -struct PubspecLock { - pub packages: HashMap, -} - -#[derive(Debug, Deserialize)] -struct PubspecLockDependency { - pub dependency: String, - pub version: String, -} - -#[derive(Debug, Deserialize)] -#[serde(untagged)] -pub enum PackageVersion { - Inline(String), - Multiline { version: Option }, -} - impl PackageVersion { pub(crate) fn version(&self) -> Option { match self { @@ -224,12 +256,6 @@ impl PackageVersion { } } -#[derive(Debug)] -pub enum PackageVersionKind { - Exact(Version), - Range(VersionReq), -} - impl TryFrom<&PackageVersion> for PackageVersionKind { type Error = anyhow::Error; fn try_from(version: &PackageVersion) -> Result { @@ -271,12 +297,6 @@ impl ToString for PackageVersionKind { } } -#[derive(Debug, PartialEq)] -pub enum PackageManager { - Dependencies, - DevDependencies, -} - impl std::fmt::Display for PackageManager { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -286,26 +306,6 @@ impl std::fmt::Display for PackageManager { } } -#[derive(Debug, Deserialize)] -struct Pubspec { - pub dependencies: Option>, - pub dev_dependencies: Option>, -} - -#[inline] -fn read_file(at: &str, filename: &str) -> anyhow::Result { - let file = PathBuf::from(at).join(filename); - if !file.exists() { - return Err(anyhow::Error::msg(format!( - "missing {} in {}", - filename, at - ))); - } - let content = std::fs::read_to_string(file) - .map_err(|_| anyhow::Error::msg(format!("unable to read {} in {}", filename, at)))?; - Ok(content) -} - #[cfg(test)] mod tests { use std::{ From a2671cdc8d69c24ffcf5d3de2238bd1350332dc1 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 14:40:16 +0700 Subject: [PATCH 05/17] :memo: add doc to tools --- frb_codegen/src/tools.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index b212d9f23d..228b4b8975 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -1,3 +1,5 @@ +//! tools and environment checking + use std::{collections::HashMap, convert::TryFrom, path::PathBuf, str::FromStr}; use cargo_metadata::{Version, VersionReq}; @@ -14,29 +16,34 @@ lazy_static! { VersionReq::from_str(">= 6.0.1, < 7.0.0").unwrap(); } +/// represents dart or flutter toolchain #[derive(Debug, PartialEq)] pub(crate) enum DartToolchain { Dart, Flutter, } +/// represents a dart / flutter repository #[derive(Debug)] pub(crate) struct DartRepository { pub(crate) at: PathBuf, pub(crate) toolchain: DartToolchain, } +/// used to deserialize packages from pubspec.lock #[derive(Debug, Deserialize)] struct PubspecLock { pub packages: HashMap, } +/// represents a dependency from pubspec.lock #[derive(Debug, Deserialize)] struct PubspecLockDependency { pub dependency: String, pub version: String, } +/// handle different formatting of dependencies version in pubspec.yaml #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum PackageVersion { @@ -44,18 +51,25 @@ pub enum PackageVersion { Multiline { version: Option }, } +/// represents a package version kind #[derive(Debug)] pub enum PackageVersionKind { + /// exact dependency requirement + /// e.g. `1.2.3` Exact(Version), + /// a range of dependencies requirement + /// e.g. `^1.2.3` Range(VersionReq), } +/// represents dependencies package manager #[derive(Debug, PartialEq)] pub enum PackageManager { Dependencies, DevDependencies, } +/// used to deserialize `dependencies` and `dev_dependencies` from pubspec.yaml #[derive(Debug, Deserialize)] struct Pubspec { pub dependencies: Option>, From 5dba22b0d21ead39758f97ab8a6cda1567153724 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 14:45:33 +0700 Subject: [PATCH 06/17] :memo: add doc to tools --- frb_codegen/src/tools.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index 228b4b8975..e302d25968 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -43,7 +43,17 @@ struct PubspecLockDependency { pub version: String, } -/// handle different formatting of dependencies version in pubspec.yaml +/// extract dependency version in pubspec.yaml, no matter its format +/// +/// e.g. +/// ```yaml +/// freezed: ^2.0.1 +/// ``` +/// or +/// ```yaml +/// freezed: +/// version: ^2.0.1 +/// ``` #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum PackageVersion { @@ -152,9 +162,11 @@ impl FromStr for DartRepository { } impl DartRepository { + /// check whether the toolchain is available from the CLI pub(crate) fn toolchain_available(&self) -> bool { self.toolchain.available() } + /// check whether a package has been correctly specified in pubspec.yaml pub(crate) fn has_specified( &self, package: &str, @@ -198,6 +210,7 @@ impl DartRepository { })), } } + /// check whether a package has been correctly pinned in pubspec.lock pub(crate) fn has_installed( &self, package: &str, From fcb0ea22c4881ae7be7abe5ac8dca50821dc97e4 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 16:50:41 +0700 Subject: [PATCH 07/17] :construction: wip version checks --- frb_codegen/Cargo.toml | 1 + frb_codegen/src/tools.rs | 99 +++++++++++++++++++++++++++++++++------- 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/frb_codegen/Cargo.toml b/frb_codegen/Cargo.toml index de5899cf7c..074742fdf4 100644 --- a/frb_codegen/Cargo.toml +++ b/frb_codegen/Cargo.toml @@ -35,6 +35,7 @@ cargo_metadata = "0.14.1" enum_dispatch = "0.3.8" thiserror = "1" cbindgen = "0.24" +semver = "1.0.12" [profile.release] strip = "debuginfo" diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index e302d25968..73d1f58d78 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -1,4 +1,8 @@ //! tools and environment checking +//! +//! please note that in the future this can probably be greatly simplified, +//! and beware that Cargo and Dart interpret semantic versioning differently : +//! see this [discussion](https://github.com/fzyzcjy/flutter_rust_bridge/pull/605#discussion_r935180160) for more informations. use std::{collections::HashMap, convert::TryFrom, path::PathBuf, str::FromStr}; @@ -11,9 +15,9 @@ use crate::{commands::call_shell, error::Error}; lazy_static! { pub(crate) static ref FFI_REQUIREMENT: VersionReq = - VersionReq::from_str(">= 2.0.1, < 3.0.0").unwrap(); + VersionReq::parse(">= 2.0.1, < 3.0.0").unwrap(); pub(crate) static ref FFIGEN_REQUIREMENT: VersionReq = - VersionReq::from_str(">= 6.0.1, < 7.0.0").unwrap(); + VersionReq::parse(">= 6.0.1, < 7.0.0").unwrap(); } /// represents dart or flutter toolchain @@ -40,11 +44,40 @@ struct PubspecLock { #[derive(Debug, Deserialize)] struct PubspecLockDependency { pub dependency: String, - pub version: String, + pub version: DartDependencyVersion, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(transparent)] +pub struct DartDependencyVersion(String); + +#[derive(Debug, Clone)] +pub struct CargoDependencyVersion(String); + +impl From<&DartDependencyVersion> for CargoDependencyVersion { + /// convert from a dependency version in Dart syntax to Cargo syntax (to be used with VersionReq later on) + /// + /// be careful because this is where you can shoot yourself in the foot :) + /// + /// see module level comments for more informations. + fn from(v: &DartDependencyVersion) -> Self { + println!("{:#?}", v); + if v.0.starts_with('^') { + let version = Version::parse(v.0.split_at(1).1).unwrap(); + + if version.major > 0 { + return CargoDependencyVersion(version.to_string()); + } + return CargoDependencyVersion( + version.to_string().rsplit_once('.').unwrap().0.to_string(), + ); + } + CargoDependencyVersion(v.0.clone()) + } } /// extract dependency version in pubspec.yaml, no matter its format -/// +/// /// e.g. /// ```yaml /// freezed: ^2.0.1 @@ -57,12 +90,14 @@ struct PubspecLockDependency { #[derive(Debug, Deserialize)] #[serde(untagged)] pub enum PackageVersion { - Inline(String), - Multiline { version: Option }, + Inline(DartDependencyVersion), + Multiline { + version: Option, + }, } /// represents a package version kind -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum PackageVersionKind { /// exact dependency requirement /// e.g. `1.2.3` @@ -275,7 +310,7 @@ impl DartRepository { } impl PackageVersion { - pub(crate) fn version(&self) -> Option { + pub(crate) fn version(&self) -> Option { match self { PackageVersion::Inline(v) => Some(v.clone()), PackageVersion::Multiline { version } => version.clone(), @@ -287,7 +322,7 @@ impl TryFrom<&PackageVersion> for PackageVersionKind { type Error = anyhow::Error; fn try_from(version: &PackageVersion) -> Result { if let Some(ref version) = version.version() { - return Self::from_str(version); + return Self::try_from(version); } Err(anyhow::anyhow!("no version found")) } @@ -296,20 +331,28 @@ impl TryFrom<&PackageVersion> for PackageVersionKind { impl TryFrom<&PubspecLockDependency> for PackageVersionKind { type Error = anyhow::Error; fn try_from(dependency: &PubspecLockDependency) -> Result { - Self::from_str(&dependency.version) + Self::try_from(&dependency.version) } } -impl FromStr for PackageVersionKind { - type Err = anyhow::Error; +impl TryFrom<&DartDependencyVersion> for PackageVersionKind { + type Error = anyhow::Error; - fn from_str(s: &str) -> Result { - let range: [char; 4] = ['>', '<', '=', '^']; - if s.contains(range) { - let version_req = VersionReq::parse(s)?; + fn try_from(s: &DartDependencyVersion) -> Result { + Self::try_from(&CargoDependencyVersion::from(s)) + } +} + +impl TryFrom<&CargoDependencyVersion> for PackageVersionKind { + type Error = anyhow::Error; + + fn try_from(s: &CargoDependencyVersion) -> Result { + let range: [char; 4] = ['>', '<', '=', '~']; + if s.0.contains(range) { + let version_req = VersionReq::parse(&s.0)?; Ok(PackageVersionKind::Range(version_req)) } else { - let version = Version::parse(s)?; + let version = Version::parse(&s.0)?; Ok(PackageVersionKind::Exact(version)) } } @@ -342,7 +385,9 @@ mod tests { use super::DartRepository; use super::DartToolchain; + use cargo_metadata::VersionReq; use lazy_static::lazy_static; + use semver::Op; lazy_static! { static ref FRB_EXAMPLES_FOLDER: PathBuf = { @@ -382,4 +427,24 @@ mod tests { DartToolchain::Flutter, ); } + + #[test] + fn cannot_parse_dart_range_syntax() { + assert!(VersionReq::parse(">=0.1.2 <0.2.0").is_err()); + } + + #[test] + fn can_parse_dart_caret_syntax() { + let caret = VersionReq::parse("^0.1.2"); + assert!(caret.is_ok()); + assert_eq!(caret.unwrap().comparators.first().unwrap().op, Op::Caret); + } + + #[test] + fn cannot_compare_version_req_with_different_op() { + assert_ne!( + VersionReq::parse("0.2.1").unwrap(), + VersionReq::parse(">=0.2.1, <0.3.0").unwrap() + ); + } } From bd24f5fd2e1cee1055f7c1f622de90777a3ae360 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 16:56:19 +0700 Subject: [PATCH 08/17] :construction: wip version checks --- frb_codegen/src/tools.rs | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index 73d1f58d78..f3cd63930d 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -223,27 +223,13 @@ impl DartRepository { } else { manifest_file.dependencies.unwrap_or_default() }; - let version = deps.get(package); - let version = match version { - Some(v) => PackageVersionKind::try_from(v).ok(), - None => None, - }; - match version { - // if user specifies an exact version, let's see if it matches - Some(PackageVersionKind::Exact(ref v)) if requirement.matches(v) => Ok(()), - // if user specifies a range of versions, we cannot check if it matches yet, but let's not fail early - Some(PackageVersionKind::Range(_)) => Ok(()), - None => Err(anyhow::Error::new(Error::MissingDep { - name: package.to_string(), - manager, - requirement: requirement.to_string(), - })), - Some(PackageVersionKind::Exact(_)) => Err(anyhow::Error::new(Error::InvalidDep { + deps.get(package) + .map(|_| ()) + .ok_or(anyhow::Error::new(Error::MissingDep { name: package.to_string(), manager, requirement: requirement.to_string(), - })), - } + })) } /// check whether a package has been correctly pinned in pubspec.lock pub(crate) fn has_installed( From 62ebb37b24a7b2db5096aa2e02ffc862924e99a5 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 17:09:33 +0700 Subject: [PATCH 09/17] :necktie: simplify version checks --- frb_codegen/src/tools.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index f3cd63930d..37cd47a786 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -47,6 +47,16 @@ struct PubspecLockDependency { pub version: DartDependencyVersion, } +impl PubspecLockDependency { + pub(crate) fn installed_in(&self) -> Option { + match self.dependency.as_str() { + "direct dev" => Some(PackageManager::DevDependencies), + "direct main" => Some(PackageManager::Dependencies), + _ => None, + } + } +} + #[derive(Debug, Clone, Deserialize)] #[serde(transparent)] pub struct DartDependencyVersion(String); @@ -223,13 +233,13 @@ impl DartRepository { } else { manifest_file.dependencies.unwrap_or_default() }; - deps.get(package) - .map(|_| ()) - .ok_or(anyhow::Error::new(Error::MissingDep { + deps.get(package).map(|_| ()).ok_or_else(|| { + anyhow::Error::new(Error::MissingDep { name: package.to_string(), manager, requirement: requirement.to_string(), - })) + }) + }) } /// check whether a package has been correctly pinned in pubspec.lock pub(crate) fn has_installed( @@ -251,11 +261,8 @@ impl DartRepository { let dependency = lock_file.packages.get(package); let version = match dependency { Some(dependency) => { - if (manager == PackageManager::Dependencies - && dependency.dependency != "direct main") - || (manager == PackageManager::DevDependencies - && dependency.dependency != "direct dev") - { + let pm = dependency.installed_in(); + if pm.is_none() || (pm.unwrap() != manager) { return Err(anyhow::Error::new(Error::InvalidDep { name: package.to_string(), manager, From 969c8c5b4d05adc7308226320f63f41553dcba51 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 17:11:29 +0700 Subject: [PATCH 10/17] :necktie: simplify version checks --- frb_codegen/Cargo.toml | 2 ++ frb_example/pure_dart/rust/Cargo.lock | 5 +++-- frb_example/pure_dart_multi/rust/Cargo.lock | 5 +++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frb_codegen/Cargo.toml b/frb_codegen/Cargo.toml index 074742fdf4..384256584f 100644 --- a/frb_codegen/Cargo.toml +++ b/frb_codegen/Cargo.toml @@ -35,6 +35,8 @@ cargo_metadata = "0.14.1" enum_dispatch = "0.3.8" thiserror = "1" cbindgen = "0.24" + +[dev-dependencies] semver = "1.0.12" [profile.release] diff --git a/frb_example/pure_dart/rust/Cargo.lock b/frb_example/pure_dart/rust/Cargo.lock index f19a0355b8..581db17b92 100644 --- a/frb_example/pure_dart/rust/Cargo.lock +++ b/frb_example/pure_dart/rust/Cargo.lock @@ -268,6 +268,7 @@ dependencies = [ "pathdiff", "quote", "regex", + "semver", "serde", "serde_yaml", "structopt", @@ -572,9 +573,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" dependencies = [ "serde", ] diff --git a/frb_example/pure_dart_multi/rust/Cargo.lock b/frb_example/pure_dart_multi/rust/Cargo.lock index 59cc2d25d8..5e004bbd1d 100644 --- a/frb_example/pure_dart_multi/rust/Cargo.lock +++ b/frb_example/pure_dart_multi/rust/Cargo.lock @@ -268,6 +268,7 @@ dependencies = [ "pathdiff", "quote", "regex", + "semver", "serde", "serde_yaml", "structopt", @@ -567,9 +568,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" dependencies = [ "serde", ] From 115cc1e2e6a3dc519dbc2aecbf79ac05f4752f61 Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 17:37:55 +0700 Subject: [PATCH 11/17] :bug: update lock files --- frb_example/pure_dart/rust/Cargo.lock | 1 - frb_example/pure_dart_multi/rust/Cargo.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/frb_example/pure_dart/rust/Cargo.lock b/frb_example/pure_dart/rust/Cargo.lock index 581db17b92..e3beb6dca0 100644 --- a/frb_example/pure_dart/rust/Cargo.lock +++ b/frb_example/pure_dart/rust/Cargo.lock @@ -268,7 +268,6 @@ dependencies = [ "pathdiff", "quote", "regex", - "semver", "serde", "serde_yaml", "structopt", diff --git a/frb_example/pure_dart_multi/rust/Cargo.lock b/frb_example/pure_dart_multi/rust/Cargo.lock index 5e004bbd1d..f1e1d2d376 100644 --- a/frb_example/pure_dart_multi/rust/Cargo.lock +++ b/frb_example/pure_dart_multi/rust/Cargo.lock @@ -268,7 +268,6 @@ dependencies = [ "pathdiff", "quote", "regex", - "semver", "serde", "serde_yaml", "structopt", From ba247763502121331fe1a776e209a34aaeb788ce Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 17:43:26 +0700 Subject: [PATCH 12/17] :bug: fix error related code --- frb_codegen/src/commands.rs | 44 +++++++++++++++++-------------------- frb_codegen/src/error.rs | 11 +++++++++- frb_codegen/src/tools.rs | 3 +-- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index ab3279d38a..15d78a0a18 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -27,31 +27,27 @@ pub fn ensure_tools_available(dart_root: &str) -> Result { return Err(Error::MissingExe(repo.toolchain.to_string())); } - if repo - .has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) - .is_err() - {} - if repo - .has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) - .is_err() - {} + if let Err(e) = repo.has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) { + return Err(e.into()); + } + if let Err(e) = repo.has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) { + return Err(e.into()); + } - if repo - .has_specified( - "ffigen", - PackageManager::DevDependencies, - &FFIGEN_REQUIREMENT, - ) - .is_err() - {} - if repo - .has_installed( - "ffigen", - PackageManager::DevDependencies, - &FFIGEN_REQUIREMENT, - ) - .is_err() - {} + if let Err(e) = repo.has_installed( + "ffigen", + PackageManager::DevDependencies, + &FFIGEN_REQUIREMENT, + ) { + return Err(e.into()); + } + if let Err(e) = repo.has_installed( + "ffigen", + PackageManager::DevDependencies, + &FFIGEN_REQUIREMENT, + ) { + return Err(e.into()); + } Ok(()) } diff --git a/frb_codegen/src/error.rs b/frb_codegen/src/error.rs index e6928b27d3..c7bd111d2a 100644 --- a/frb_codegen/src/error.rs +++ b/frb_codegen/src/error.rs @@ -4,7 +4,7 @@ use crate::tools::PackageManager; pub type Result = std::result::Result<(), Error>; -#[derive(Error, Debug)] +#[derive(Error, Debug, Clone)] pub enum Error { #[error("rustfmt failed: {0}")] Rustfmt(String), @@ -44,3 +44,12 @@ impl Error { Self::StringError(msg) } } + +impl From for Error { + fn from(e: anyhow::Error) -> Self { + if let Some(e) = e.downcast_ref::() { + return e.clone(); + } + Error::StringError(e.to_string()) + } +} diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index 37cd47a786..d8f1615a5c 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -71,7 +71,6 @@ impl From<&DartDependencyVersion> for CargoDependencyVersion { /// /// see module level comments for more informations. fn from(v: &DartDependencyVersion) -> Self { - println!("{:#?}", v); if v.0.starts_with('^') { let version = Version::parse(v.0.split_at(1).1).unwrap(); @@ -118,7 +117,7 @@ pub enum PackageVersionKind { } /// represents dependencies package manager -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum PackageManager { Dependencies, DevDependencies, From c4eeef1ceb2cb6c759bf463abee141f92352347f Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Wed, 3 Aug 2022 17:51:35 +0700 Subject: [PATCH 13/17] :bug: fix wrong condition --- frb_codegen/src/commands.rs | 2 +- frb_codegen/src/error.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index 15d78a0a18..e3c377b0b9 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -34,7 +34,7 @@ pub fn ensure_tools_available(dart_root: &str) -> Result { return Err(e.into()); } - if let Err(e) = repo.has_installed( + if let Err(e) = repo.has_specified( "ffigen", PackageManager::DevDependencies, &FFIGEN_REQUIREMENT, diff --git a/frb_codegen/src/error.rs b/frb_codegen/src/error.rs index c7bd111d2a..2c96458331 100644 --- a/frb_codegen/src/error.rs +++ b/frb_codegen/src/error.rs @@ -47,7 +47,7 @@ impl Error { impl From for Error { fn from(e: anyhow::Error) -> Self { - if let Some(e) = e.downcast_ref::() { + if let Some(e) = e.downcast_ref::() { return e.clone(); } Error::StringError(e.to_string()) From d7f913f9665145617c107e8a4d529226be3b665e Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Thu, 4 Aug 2022 16:18:11 +0700 Subject: [PATCH 14/17] :art: improve error handling --- frb_codegen/src/commands.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index e3c377b0b9..1ee5211a9a 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -27,27 +27,19 @@ pub fn ensure_tools_available(dart_root: &str) -> Result { return Err(Error::MissingExe(repo.toolchain.to_string())); } - if let Err(e) = repo.has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) { - return Err(e.into()); - } - if let Err(e) = repo.has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT) { - return Err(e.into()); - } + let _ = repo.has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; + let _ = repo.has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; - if let Err(e) = repo.has_specified( + let _ = repo.has_specified( "ffigen", PackageManager::DevDependencies, &FFIGEN_REQUIREMENT, - ) { - return Err(e.into()); - } - if let Err(e) = repo.has_installed( + )?; + let _ = repo.has_installed( "ffigen", PackageManager::DevDependencies, &FFIGEN_REQUIREMENT, - ) { - return Err(e.into()); - } + )?; Ok(()) } From 301e7f04439b77fe2adce7f2095fe6f5032aaccf Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Thu, 4 Aug 2022 16:20:17 +0700 Subject: [PATCH 15/17] :art: improve pattern matching --- frb_codegen/src/tools.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index d8f1615a5c..4010a19e06 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -227,10 +227,9 @@ impl DartRepository { at )) })?; - let deps = if manager == PackageManager::DevDependencies { - manifest_file.dev_dependencies.unwrap_or_default() - } else { - manifest_file.dependencies.unwrap_or_default() + let deps = match manager { + PackageManager::Dependencies => manifest_file.dependencies.unwrap_or_default(), + PackageManager::DevDependencies => manifest_file.dev_dependencies.unwrap_or_default(), }; deps.get(package).map(|_| ()).ok_or_else(|| { anyhow::Error::new(Error::MissingDep { From 47037b7d40a6afe68d16be5fff38c3253e9fa7cb Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Thu, 4 Aug 2022 16:24:54 +0700 Subject: [PATCH 16/17] :art: improve condition --- frb_codegen/src/tools.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frb_codegen/src/tools.rs b/frb_codegen/src/tools.rs index 4010a19e06..fd37696218 100644 --- a/frb_codegen/src/tools.rs +++ b/frb_codegen/src/tools.rs @@ -260,7 +260,7 @@ impl DartRepository { let version = match dependency { Some(dependency) => { let pm = dependency.installed_in(); - if pm.is_none() || (pm.unwrap() != manager) { + if pm.as_ref() != Some(&manager) { return Err(anyhow::Error::new(Error::InvalidDep { name: package.to_string(), manager, From ebea31c5810eff7b5883ae4504a8bf42b901bffa Mon Sep 17 00:00:00 2001 From: Roms1383 Date: Thu, 4 Aug 2022 16:29:17 +0700 Subject: [PATCH 17/17] :art: fix clippy warnings --- frb_codegen/src/commands.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frb_codegen/src/commands.rs b/frb_codegen/src/commands.rs index 1ee5211a9a..a7904f49b9 100644 --- a/frb_codegen/src/commands.rs +++ b/frb_codegen/src/commands.rs @@ -27,15 +27,15 @@ pub fn ensure_tools_available(dart_root: &str) -> Result { return Err(Error::MissingExe(repo.toolchain.to_string())); } - let _ = repo.has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; - let _ = repo.has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; + repo.has_specified("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; + repo.has_installed("ffi", PackageManager::Dependencies, &FFI_REQUIREMENT)?; - let _ = repo.has_specified( + repo.has_specified( "ffigen", PackageManager::DevDependencies, &FFIGEN_REQUIREMENT, )?; - let _ = repo.has_installed( + repo.has_installed( "ffigen", PackageManager::DevDependencies, &FFIGEN_REQUIREMENT,