diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de5472e..6086fe3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ on: branches: [main, master] env: - BINARY_NAME: proxy-convert + BINARY_NAME: subforge RUST_BACKTRACE: 1 jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 927bbc0..9123b24 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - 'v*' env: - BINARY_NAME: proxy-convert + BINARY_NAME: subforge RUST_BACKTRACE: 1 jobs: diff --git a/Cargo.lock b/Cargo.lock index cc73f07..391bbd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1410,35 +1410,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proxy-convert" -version = "2.0.0" -dependencies = [ - "anyhow", - "base64", - "cached", - "chrono", - "clap", - "config", - "dirs", - "indexmap 2.12.1", - "regex", - "reqwest", - "serde", - "serde_json", - "serde_with", - "serde_yaml", - "tempfile", - "thiserror", - "tokio", - "tokio-test", - "tracing", - "tracing-subscriber", - "url", - "urlencoding", - "wiremock", -] - [[package]] name = "quinn" version = "0.11.9" @@ -2015,6 +1986,35 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subforge" +version = "2.0.0" +dependencies = [ + "anyhow", + "base64", + "cached", + "chrono", + "clap", + "config", + "dirs", + "indexmap 2.12.1", + "regex", + "reqwest", + "serde", + "serde_json", + "serde_with", + "serde_yaml", + "tempfile", + "thiserror", + "tokio", + "tokio-test", + "tracing", + "tracing-subscriber", + "url", + "urlencoding", + "wiremock", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index c01d106..d5bd961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "proxy-convert" +name = "subforge" version = "2.0.0" edition = "2021" authors = ["Messica "] -description = "A modern, extensible tool for converting between different proxy configuration formats with multi-input integration support (Clash, Sing-box, V2Ray, etc.)" +description = "A modern, extensible tool for forging unified proxy configurations from multiple subscription sources (Clash, Sing-box, V2Ray, etc.) with template-driven node selection." license = "MIT" -repository = "https://github.com/picopock/proxy-convert" -keywords = ["proxy", "clash", "sing-box", "v2ray", "subscription", "converter", "configuration", "multi-input", "integration"] +repository = "https://github.com/bitxwave/subforge" +keywords = ["proxy", "clash", "sing-box", "v2ray", "subscription"] categories = ["command-line-utilities", "network-programming"] [lib] -name = "proxy_convert" +name = "subforge" path = "src/lib.rs" [dependencies] @@ -50,7 +50,7 @@ tempfile = "3.23" wiremock = "0.6" [[bin]] -name = "proxy-convert" +name = "subforge" path = "src/main.rs" [profile.release] diff --git a/README.md b/README.md index bf71000..8c0c312 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Proxy Config Converter +# SubForge -A modern, extensible proxy configuration conversion tool supporting conversion and multi-source integration between various proxy configuration formats. +> Forge unified proxy configurations from multiple subscription sources. + +A modern, extensible Rust CLI that merges multiple proxy subscription sources (Clash, Sing-box, V2Ray, etc.) into a single configuration via template-driven node selection. ## 🚀 Features @@ -21,15 +23,15 @@ A modern, extensible proxy configuration conversion tool supporting conversion a ### Build from source ```bash -git clone https://github.com/your-username/proxy-convert.git -cd proxy-convert +git clone https://github.com/bitxwave/subforge.git +cd subforge cargo build --release ``` ### Install via Cargo ```bash -cargo install --git https://github.com/your-username/proxy-convert.git +cargo install --git https://github.com/bitxwave/subforge.git ``` ## 🎯 Quick Start @@ -38,20 +40,20 @@ cargo install --git https://github.com/your-username/proxy-convert.git ```bash # Recommended: URL-style (path/url + query params) -proxy-convert convert --source "./clash.yaml?type=clash" -proxy-convert convert --source "https://example.com/sub?type=clash&name=my&flag=clash" -o config.json +subforge convert --source "./clash.yaml?type=clash" +subforge convert --source "https://example.com/sub?type=clash&name=my&flag=clash" -o config.json # Legacy: name@type@source or type@source -proxy-convert convert --source "clash1@clash@./clash.yaml" --source "singbox1@sing-box@./singbox.json" -o config.json +subforge convert --source "clash1@clash@./clash.yaml" --source "singbox1@sing-box@./singbox.json" -o config.json # Validate config file -proxy-convert validate config.json +subforge validate config.json # Generate template -proxy-convert template singbox --output template.json +subforge template singbox --output template.json # Show version info -proxy-convert version +subforge version ``` ## 🔧 Multi-Source Mode @@ -212,7 +214,7 @@ When using `source-name@tag` format, the final node tags automatically include s ### `convert` - Convert config format ```bash -proxy-convert convert [OPTIONS] --source [--source ...] +subforge convert [OPTIONS] --source [--source ...] ``` **Arguments:** @@ -231,7 +233,7 @@ proxy-convert convert [OPTIONS] --source [--source ...] ### `validate` - Validate config format ```bash -proxy-convert validate [OPTIONS] +subforge validate [OPTIONS] ``` **Arguments:** @@ -245,7 +247,7 @@ proxy-convert validate [OPTIONS] ### `template` - Generate template ```bash -proxy-convert template [OPTIONS] +subforge template [OPTIONS] ``` **Options:** @@ -256,7 +258,7 @@ proxy-convert template [OPTIONS] ### `version` - Show version info ```bash -proxy-convert version +subforge version ``` ## ⚙️ Configuration @@ -266,13 +268,13 @@ proxy-convert version The program will look for config files in the following order: 1. `config.yaml` or `config.yml` in the current directory -2. `proxy-convert/config.yaml` or `config.yml` in the user config directory +2. `subforge/config.yaml` or `config.yml` in the user config directory ### Config Example ```yaml # config.yaml -user_agent: "ProxyConfigConverter/3.0.0" +user_agent: "SubForge/2.0.0" timeout_seconds: 30 retry_count: 3 cache_ttl_seconds: 3600 @@ -280,7 +282,7 @@ log_level: info output_format: json default_input_format: clash default_output_format: singbox -template_dir: ~/.config/proxy-convert/templates +template_dir: ~/.config/subforge/templates # sources: same format as --source, e.g. ["path?type=clash&name=my"] # sources: # - "./clash.yaml?type=clash&name=clash1" @@ -289,12 +291,12 @@ template_dir: ~/.config/proxy-convert/templates ### Environment Variables -All config items can be overridden by environment variables in the format `PROXY_CONVERT_`: +All config items can be overridden by environment variables in the format `SUBFORGE_`: ```bash -export PROXY_CONVERT_LOG_LEVEL=debug -export PROXY_CONVERT_TIMEOUT_SECONDS=60 -export PROXY_CONVERT_DEFAULT_OUTPUT_FORMAT=v2ray +export SUBFORGE_LOG_LEVEL=debug +export SUBFORGE_TIMEOUT_SECONDS=60 +export SUBFORGE_DEFAULT_OUTPUT_FORMAT=v2ray ``` ## 🏗️ Project Structure diff --git a/README.zh-CN.md b/README.zh-CN.md index 27d5d83..d4364aa 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,6 +1,8 @@ -# Proxy Config Converter +# SubForge -一个现代化、可扩展的代理配置转换工具,支持多种代理配置格式之间的转换和多输入整合。 +> 多源订阅锻造为一份统一代理配置。 + +现代化、可扩展的 Rust CLI 工具:把多个代理订阅源(Clash、Sing-box、V2Ray 等)按模板规则锻造成一份统一的代理配置。 ## 📑 目录 @@ -34,15 +36,15 @@ ### 源码编译 ```bash -git clone https://github.com/your-username/proxy-convert.git -cd proxy-convert +git clone https://github.com/bitxwave/subforge.git +cd subforge cargo build --release ``` ### 使用 Cargo 安装 ```bash -cargo install --git https://github.com/your-username/proxy-convert.git +cargo install --git https://github.com/bitxwave/subforge.git ``` ## 🎯 快速开始 @@ -54,7 +56,7 @@ cargo install --git https://github.com/your-username/proxy-convert.git - 转换订阅 ```bash - proxy-convert convert [OPTIONS] --source [--source ...] + subforge convert [OPTIONS] --source [--source ...] ``` **选项:** @@ -82,40 +84,40 @@ cargo install --git https://github.com/your-username/proxy-convert.git - **验证配置文件**:验证配置文件的格式 ```bash - proxy-convert validate [-f, --format ] + subforge validate [-f, --format ] ``` - **生成模板**: ```bash - proxy-convert template [-o, --output ] [-t, --template-type ] + subforge template [-o, --output ] [-t, --template-type ] ``` - **显示版本信息**: ```bash - proxy-convert version + subforge version ``` #### 使用示例 ```bash # 单源 -proxy-convert convert --source "./clash.yaml?type=clash" +subforge convert --source "./clash.yaml?type=clash" # 多源(可带 name、flag) -proxy-convert convert \ +subforge convert \ --source "https://example.com/sub?type=clash&name=my&flag=clash" \ --source "examples/sources/Eternal Network?type=singbox" \ -o config.json # 使用配置文件(sources 格式与 --source 一致) -proxy-convert convert --config examples/config.yaml -o config.json +subforge convert --config examples/config.yaml -o config.json # 其他命令 -proxy-convert validate config.json -proxy-convert template singbox --output template.json -proxy-convert version +subforge validate config.json +subforge template singbox --output template.json +subforge version ``` ## 📋 插值规则系统 ✨ @@ -289,7 +291,7 @@ proxy-convert version 程序会按以下顺序查找配置文件: 1. 当前目录下的 `config.yaml` 或 `config.yml` -2. 用户配置目录下的 `proxy-convert/config.yaml` 或 `config.yml` +2. 用户配置目录下的 `subforge/config.yaml` 或 `config.yml` **配置文件优先级:** @@ -299,7 +301,7 @@ proxy-convert version ```yaml # config.yaml -user_agent: "ProxyConfigConverter/3.0.0" +user_agent: "SubForge/2.0.0" timeout_seconds: 30 retry_count: 3 log_level: info @@ -325,12 +327,12 @@ default_output_format: singbox ### 环境变量 -所有配置项均可通过环境变量覆盖,格式为 `PROXY_CONVERT_`(嵌套键使用 `__`): +所有配置项均可通过环境变量覆盖,格式为 `SUBFORGE_`(嵌套键使用 `__`): ```bash -export PROXY_CONVERT_LOG_LEVEL=debug -export PROXY_CONVERT_TIMEOUT_SECONDS=60 -export PROXY_CONVERT_DEFAULT_OUTPUT_FORMAT=v2ray +export SUBFORGE_LOG_LEVEL=debug +export SUBFORGE_TIMEOUT_SECONDS=60 +export SUBFORGE_DEFAULT_OUTPUT_FORMAT=v2ray ``` **配置优先级:** 命令行 > 环境变量 > 配置文件 > 默认值 diff --git a/src/commands/cli.rs b/src/commands/cli.rs index c323ad4..1e3e72c 100644 --- a/src/commands/cli.rs +++ b/src/commands/cli.rs @@ -3,16 +3,16 @@ use std::path::PathBuf; #[derive(Parser, Debug)] #[command( - name = "proxy-convert", + name = "subforge", author = "Messica ", version = "2.0.0", - about = "A modern tool for converting proxy configuration", - long_about = "A powerful tool for converting proxy configuration. Supports multiple protocol conversions, template customization, rule filtering and other features." + about = "Forge unified proxy configurations from multiple subscription sources", + long_about = "SubForge merges multiple proxy subscription sources (Clash, Sing-box, V2Ray, etc.) into a single configuration via template-driven node selection. Supports multi-source integration, tag-based filtering, and pluggable protocol processors." )] pub struct Cli { /// Configuration file path. If not specified, will search in default locations: /// 1. ./config.yaml or ./config.yml (current directory) - /// 2. ~/.config/proxy-convert/config.yaml (Linux/macOS) or %APPDATA%/proxy-convert/config.yaml (Windows) + /// 2. ~/.config/subforge/config.yaml (Linux/macOS) or %APPDATA%/subforge/config.yaml (Windows) #[arg(short, long, global = true, value_name = "PATH")] pub config: Option, diff --git a/src/commands/version.rs b/src/commands/version.rs index 82e7293..4d5de78 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -2,8 +2,8 @@ /// Display version information pub fn handle_version() { - println!("proxy-convert v{}", env!("CARGO_PKG_VERSION")); - println!("A modern, extensible proxy configuration conversion tool"); + println!("subforge v{}", env!("CARGO_PKG_VERSION")); + println!("Forge unified proxy configurations from multiple subscription sources"); } #[cfg(test)] diff --git a/src/core/config.rs b/src/core/config.rs index c1c1c28..dd7bbec 100644 --- a/src/core/config.rs +++ b/src/core/config.rs @@ -51,7 +51,7 @@ pub struct AppConfig { fn default_user_agent() -> String { // Empty by default -> SourceLoader picks a protocol-matched UA per-request // (sing-box/mihomo/v2rayN). Subscription panels route by UA, so a generic - // UA like "Mozilla/... ProxyConvert/..." gets rejected. + // UA like "Mozilla/... SubForge/..." gets rejected. String::new() } @@ -89,7 +89,7 @@ impl Default for AppConfig { impl AppConfig { /// Load config: optional file from default paths (first existing) + env vars. - /// Env vars (PROXY_CONVERT_*) override file. Used when no explicit config path is given. + /// Env vars (SUBFORGE_*) override file. Used when no explicit config path is given. fn load_default_locations() -> Result { let paths = Self::get_config_paths(); let mut builder = config::Config::builder(); @@ -102,7 +102,7 @@ impl AppConfig { } } builder = builder.add_source( - config::Environment::with_prefix("PROXY_CONVERT").separator("__"), + config::Environment::with_prefix("SUBFORGE").separator("__"), ); let c = builder .build() @@ -129,7 +129,7 @@ impl AppConfig { // User config directory if let Some(config_dir) = dirs::config_dir() { - let app_config_dir = config_dir.join("proxy-convert"); + let app_config_dir = config_dir.join("subforge"); paths.push(app_config_dir.join("config.yaml")); paths.push(app_config_dir.join("config.yml")); } @@ -163,13 +163,13 @@ impl AppConfig { } /// Load application configuration. - /// Priority: explicit path (if given) > default paths (first existing) > env (PROXY_CONVERT_*) > serde defaults. + /// Priority: explicit path (if given) > default paths (first existing) > env (SUBFORGE_*) > serde defaults. pub fn load_from_path(config_path: Option<&str>) -> Result { let config = if let Some(path) = config_path { let c = config::Config::builder() .add_source(config::File::from(std::path::Path::new(path)).required(true)) .add_source( - config::Environment::with_prefix("PROXY_CONVERT").separator("__"), + config::Environment::with_prefix("SUBFORGE").separator("__"), ) .build() .map_err(|e| ConvertError::ConfigValidationError(e.to_string()))?; diff --git a/src/core/logging.rs b/src/core/logging.rs index 93512c4..5e9d665 100644 --- a/src/core/logging.rs +++ b/src/core/logging.rs @@ -7,7 +7,7 @@ use tracing_subscriber::EnvFilter; /// Initialize logging system pub fn init_logging(log_level: Level) -> Result<()> { let filter = EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new(format!("proxy_convert={}", log_level.as_str()))); + .unwrap_or_else(|_| EnvFilter::new(format!("subforge={}", log_level.as_str()))); tracing_subscriber::fmt() .with_env_filter(filter) diff --git a/src/lib.rs b/src/lib.rs index 5b90009..ace9b8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! Proxy-convert library: core, protocols, commands, utils. +//! SubForge library: core, protocols, commands, utils. //! Used by the CLI binary and by integration tests. pub mod commands; diff --git a/src/main.rs b/src/main.rs index 85de026..dfc2d05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ -//! Proxy Configuration Converter Main Program +//! SubForge Main Program //! -//! A modern, extensible tool for converting between different proxy configuration formats. -//! Supports Clash, Sing-box, V2Ray and other formats. - -use proxy_convert::commands::cli::{Cli, Commands}; -use proxy_convert::commands::{convert, template, validate, version}; -use proxy_convert::core::error; -use proxy_convert::core::{config::AppConfig, logging}; +//! A modern, extensible tool for forging unified proxy configurations from +//! multiple subscription sources (Clash, Sing-box, V2Ray, etc.) with +//! template-driven node selection. + +use subforge::commands::cli::{Cli, Commands}; +use subforge::commands::{convert, template, validate, version}; +use subforge::core::error; +use subforge::core::{config::AppConfig, logging}; use clap::Parser; use tracing::Level; @@ -43,7 +44,7 @@ async fn run() -> error::Result<()> { }; logging::init_logging(log_level)?; - let registry = proxy_convert::protocols::ProtocolRegistry::init(); + let registry = subforge::protocols::ProtocolRegistry::init(); match cli.command { Commands::Convert(args) => convert::handle_convert(&args, &config, ®istry).await, diff --git a/tests/convert_integration.rs b/tests/convert_integration.rs index 879e06b..b91d137 100644 --- a/tests/convert_integration.rs +++ b/tests/convert_integration.rs @@ -1,9 +1,9 @@ //! Integration tests: full convert/validate flow with file-based sources. -use proxy_convert::commands::convert::ConvertCommand; -use proxy_convert::core::config::AppConfig; -use proxy_convert::core::source::Protocol; -use proxy_convert::protocols::ProtocolRegistry; +use subforge::commands::convert::ConvertCommand; +use subforge::core::config::AppConfig; +use subforge::core::source::Protocol; +use subforge::protocols::ProtocolRegistry; use tempfile::NamedTempFile; fn minimal_singbox_content() -> &'static str { @@ -41,7 +41,7 @@ async fn integration_convert_file_source_singbox_to_json() { let source_meta = ConvertCommand::parse_source_string(&source_str).unwrap(); assert!(matches!(source_meta.source_type, Protocol::SingBox)); - let source = proxy_convert::utils::source::SourceLoader::load_source( + let source = subforge::utils::source::SourceLoader::load_source( &source_meta, ®istry, &config, @@ -108,7 +108,7 @@ async fn integration_convert_file_source_clash_to_singbox() { #[test] fn integration_subscription_parse_plain_text() { let content = "vmess://uuid@host:443#name1\ntrojan://pwd@h2:8443#name2\n"; - let servers = proxy_convert::protocols::subscription::parse_plain_text(content).unwrap(); + let servers = subforge::protocols::subscription::parse_plain_text(content).unwrap(); assert_eq!(servers.len(), 2); assert_eq!(servers[0].protocol, "vmess"); assert_eq!(servers[0].name, "name1"); @@ -129,7 +129,7 @@ fn integration_subscription_parse_mixed_with_new_protocols() { "socks5://alice:secret@3.3.3.3:1080#socks-1\n", "ssh://root:hunter2@4.4.4.4:22#ssh-1\n", ); - let servers = proxy_convert::protocols::subscription::parse_plain_text(content).unwrap(); + let servers = subforge::protocols::subscription::parse_plain_text(content).unwrap(); let protocols: Vec<&str> = servers.iter().map(|s| s.protocol.as_str()).collect(); assert_eq!( protocols, diff --git a/tests/parser_test.rs b/tests/parser_test.rs index 5a4bf10..6fb407f 100644 --- a/tests/parser_test.rs +++ b/tests/parser_test.rs @@ -1,11 +1,11 @@ //! Tests for parser extraction of individual proxy types from strongly-typed configs. -use proxy_convert::core::source::Protocol; -use proxy_convert::protocols::{clash, singbox, ProxyParams}; -use proxy_convert::protocols::source::{Config, Source}; +use subforge::core::source::Protocol; +use subforge::protocols::{clash, singbox, ProxyParams}; +use subforge::protocols::source::{Config, Source}; fn make_source(config: Config, source_type: Protocol) -> Source { - use proxy_convert::core::source::{SourceLocation, SourceMeta}; + use subforge::core::source::{SourceLocation, SourceMeta}; Source { meta: SourceMeta { name: Some("test".into()), @@ -373,8 +373,8 @@ fn test_extract_singbox_anytls() { #[test] fn test_clash_anytls_to_singbox_node_config() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -411,7 +411,7 @@ fn test_clash_anytls_to_singbox_node_config() { #[test] fn test_parse_anytls_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("anytls://letmein@example.com:8443/?sni=real.example.com&insecure=1#node1") .unwrap() .expect("parsed"); @@ -475,8 +475,8 @@ fn test_extract_clash_vless_with_reality() { #[test] fn test_clash_vless_reality_to_singbox_emits_reality() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -552,8 +552,8 @@ fn test_extract_clash_hysteria2() { #[test] fn test_clash_hysteria2_to_singbox_emits_obfs_and_mbps() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -618,8 +618,8 @@ fn test_extract_clash_hysteria_v1() { #[test] fn test_clash_hysteria_v1_to_singbox_uses_auth_str() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -683,8 +683,8 @@ fn test_extract_clash_tuic_v5() { #[test] fn test_clash_tuic_to_singbox_emits_v5_fields() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -753,8 +753,8 @@ fn test_extract_clash_wireguard_simplified() { #[test] fn test_clash_wireguard_full_peers_to_singbox() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let config: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -790,7 +790,7 @@ fn test_clash_wireguard_full_peers_to_singbox() { #[test] fn test_parse_hysteria_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("hysteria://h.example.com:443?auth=pwd&peer=h.com&upmbps=30&downmbps=200&obfs=mystic#hy1") .unwrap() .expect("parsed"); @@ -811,7 +811,7 @@ fn test_parse_hysteria_url() { #[test] fn test_parse_tuic_v5_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("tuic://U:P@host.com:443/?sni=host.com&congestion_control=bbr&udp_relay_mode=native&allow_insecure=1#tuic1") .unwrap() .expect("parsed"); @@ -876,7 +876,7 @@ fn test_extract_clash_multiple_proxies() { #[test] fn test_parse_ssr_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; // body = "1.2.3.4:443:auth_aes128_md5:aes-256-cfb:plain:base64(mypass)/?remarks=base64(mynode)" let url = "ssr://MS4yLjMuNDo0NDM6YXV0aF9hZXMxMjhfbWQ1OmFlcy0yNTYtY2ZiOnBsYWluOmJYbHdZWE56Lz9yZW1hcmtzPWJYbHViMlJs"; let s = parse_proxy_url(url).unwrap().expect("parsed ssr"); @@ -900,7 +900,7 @@ fn test_parse_ssr_url() { #[test] fn test_parse_snell_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("snell://yourpsk@example.com:443?obfs=tls&obfs-host=cdn.example.com&version=3#snell-1") .unwrap() .expect("parsed snell"); @@ -925,7 +925,7 @@ fn test_parse_snell_url() { #[test] fn test_parse_socks5_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("socks5://alice:secret@example.com:1080?tls=1&allowInsecure=1#socks-1") .unwrap() .expect("parsed socks5"); @@ -954,7 +954,7 @@ fn test_parse_socks5_url() { #[test] fn test_parse_ssh_url() { - use proxy_convert::protocols::subscription::parse_proxy_url; + use subforge::protocols::subscription::parse_proxy_url; let s = parse_proxy_url("ssh://root:hunter2@example.com:2222#bastion") .unwrap() .expect("parsed ssh"); @@ -980,7 +980,7 @@ fn test_parse_ssh_url() { fn test_parse_subscription_with_ssr_lines() { // Regression: detect.rs accepts ssr:// as subscription, but parse_proxy_url // used to drop it → the whole sub returned 0 nodes. Now ssr lines yield ProxyServer. - use proxy_convert::protocols::subscription::parse_subscription; + use subforge::protocols::subscription::parse_subscription; let content = concat!( "ssr://MS4yLjMuNDo0NDM6YXV0aF9hZXMxMjhfbWQ1OmFlcy0yNTYtY2ZiOnBsYWluOmJYbHdZWE56Lz9yZW1hcmtzPWJYbHViMlJs\n", "ss://YWVzLTI1Ni1nY206cGFzcw==@5.6.7.8:8388#ss-node\n" @@ -1331,8 +1331,8 @@ fn test_extract_singbox_naive() { #[test] fn test_clash_socks5_to_singbox_renames_type_and_rebuilds_tls() { - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -1375,8 +1375,8 @@ fn test_clash_socks5_to_singbox_renames_type_and_rebuilds_tls() { #[test] fn test_singbox_socks_to_clash_renames_type() { - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], @@ -1410,8 +1410,8 @@ fn test_clash_http_to_singbox_passthrough_preserves_tls() { // HTTP field shape is identical between Clash and sing-box (username, // password, tls), so the generic path already produced the right output — // but we lock that in here so a future refactor can't silently break it. - use proxy_convert::protocols::singbox::template_processor::SingboxProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::singbox::template_processor::SingboxProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -1441,8 +1441,8 @@ fn test_clash_snell_to_clash_does_not_emit_password() { // mihomo Snell uses `psk`, not `password`. Generic emit used to insert // `password: ` because we set ProxyServer.password = ssr_or_snell_psk // for routing convenience. The output now suppresses that key for snell. - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: clash::Config = serde_json::from_value(serde_json::json!({ "proxies": [{ @@ -1482,8 +1482,8 @@ fn test_clash_snell_to_clash_does_not_emit_password() { #[test] fn test_singbox_vless_reality_to_clash() { - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ @@ -1520,8 +1520,8 @@ fn test_singbox_vless_reality_to_clash() { #[test] fn test_singbox_hysteria2_to_clash() { - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ @@ -1556,8 +1556,8 @@ fn test_singbox_hysteria2_to_clash() { #[test] fn test_singbox_hysteria_v1_to_clash() { - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ @@ -1595,8 +1595,8 @@ fn test_singbox_tuic_to_clash_converts_heartbeat_to_milliseconds() { // was emitted verbatim as `heartbeat-interval: "10s"`, but mihomo expects // an integer in milliseconds. The fix converts duration suffixes // (s / ms) to a numeric ms value. - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ @@ -1635,8 +1635,8 @@ fn test_singbox_tuic_to_clash_converts_heartbeat_to_milliseconds() { fn test_singbox_tuic_heartbeat_in_ms_passthrough() { // Cover the other suffix mihomo's converter encounters: sing-box may also // emit "500ms" verbatim; should land as 500. - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ @@ -1663,8 +1663,8 @@ fn test_singbox_tuic_heartbeat_in_ms_passthrough() { fn test_singbox_wireguard_to_clash_splits_local_addresses() { // sing-box uses `local_address: ["10.0.0.2/32", "fd00::2/128"]`. mihomo // splits these into separate `ip` and `ipv6` fields without the mask. - use proxy_convert::protocols::clash::template_processor::ClashProcessor; - use proxy_convert::protocols::ProtocolProcessor; + use subforge::protocols::clash::template_processor::ClashProcessor; + use subforge::protocols::ProtocolProcessor; let cfg: singbox::Config = serde_json::from_value(serde_json::json!({ "inbounds": [], "outbounds": [{ diff --git a/tests/subscription_test.rs b/tests/subscription_test.rs index 8b41679..eb6b01f 100644 --- a/tests/subscription_test.rs +++ b/tests/subscription_test.rs @@ -1,8 +1,8 @@ //! Tests for subscription URL parsing (vmess://, vless://, trojan://, ss://, hysteria2://). use base64::Engine; -use proxy_convert::protocols::subscription; -use proxy_convert::protocols::ProxyParams; +use subforge::protocols::subscription; +use subforge::protocols::ProxyParams; #[test] fn test_parse_vmess_url() { diff --git a/tests/transport_roundtrip_test.rs b/tests/transport_roundtrip_test.rs index 7e748f6..419bcd6 100644 --- a/tests/transport_roundtrip_test.rs +++ b/tests/transport_roundtrip_test.rs @@ -1,6 +1,6 @@ //! Tests for transport/TLS conversion roundtrips between Clash and sing-box formats. -use proxy_convert::protocols::transport_converter; +use subforge::protocols::transport_converter; use serde_json::{json, Map}; use std::collections::HashMap; diff --git a/tests/url_fetch_test.rs b/tests/url_fetch_test.rs index a27d911..aaa655c 100644 --- a/tests/url_fetch_test.rs +++ b/tests/url_fetch_test.rs @@ -5,11 +5,11 @@ //! - retry behavior on transient 5xx //! - error classification / message hints -use proxy_convert::commands::convert::ConvertCommand; -use proxy_convert::core::config::AppConfig; -use proxy_convert::core::error::{ConvertError, NetworkErrorKind}; -use proxy_convert::protocols::ProtocolRegistry; -use proxy_convert::utils::source::SourceLoader; +use subforge::commands::convert::ConvertCommand; +use subforge::core::config::AppConfig; +use subforge::core::error::{ConvertError, NetworkErrorKind}; +use subforge::protocols::ProtocolRegistry; +use subforge::utils::source::SourceLoader; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate};