Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: refine tman install codes #509

Merged
merged 2 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions core/src/ten_manager/src/cmd/cmd_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ use crate::{
constants::{APP_DIR_IN_DOT_TEN_DIR, DOT_TEN_DIR},
dep_and_candidate::get_all_candidates_from_deps,
error::TmanError,
fs::check_is_app_folder,
install::{
compare_solver_results_with_existed_pkgs,
filter_compatible_pkgs_to_candidates, is_installing_package_standalone,
Expand Down Expand Up @@ -303,13 +302,15 @@ pub async fn execute_cmd(

match affected_pkg_type {
PkgType::App => {
check_is_app_folder(&cwd)?;

// Push the app itself into the initial_input_pkgs.
// The TEN app itself is also a package. Extensions can declare
// dependencies on a specific version of an app, so the app also
// needs to be included in the package list for dependency tree
// calculation.
initial_input_pkgs.push(get_pkg_info_from_path(&cwd, true)?);

all_existing_local_pkgs =
tman_get_all_existed_pkgs_info_of_app(tman_config, &cwd)?;

filter_compatible_pkgs_to_candidates(
tman_config,
&all_existing_local_pkgs,
Expand Down
3 changes: 0 additions & 3 deletions core/src/ten_rust/src/pkg_info/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,3 @@ pub const EXTENSION_DIR: &str = "extension";
pub const PROTOCOL_DIR: &str = "protocol";
pub const ADDON_LOADER_DIR: &str = "addon_loader";
pub const SYSTEM_DIR: &str = "system";

pub const ERR_STR_NOT_APP_DIR: &str =
"The current working directory does not belong to the `app`.";
37 changes: 36 additions & 1 deletion core/src/ten_rust/src/pkg_info/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub mod support;

use std::{fmt, fs, path::Path, str::FromStr};

use anyhow::{Context, Result};
use anyhow::{anyhow, Context, Result};
use serde::{Deserialize, Serialize};

use crate::pkg_info::utils::read_file_to_string;
Expand Down Expand Up @@ -72,6 +72,41 @@ impl Manifest {
pub fn validate_and_complete(&mut self) -> Result<()> {
Ok(())
}

pub fn check_fs_location(
&self,
addon_type_folder_name: Option<&str>,
addon_folder_name: Option<&str>,
) -> Result<()> {
if let Some(addon_folder_name) = addon_folder_name {
// The package `Foo` must be located inside the folder
// `Foo/`, so that during runtime dynamic loading, the
// desired package can be identified simply by searching
// for the folder name. Additionally, the unique nature
// of package names can be ensured through the file
// system's restriction that prevents duplicate folder
// names within the same directory.
if self.type_and_name.name != addon_folder_name {
return Err(anyhow!(format!(
"the name of the folder '{}' and the package '{}' are different",
addon_folder_name, self.type_and_name.name
)));
}
}

if let Some(addon_type_folder_name) = addon_type_folder_name {
if self.type_and_name.pkg_type.to_string() != addon_type_folder_name
{
return Err(anyhow!(format!(
"The folder name '{}' does not match the expected package type '{}'",
addon_type_folder_name,
self.type_and_name.pkg_type.to_string(),
)));
}
}

Ok(())
}
}

pub fn dump_manifest_str_to_file<P: AsRef<Path>>(
Expand Down
108 changes: 47 additions & 61 deletions core/src/ten_rust/src/pkg_info/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ use std::{
path::{Path, PathBuf},
};

use anyhow::Result;
use anyhow::{anyhow, Result};
use graph::Graph;
use pkg_basic_info::PkgBasicInfo;
use pkg_type_and_name::PkgTypeAndName;

use crate::schema::store::SchemaStore;
use api::PkgApi;
use constants::{
ADDON_LOADER_DIR, ERR_STR_NOT_APP_DIR, EXTENSION_DIR,
MANIFEST_JSON_FILENAME, PROTOCOL_DIR, SYSTEM_DIR, TEN_PACKAGES_DIR,
ADDON_LOADER_DIR, EXTENSION_DIR, MANIFEST_JSON_FILENAME, PROTOCOL_DIR,
SYSTEM_DIR, TEN_PACKAGES_DIR,
};
use dependencies::{get_pkg_dependencies_from_manifest, PkgDependency};
use manifest::{parse_manifest_from_file, parse_manifest_in_folder, Manifest};
Expand Down Expand Up @@ -188,41 +188,47 @@ impl PkgInfo {
}
}

/// Retrieve the package represented by the specified path from the information
/// within that path.
pub fn get_pkg_info_from_path(
pkg_path: &Path,
path: &Path,
is_installed: bool,
) -> Result<PkgInfo> {
let manifest = parse_manifest_in_folder(pkg_path)?;
let property = parse_property_in_folder(pkg_path)?;
let manifest = parse_manifest_in_folder(path)?;
let property = parse_property_in_folder(path)?;

let mut pkg_info: PkgInfo = PkgInfo::from_metadata(&manifest, &property)?;

pkg_info.is_installed = is_installed;
pkg_info.url = pkg_path.to_string_lossy().to_string();

// This package comes from a path, not from a registry, so the value of
// `url` will be the path.
pkg_info.url = path.to_string_lossy().to_string();

Ok(pkg_info)
}

fn collect_pkg_info_from_path(
base_path: &Path,
pkgs_info: &mut HashMap<PkgTypeAndName, PkgInfo>,
) -> Result<Manifest> {
let pkg_info = get_pkg_info_from_path(base_path, true)?;

/// Collect the corresponding package from the information within the specified
/// path, add it to the collection provided as a parameter, and return the newly
/// collected package.
fn collect_pkg_info_from_path<'a>(
path: &Path,
pkgs_info: &'a mut HashMap<PkgTypeAndName, PkgInfo>,
) -> Result<&'a PkgInfo> {
let pkg_info = get_pkg_info_from_path(path, true)?;
let pkg_type_name = PkgTypeAndName::from(&pkg_info);
if pkgs_info.contains_key(&pkg_type_name) {
return Err(anyhow::anyhow!(

match pkgs_info.entry(pkg_type_name) {
std::collections::hash_map::Entry::Occupied(_) => Err(anyhow!(
"Duplicated package, type: {}, name: {}",
pkg_info.basic_info.type_and_name.pkg_type,
pkg_info.basic_info.type_and_name.name
));
)),
std::collections::hash_map::Entry::Vacant(entry) => {
let inserted = entry.insert(pkg_info);
Ok(inserted)
}
}

let manifest = pkg_info.manifest.clone().unwrap().clone();

pkgs_info.insert(pkg_type_name, pkg_info);

Ok(manifest)
}

pub fn get_all_existed_pkgs_info_of_app_to_hashmap(
Expand All @@ -231,64 +237,44 @@ pub fn get_all_existed_pkgs_info_of_app_to_hashmap(
let mut pkgs_info: HashMap<PkgTypeAndName, PkgInfo> = HashMap::new();

// Process the manifest.json file in the root path.
let app_pkg_manifest =
collect_pkg_info_from_path(app_path, &mut pkgs_info)?;
if app_pkg_manifest.type_and_name.pkg_type != PkgType::App {
return Err(anyhow::anyhow!(ERR_STR_NOT_APP_DIR));
let app_pkg = collect_pkg_info_from_path(app_path, &mut pkgs_info)?;
if app_pkg.basic_info.type_and_name.pkg_type != PkgType::App {
return Err(anyhow!(
"The current working directory does not belong to the `app`."
));
}

// Define paths to include manifest.json files from.
// Define the sub-folders for searching packages.
let addon_type_dirs =
vec![EXTENSION_DIR, PROTOCOL_DIR, ADDON_LOADER_DIR, SYSTEM_DIR];

for addon_type_dir in addon_type_dirs {
let allowed_path = app_path.join(TEN_PACKAGES_DIR).join(addon_type_dir);
let addon_type_dir_path =
app_path.join(TEN_PACKAGES_DIR).join(addon_type_dir);

if allowed_path.exists() && allowed_path.is_dir() {
for entry in allowed_path.read_dir()?.flatten() {
if addon_type_dir_path.exists() && addon_type_dir_path.is_dir() {
for entry in addon_type_dir_path.read_dir()?.flatten() {
let path = entry.path();

if path.is_dir() {
// An essential component of a TEN package folder is its
// `manifest.json` file that adheres to the TEN standard.
let manifest_path = path.join(MANIFEST_JSON_FILENAME);

if manifest_path.exists() && manifest_path.is_file() {
let manifest =
parse_manifest_from_file(&manifest_path)?;

// Do some simple checks.
if manifest.type_and_name.name
!= path.file_name().unwrap().to_str().unwrap()
{
return Err(anyhow::anyhow!(
"The path '{}' is not valid: {}.",
format!("{}/{}",addon_type_dir, manifest.type_and_name.name),
format!(
"the path '{}' and the name '{}' of the package are different",
path.file_name().unwrap().to_str().unwrap(), manifest.type_and_name.name
)));
}

if manifest.type_and_name.pkg_type.to_string()
!= addon_type_dir
{
return Err(anyhow::anyhow!(
"The path '{}' is not valid: {}.",
format!(
"{}/{}",
addon_type_dir,
manifest.type_and_name.name
),
format!(
"the package type '{}' is not as expected '{}'",
manifest.type_and_name.pkg_type.to_string(), addon_type_dir)
));
}
manifest.check_fs_location(
Some(addon_type_dir),
path.file_name().and_then(|os_str| os_str.to_str()),
)?;

// This folder contains a manifest.json file and
// that manifest.json file is a correct TEN
// manifest.json file, and this package is a
// dependency of app, so read it to treat it as a
// local dependency.
// manifest.json file, so that it means the folder
// represents a valid TEN package, and this package is a
// _local_ dependency of app.

collect_pkg_info_from_path(&path, &mut pkgs_info)?;
}
Expand Down
Loading