DEV Community

Cover image for Cross Platform Web Write Once Run Rust Framework(1750828453408300)
member_c6d11ca9
member_c6d11ca9

Posted on

Cross Platform Web Write Once Run Rust Framework(1750828453408300)

Cross-Platform: Write Once, Run Everywhere

As a third-year computer science student, I frequently face challenges with cross-platform deployment when developing web applications. Different operating systems, different architectures, different environment configurations - these issues give me headaches when deploying projects. It wasn't until I encountered a Rust framework whose cross-platform features completely solved my troubles. This framework made me truly experience the charm of "write once, run everywhere."

The Magic of Cross-Platform Compilation

This Rust framework is developed based on the Rust language, and Rust's cross-platform compilation capabilities amaze me. I can develop on Windows and then compile executable files for Linux, macOS, and even ARM architectures.

use hyperlane::*;
use std::env;
use std::path::Path;

// Cross-platform path handling
async fn handle_file_upload(ctx: Context) {
    let upload_dir = if cfg!(target_os = "windows") {
        "C:\\uploads"
    } else {
        "/tmp/uploads"
    };

    // Ensure directory exists
    if !Path::new(upload_dir).exists() {
        std::fs::create_dir_all(upload_dir).unwrap();
    }

    let file_name = format!("{}_{}",
        chrono::Utc::now().timestamp(),
        "uploaded_file.txt"
    );

    let file_path = if cfg!(target_os = "windows") {
        format!("{}\\{}", upload_dir, file_name)
    } else {
        format!("{}/{}", upload_dir, file_name)
    };

    let body = ctx.get_request_body().await;
    std::fs::write(&file_path, body).unwrap();

    let response = UploadResponse {
        success: true,
        file_path: file_path,
        message: "File uploaded successfully".to_string(),
    };

    let response_json = serde_json::to_string(&response).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct UploadResponse {
    success: bool,
    file_path: String,
    message: String,
}

// Cross-platform system information retrieval
async fn system_info(ctx: Context) {
    let info = SystemInfo {
        os: env::consts::OS.to_string(),
        arch: env::consts::ARCH.to_string(),
        family: env::consts::FAMILY.to_string(),
        current_dir: env::current_dir().unwrap().to_string_lossy().to_string(),
        temp_dir: env::temp_dir().to_string_lossy().to_string(),
        framework_version: env!("CARGO_PKG_VERSION").to_string(),
    };

    let response_json = serde_json::to_string(&info).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct SystemInfo {
    os: String,
    arch: String,
    family: String,
    current_dir: String,
    temp_dir: String,
    framework_version: String,
}
Enter fullscreen mode Exit fullscreen mode

The Advantages of Single Binary Deployment

This framework compiles into a single executable file, eliminating the need for complex dependency installation. This feature saves me a lot of trouble during deployment.

use hyperlane::*;
use std::process::Command;

// Cross-platform process management
async fn process_management(ctx: Context) {
    let command = if cfg!(target_os = "windows") {
        "tasklist"
    } else {
        "ps"
    };

    let args = if cfg!(target_os = "windows") {
        vec!["/FO", "CSV"]
    } else {
        vec!["aux"]
    };

    let output = Command::new(command)
        .args(&args)
        .output()
        .unwrap();

    let process_list = String::from_utf8_lossy(&output.stdout);

    let response = ProcessResponse {
        command: command.to_string(),
        output: process_list.to_string(),
        success: output.status.success(),
    };

    let response_json = serde_json::to_string(&response).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct ProcessResponse {
    command: String,
    output: String,
    success: bool,
}

// Cross-platform file system operations
async fn file_operations(ctx: Context) {
    let base_path = if cfg!(target_os = "windows") {
        "C:\\data"
    } else {
        "/data"
    };

    // Create directory structure
    let dirs = vec![
        format!("{}\\logs", base_path),
        format!("{}\\config", base_path),
        format!("{}\\cache", base_path),
    ];

    for dir in &dirs {
        let normalized_dir = if cfg!(target_os = "windows") {
            dir.replace("/", "\\")
        } else {
            dir.replace("\\", "/")
        };

        if !Path::new(&normalized_dir).exists() {
            std::fs::create_dir_all(&normalized_dir).unwrap();
        }
    }

    // Write configuration file
    let config_content = r#"
{
    "server": {
        "host": "0.0.0.0",
        "port": 8080
    },
    "database": {
        "url": "sqlite:///data.db"
    }
}
"#;

    let config_path = if cfg!(target_os = "windows") {
        format!("{}\\config\\app.json", base_path)
    } else {
        format!("{}/config/app.json", base_path)
    };

    std::fs::write(&config_path, config_content).unwrap();

    let response = FileResponse {
        message: "File operations completed".to_string(),
        config_path: config_path,
        directories_created: dirs.len(),
    };

    let response_json = serde_json::to_string(&response).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct FileResponse {
    message: String,
    config_path: String,
    directories_created: usize,
}
Enter fullscreen mode Exit fullscreen mode

Intelligent Environment Adaptation

This framework can automatically adapt to different runtime environments, eliminating the need for me to write platform-specific code.

use hyperlane::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

// Intelligent network configuration
async fn network_config(ctx: Context) {
    let host = if cfg!(target_os = "windows") {
        "127.0.0.1"
    } else {
        "0.0.0.0"
    };

    let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string());
    let workers = env::var("WORKERS").unwrap_or_else(|_| "4".to_string());

    let config = NetworkConfig {
        host: host.to_string(),
        port: port.parse().unwrap(),
        workers: workers.parse().unwrap(),
        ipv6_support: cfg!(target_os = "linux"),
        tcp_nodelay: true,
        keep_alive: true,
    };

    let response_json = serde_json::to_string(&config).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct NetworkConfig {
    host: String,
    port: u16,
    workers: u32,
    ipv6_support: bool,
    tcp_nodelay: bool,
    keep_alive: bool,
}

// Cross-platform logging system
async fn logging_system(ctx: Context) {
    let log_dir = if cfg!(target_os = "windows") {
        "C:\\logs"
    } else {
        "/var/log"
    };

    let log_file = if cfg!(target_os = "windows") {
        format!("{}\\app.log", log_dir)
    } else {
        format!("{}/app.log", log_dir)
    };

    // Ensure log directory exists
    if !Path::new(log_dir).exists() {
        std::fs::create_dir_all(log_dir).unwrap();
    }

    let log_entry = format!(
        "[{}] {} - {} - {}\n",
        chrono::Utc::now().format("%Y-%m-%d %H:%M:%S"),
        "INFO",
        "Logging system initialized",
        env::consts::OS
    );

    std::fs::OpenOptions::new()
        .create(true)
        .append(true)
        .open(&log_file)
        .unwrap()
        .write_all(log_entry.as_bytes())
        .unwrap();

    let response = LogResponse {
        log_file: log_file,
        message: "Log entry written successfully".to_string(),
        platform: env::consts::OS.to_string(),
    };

    let response_json = serde_json::to_string(&response).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct LogResponse {
    log_file: String,
    message: String,
    platform: String,
}
Enter fullscreen mode Exit fullscreen mode

The Convenience of Containerized Deployment

The single binary nature of this framework makes containerized deployment very simple. I only need a minimal base image to run the application.

use hyperlane::*;

// Health check endpoint
async fn health_check(ctx: Context) {
    let health = HealthStatus {
        status: "healthy".to_string(),
        timestamp: chrono::Utc::now(),
        uptime: std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_secs(),
        platform: env::consts::OS.to_string(),
        architecture: env::consts::ARCH.to_string(),
        version: env!("CARGO_PKG_VERSION").to_string(),
    };

    let response_json = serde_json::to_string(&health).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct HealthStatus {
    status: String,
    timestamp: chrono::DateTime<chrono::Utc>,
    uptime: u64,
    platform: String,
    architecture: String,
    version: String,
}

// Configuration management
async fn configuration_management(ctx: Context) {
    let config = AppConfig {
        environment: env::var("ENVIRONMENT").unwrap_or_else(|_| "development".to_string()),
        debug: env::var("DEBUG").unwrap_or_else(|_| "false".to_string()).parse().unwrap(),
        database_url: env::var("DATABASE_URL").unwrap_or_else(|_| "sqlite:///app.db".to_string()),
        redis_url: env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string()),
        cors_origin: env::var("CORS_ORIGIN").unwrap_or_else(|_| "*".to_string()),
    };

    let response_json = serde_json::to_string(&config).unwrap();
    ctx.set_response_status_code(200).await;
    ctx.set_response_header(CONTENT_TYPE, APPLICATION_JSON).await;
    ctx.set_response_body(response_json).await;
}

#[derive(Serialize)]
struct AppConfig {
    environment: String,
    debug: bool,
    database_url: String,
    redis_url: String,
    cors_origin: String,
}

#[tokio::main]
async fn main() {
    let server: Server = Server::new();

    // Cross-platform server configuration
    let host = env::var("HOST").unwrap_or_else(|_| {
        if cfg!(target_os = "windows") {
            "127.0.0.1".to_string()
        } else {
            "0.0.0.0".to_string()
        }
    });

    let port = env::var("PORT").unwrap_or_else(|_| "8080".to_string());

    server.host(&host).await;
    server.port(port.parse().unwrap()).await;

    // Register routes
    server.route("/health", health_check).await;
    server.route("/config", configuration_management).await;
    server.route("/upload", handle_file_upload).await;
    server.route("/system", system_info).await;
    server.route("/processes", process_management).await;
    server.route("/files", file_operations).await;
    server.route("/network", network_config).await;
    server.route("/logs", logging_system).await;

    println!("Server starting on {}:{}", host, port);
    println!("Platform: {} {}", env::consts::OS, env::consts::ARCH);

    server.run().await.unwrap();
}
Enter fullscreen mode Exit fullscreen mode

Comparison with Node.js Cross-Platform Deployment

I once developed cross-platform applications using Node.js, and the deployment process felt complex:

// Node.js cross-platform deployment
const express = require('express');
const path = require('path');
const os = require('os');
const fs = require('fs');

const app = express();

// Need to handle different platform path separators
const uploadDir = path.join(os.tmpdir(), 'uploads');

// Need to ensure directory exists
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

app.get('/system-info', (req, res) => {
  res.json({
    platform: os.platform(),
    arch: os.arch(),
    hostname: os.hostname(),
    tempDir: os.tmpdir(),
    nodeVersion: process.version,
  });
});

// Need to install dependencies
// npm install
// Need to configure package.json
// Need to handle node_modules
// Need to transfer entire project directory during deployment

app.listen(process.env.PORT || 3000, () => {
  console.log('Server running');
});
Enter fullscreen mode Exit fullscreen mode

Using this Rust framework, cross-platform deployment becomes very simple:

# Compile executable files for different platforms
cargo build --release --target x86_64-unknown-linux-gnu
cargo build --release --target x86_64-pc-windows-msvc
cargo build --release --target x86_64-apple-darwin
cargo build --release --target aarch64-unknown-linux-gnu

# Deployment only requires transferring a single file
scp target/x86_64-unknown-linux-gnu/release/myapp user@server:/app/
chmod +x /app/myapp
./myapp
Enter fullscreen mode Exit fullscreen mode

Simplified Docker Deployment

The single binary nature of this framework makes Docker images very small:

# Multi-stage build
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release

# Runtime image
FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/myapp /usr/local/bin/
EXPOSE 8080
CMD ["myapp"]
Enter fullscreen mode Exit fullscreen mode

The final image size is only tens of MB, while Node.js applications typically require hundreds of MB.

Advantages in Cloud-Native Deployment

The cross-platform features of this framework give me huge advantages in cloud-native deployment:

# Kubernetes deployment configuration
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:latest
          ports:
            - containerPort: 8080
          env:
            - name: ENVIRONMENT
              value: 'production'
            - name: PORT
              value: '8080'
          resources:
            requests:
              memory: '64Mi'
              cpu: '50m'
            limits:
              memory: '128Mi'
              cpu: '100m'
Enter fullscreen mode Exit fullscreen mode

Thoughts on the Future

As a computer science student about to graduate, this cross-platform development experience gave me a deeper understanding of modern software deployment. Cross-platform compatibility is not just a technical issue, but an engineering efficiency problem.

This Rust framework shows me the future direction of modern web development: simple deployment, efficient operations, low-cost maintenance. It's not just a framework, but the perfect embodiment of DevOps philosophy.

I believe that with the proliferation of cloud-native technologies, cross-platform compatibility will become a core competitive advantage of web frameworks, and this framework provides developers with the perfect technical foundation.


This article documents my journey as a third-year student exploring cross-platform features of web frameworks. Through actual deployment experience and comparative analysis, I deeply understood the importance of cross-platform compatibility in modern software development. I hope my experience can provide some reference for other students.

For more information, please visit Hyperlane GitHub page or contact the author: [email protected]

Top comments (0)