diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml
index db94fa1..46a2f6a 100644
--- a/.github/workflows/dev.yml
+++ b/.github/workflows/dev.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v3
- name: Build
run: cargo build --release --verbose
- name: Run tests
diff --git a/.github/workflows/pr_main.yml b/.github/workflows/pr_main.yml
index 8ac36d2..db4e60d 100644
--- a/.github/workflows/pr_main.yml
+++ b/.github/workflows/pr_main.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v3
- name: Build
run: cargo build --release --verbose
- name: Run tests
@@ -25,7 +25,7 @@ jobs:
runs-on: windows-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v3
- name: Build
run: cargo build --release --verbose
- name: Run tests
@@ -37,7 +37,7 @@ jobs:
runs-on: macos-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v3
- name: Build
run: cargo build --release --verbose
- name: Run tests
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index e2611ed..58c0fab 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -2,95 +2,99 @@ name: Rust -> Build & Test & Release
on:
push:
- branches: ["main"]
+ branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
- PROJECT_NAME: ${{ github.event.repository.name }}
jobs:
build_linux:
+
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v3
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload Linux Binary
- uses: actions/upload-artifact@v4
- with:
- name: ${{ env.PROJECT_NAME }}-linux-x64_86
- path: target/release/${{ env.PROJECT_NAME }}
+ - name: Upload Linux Binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: rust_tcp_file_transfer_linux_x64_86
+ path: target/release/*transfer
build_windows:
+
runs-on: windows-latest
steps:
- - uses: actions/checkout@v4
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v3
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload Windows Binary
- uses: actions/upload-artifact@v4
- with:
- name: ${{ env.PROJECT_NAME }}-windows-x64_86
- path: target/release/${{ env.PROJECT_NAME }}.exe
+ - name: Upload Windows Binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: rust_tcp_file_transfer_windows_x64_86
+ path: target/release/*transfer.exe
build_macos:
+
runs-on: macos-latest
steps:
- - uses: actions/checkout@v4
- - name: Build
- run: cargo build --release --verbose
- - name: Run tests
- run: cargo test --verbose
+ - uses: actions/checkout@v3
+ - name: Build
+ run: cargo build --release --verbose
+ - name: Run tests
+ run: cargo test --verbose
- - name: Upload MacOS Binary
- uses: actions/upload-artifact@v4
- with:
- name: ${{ env.PROJECT_NAME }}-macos-arm64
- path: target/release/${{ env.PROJECT_NAME }}
+ - name: Upload MacOS Binary
+ uses: actions/upload-artifact@v3
+ with:
+ name: rust_tcp_file_transfer_macos_x64_86
+ path: target/release/*transfer
release:
needs: [build_linux, build_windows, build_macos]
runs-on: ubuntu-latest
- permissions:
- contents: write
+ permissions:
+ contents: write
steps:
- - uses: actions/checkout@v4
- - name: Forge a Folder
- run: mkdir Downloads
- working-directory: /home/runner/work/${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}/
- - uses: actions/download-artifact@v4
- name: Download
- with:
- path: Downloads/
+ - uses: actions/checkout@v3
+ - name: Forge a Folder
+ run: mkdir Downloads
+ working-directory: /home/runner/work/rust-tcp-file-transfer/rust-tcp-file-transfer/
- - name: Rename Binaries
- run: |
- tree Downloads/
- mv Downloads/${{ env.PROJECT_NAME }}-linux-x64_86/${{ env.PROJECT_NAME }} Downloads/${{ env.PROJECT_NAME }}-linux-x64_86/${{ env.PROJECT_NAME }}-linux-x64_86
- mv Downloads/${{ env.PROJECT_NAME }}-windows-x64_86/${{ env.PROJECT_NAME }}.exe Downloads/${{ env.PROJECT_NAME }}-windows-x64_86/${{ env.PROJECT_NAME }}-windows-x64_86.exe
- mv Downloads/${{ env.PROJECT_NAME }}-macos-arm64/${{ env.PROJECT_NAME }} Downloads/${{ env.PROJECT_NAME }}-macos-arm64/${{ env.PROJECT_NAME }}-macos-arm64
- - name: Git Commit SHA
- id: vars
- run: |
- calculatedSha=$(git rev-parse --short ${{ github.sha }})
- echo "short_sha=$calculatedSha" >> $GITHUB_OUTPUT
+ - uses: actions/download-artifact@v3
+ name: Download
+ with:
+ path: Downloads/
+
+ - name: Rename Binaries
+ run: |
+ mv Downloads/rust_tcp_file_transfer_linux_x64_86/rust-tcp-file-transfer Downloads/rust_tcp_file_transfer_linux_x64_86/rust-tcp-file-transfer-linux_x64_86
+ mv Downloads/rust_tcp_file_transfer_windows_x64_86/rust-tcp-file-transfer.exe Downloads/rust_tcp_file_transfer_windows_x64_86/rust-tcp-file-transfer-windows_x64_86.exe
+ mv Downloads/rust_tcp_file_transfer_macos_x64_86/rust-tcp-file-transfer Downloads/rust_tcp_file_transfer_macos_x64_86/rust-tcp-file-transfer-macos_x64_86
- - uses: softprops/action-gh-release@v2
- name: Release
- with:
- tag_name: ${{ steps.vars.outputs.short_sha }}
- generate_release_notes: true
- files: |
- Downloads/*linux*/${{ env.PROJECT_NAME }}*
- Downloads/*windows*/${{ env.PROJECT_NAME }}*
- Downloads/*macos*/${{ env.PROJECT_NAME }}*
+ - name: Git Commit SHA
+ id: vars
+ run: |
+ calculatedSha=$(git rev-parse --short ${{ github.sha }})
+ echo "short_sha=$calculatedSha" >> $GITHUB_OUTPUT
+
+ - uses: softprops/action-gh-release@v0.1.15
+ name: Release
+ with:
+ tag_name: ${{ steps.vars.outputs.short_sha }}
+ generate_release_notes: true
+ files: |
+ Downloads/*linux*/*transfer*
+ Downloads/*windows*/*transfer*
+ Downloads/*macos*/*transfer*
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 32d71ea..196e176 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
# will have compiled files and executables
debug/
target/
-.vscode/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
diff --git a/Cargo.toml b/Cargo.toml
index f4a5455..3d80046 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,15 +3,6 @@ name = "rust-tcp-file-transfer"
version = "0.1.0"
edition = "2021"
-[profile.release]
-strip = "symbols"
-opt-level = 3
-overflow-checks = true
-lto = true
-codegen-units = 1
-panic = "abort"
-
-[lints.rust]
-unsafe_code = "forbid"
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
diff --git a/README.md b/README.md
index b80f7bd..530b6b4 100644
--- a/README.md
+++ b/README.md
@@ -2,25 +2,16 @@
# rust-tcp-file-transfer
TCP File Transfer Server and Client in Rust
-**Usage**
-> ./rust-tcp-file-transfer -h
->
-
-
-
-**Examples**
-> ./rust-tcp-file-transfer -sv -s -l ~/Desktop/cat.png
->
-> ./rust-tcp-file-transfer -cl -r -l ~/Documents/
-
-
-
-**TO-DO List**
-
-☑ Standard library only.
-
☑ File transfer over network.
☑ Remove memory limitations. [d42412c](https://github.com/Tahinli/rust-tcp-file-transfer/pull/1/commits/d42412c57d7d95672ba64b3e489b95f1c4b04a08)
-☑ Bidirectional transfer. [b0531de](https://github.com/Tahinli/rust-tcp-file-transfer/commit/b0531deb257332f46fc76de16d3a17fb3b28acee)
+☐ Bidirectional transfer.
+
+☐ Folder transfer.
+
+☐ Remember where it stopped.
+
+☐ Reach over NAT (peer to peer).
+
+☐ Async.
diff --git a/assets/example.png b/assets/example.png
deleted file mode 100644
index c7f3952..0000000
Binary files a/assets/example.png and /dev/null differ
diff --git a/assets/help_menu.png b/assets/help_menu.png
deleted file mode 100644
index 311613f..0000000
Binary files a/assets/help_menu.png and /dev/null differ
diff --git a/src/main.rs b/src/main.rs
index 41d3a98..ee5d644 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,729 +1,491 @@
-use std::env::{self};
-use std::fs::{self, File, Metadata};
-use std::io::{BufWriter, Read, Write};
-use std::net::{IpAddr, TcpListener, TcpStream};
-use std::path::{Path, PathBuf};
+use std::fs::{File, Metadata, self};
use std::time::Instant;
+use std::net::{TcpListener, TcpStream};
+use std::io::{Read, Write, self, BufWriter, BufReader, BufRead};
+use std::env::{self};
-const BUFFER_SIZE: u64 = 100000;
-#[derive(Debug)]
-enum FileType {
- Symlink,
- File,
- Folder,
-}
-impl FileType {
- fn what_type(value: u8, file_info: &FileInfo, debug_mode: &bool) -> FileType {
- match value {
- 100 => {
- if *debug_mode {
- println!(
- "Done: Symlink Detected -> {}",
- file_info.location.as_ref().unwrap()
- );
- }
- FileType::Symlink
- }
- 101 => {
- if *debug_mode {
- println!(
- "Done: File Detected -> {}",
- file_info.location.as_ref().unwrap()
- );
- }
- FileType::File
- }
- 102 => {
- if *debug_mode {
- println!(
- "Done: Folder Detected -> {}",
- file_info.location.as_ref().unwrap()
- );
- }
- FileType::Folder
- }
- _ => {
- println!(
- "Error: Undefined Type -> {}",
- file_info.location.as_ref().unwrap()
- );
- panic!()
- }
- }
+const BUFFER_SIZE:u64 = 100000;
+struct FileInfo
+ {
+ file:Option,
+ location:String,
+ size_current:usize,
+ metadata:Option,
}
-}
-#[derive(Debug)]
-struct UserEnvironment {
- ip: IpAddr,
- port: u16,
- server: bool,
- send: bool,
- location: Option,
- debug: bool,
-}
-impl UserEnvironment {
- fn new() -> UserEnvironment {
- UserEnvironment {
- ip: "127.0.0.1".parse().unwrap(),
- port: 2121,
- server: false,
- send: false,
- location: None,
- debug: false,
- }
- }
-}
-#[derive(Debug)]
-struct FileInfo {
- file: Option,
- location: Option,
- sign: Option,
- size_total: u64,
- size_current: u64,
- metadata: Option,
- progress: u8,
-}
-impl FileInfo {
- fn new() -> FileInfo {
- FileInfo {
- file: None,
- location: None,
- sign: None,
- size_total: 0,
- size_current: 0,
- metadata: None,
- progress: 0,
- }
- }
- fn pass_user_environment(&mut self, user_environment: &UserEnvironment) {
- self.location = user_environment.location.clone();
- self.sign = user_environment.location.clone();
- }
- fn reading_operations(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
- match self.location.as_ref() {
- Some(_) => {
- self.read_metadata(debug_mode);
- match self.metadata {
- Some(ref mut metadata) => {
- if Metadata::is_symlink(metadata) {
- //Unix-Windows Problem
- println!("\n\tError: Symlink Transfers've not Supported yet\n");
- //self.open_file(debug_mode);
- //self.send_file(stream, &(100 as u8),debug_mode);
- } else if Metadata::is_file(metadata) {
- self.open_file(debug_mode);
- self.send_file(stream, &(101_u8), debug_mode);
- } else if Metadata::is_dir(metadata) {
- //path recognition and creation on the other side
- //std:path
- println!("\n\tError: Folder Transfers've not Supported yet\n");
- //self.open_file(debug_mode);
- //self.send_file(stream, &(102 as u8),debug_mode);
- } else {
- println!(
- "Error: Undefined Type -> {}",
- self.location.as_ref().unwrap()
- );
- }
+impl FileInfo
+ {
+ fn reading_operations(&mut self, stream:&mut TcpStream, debug_mode:&bool)
+ {
+ self.read_metadata();
+ match self.metadata
+ {
+ Some(ref mut metadata) =>
+ {
+ if Metadata::is_file(metadata)
+ {
+ self.open_file();
+ self.send_file(stream, debug_mode);
+ }
+ else if Metadata::is_symlink(metadata)
+ {
+ self.open_file();
+ self.send_file(stream, debug_mode);
+ }
+ else
+ {
+ //path recognition and creation on the other side
+ //std:path
+ panic!("\n\tError: Folder Transfers've not Supported yet\n")
+ }
+ }
+ None =>
+ {
+ println!("Error: Read Metadata -> {}", self.location);
+ }
}
- None => {
- println!(
- "Error: Read Metadata -> {}",
- self.location.as_ref().unwrap()
- );
+ }
+ fn writing_operations(&mut self, stream:&mut TcpStream, debug_mode:&bool)
+ {
+ self.forge_file();
+ self.write_file(stream, debug_mode);
+ }
+ fn read_metadata(&mut self)
+ {
+ self.metadata = Some(fs::metadata(&self.location).expect("Error: Read Metadata"));
+ }
+ fn open_file(&mut self)
+ {
+ match File::open(&self.location)
+ {
+ Ok(file) =>
+ {
+ self.file = Some(file);
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Open File -> {} | Error: {}", self.location, err_val);
+ return;
+ }
}
- }
+
}
- None => {
- println!("Error: Reading Operations -> {:#?}", &self.location);
- panic!();
- }
- }
- }
- fn writing_operations(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
- self.write_file(stream, debug_mode);
- self.cleaning();
- }
- fn cleaning(&mut self) {
- self.location = self.sign.clone();
- self.size_current = 0;
- }
- fn read_metadata(&mut self, debug_mode: &bool) {
- let path = PathBuf::from(self.location.as_ref().unwrap());
- if path.is_symlink() {
- match path.symlink_metadata() {
- Ok(metadata) => {
- self.metadata = Some(metadata);
- }
- Err(err_val) => {
- println!(
- "Error: Symlink Metadata -> {:#?} | Error: {}",
- &self.location, err_val
- );
- }
- }
- } else {
- match fs::metadata(self.location.as_ref().unwrap()) {
- Ok(metadata) => {
- self.metadata = Some(metadata);
- if *debug_mode {
- println!("Done: Read Metadata -> {:#?}", self.metadata);
+ fn send_file(&mut self, stream:&mut TcpStream, debug_mode:&bool)
+ {
+ let size = self.metadata.as_ref().unwrap().len();
+ let mut iteration = (size/BUFFER_SIZE)+1;
+ let total_iteration = iteration;
+ self.handshake_validation(stream, size, debug_mode);
+ println!("Size = {}", size);
+ println!("Iteration = {}", iteration);
+ while iteration != 0
+ {
+ iteration -= 1;
+ let mut buffer = [0u8;BUFFER_SIZE as usize];
+ if iteration != 0
+ {
+ self.read_exact(&mut buffer, debug_mode);
+ }
+ else
+ {
+ self.read_exact(&mut buffer[..(size%BUFFER_SIZE) as usize], debug_mode);
+ }
+ if *debug_mode
+ {
+ println!("Read Data = {:#?}", buffer);
+ }
+ self.send_exact(&mut buffer, stream, debug_mode);
+ println!("%{}", 100 as f64 -((iteration as f64/total_iteration as f64)*100 as f64));
}
- }
- Err(err_val) => {
- println!(
- "Error: Read Metadata -> {} | Error: {}",
- &self.location.as_ref().unwrap(),
- err_val
- );
- }
}
- }
+ fn handshake_validation(&mut self, stream:&mut TcpStream, size:u64, debug_mode:&bool)
+ {
+ self.send_exact(String::from(size.to_string()+"\n").as_bytes(), stream, debug_mode);
+ match self.recv_until(stream, '\n', debug_mode)
+ {
+ Some(handshake_callback) =>
+ {
+ if handshake_callback == size.to_string().as_bytes().to_vec()
+ {
+ println!("Done: Handshake -> {}", self.location);
+ if *debug_mode
+ {
+ println!("{:#?} ", handshake_callback);
+ }
+ }
+ else
+ {
+ println!("Error: Handshake -> {}", self.location);
+ println!("{:#?} ", handshake_callback);
+ panic!()
+ }
+ }
+ None =>
+ {
+ panic!()
+ }
+ }
+ }
+ fn read_exact(&mut self, buffer:&mut [u8], debug_mode:&bool)
+ {
+ match self.file.as_ref().unwrap().read_exact(buffer)
+ {
+ Ok(_) =>
+ {
+ if *debug_mode
+ {
+ println!("Done: Read Bytes -> {}", self.location);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Read Bytes -> {} | Error: {}", self.location, err_val);
+ panic!()
+ }
+ }
+ }
+ fn send_exact(&mut self, buffer:&[u8], stream:&mut TcpStream, debug_mode:&bool)
+ {
+ let mut stream_writer = BufWriter::new(stream.try_clone().unwrap());
+ match stream_writer.write_all(buffer)
+ {
+ Ok(_) =>
+ {
+ self.size_current += buffer.len();
+ if *debug_mode
+ {
+ println!("Done: Send Bytes -> {}", self.location);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Send Bytes -> {} | Error: {}", self.location, err_val);
+ panic!();
+ }
+ }
+ match stream_writer.flush()
+ {
+ Ok(_) =>
+ {
+ if *debug_mode
+ {
+ println!("Done: Flush -> {}", self.location);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Flush -> {} | Error: {}", self.location, err_val);
+ panic!()
+ }
+ }
+ }
+ fn recv_exact(&mut self, buffer:&mut [u8], stream:&mut TcpStream, debug_mode:&bool)
+ {
+ let mut stream_reader = BufReader::new(stream.try_clone().unwrap());
+ match stream_reader.read_exact(buffer)
+ {
+ Ok(_) =>
+ {
+ self.size_current += buffer.len();
+ if *debug_mode
+ {
+ println!("Done: Receive Bytes -> {}", self.location);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Receive Bytes -> {} | Error: {}", self.location, err_val);
+ panic!();
+ }
+ }
+ }
+ fn recv_until(&mut self, stream:&mut TcpStream, until:char, debug_mode:&bool) -> Option>
+ {
+ let mut buffer = Vec::new();
+ let mut stream_reader = BufReader::new(stream.try_clone().unwrap());
+ match stream_reader.read_until(until as u8,&mut buffer)
+ {
+ Ok(_) =>
+ {
+ if *debug_mode
+ {
+ println!("Done: Receive Until -> {}", self.location);
+ println!("{:#?}", buffer);
+ }
+ buffer.pop();
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Receive Until -> {} | Error: {}", self.location, err_val);
+ return None;
+ }
+ }
+ return Some(buffer);
+ }
+ fn forge_file(&mut self)
+ {
+ self.file = Some(File::create(&self.location).expect("Error: Create File"));
+ }
+ fn handshake_recv(&mut self, stream:&mut TcpStream, debug_mode:&bool) -> u64
+ {
+ match self.recv_until(stream, '\n', debug_mode)
+ {
+ Some(mut handshake) =>
+ {
+ println!("Done: Handshake -> {}", self.location);
+ if *debug_mode
+ {
+ println!("{:#?} ", handshake);
+ }
+ let size = String::from_utf8(handshake.clone()).unwrap().parse().unwrap();
+ handshake.push(b'\n');
+ self.send_exact(&handshake.as_slice(), stream, debug_mode);
+ size
+ }
+ None =>
+ {
+ println!("Error: Handshake -> {}", self.location);
+ 0
+ }
+ }
+ }
+ fn save_exact(&mut self, buffer:&[u8], debug_mode:&bool)
+ {
+ let mut file_writer = BufWriter::new(self.file.as_ref().unwrap());
+ match file_writer.write_all(buffer)
+ {
+ Ok(_) =>
+ {
+ if *debug_mode
+ {
+ println!("Done: Write -> {} | {} bytes", self.location, self.size_current);
+ println!("{:#?}", buffer);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Write -> {} | Error: {}", self.location,err_val);
+ panic!();
+ }
+ }
+ match file_writer.flush()
+ {
+ Ok(_) =>
+ {
+ if *debug_mode
+ {
+ println!("Done: Flush -> {}", self.location);
+ }
+ }
+ Err(err_val) =>
+ {
+ println!("Error: Flush -> {} | Error: {}", self.location,err_val);
+ panic!();
+ }
+ }
+ }
+ fn write_file(&mut self, stream:&mut TcpStream, debug_mode:&bool)
+ {
+ let size = self.handshake_recv(stream, debug_mode);
+ let mut iteration:u64 = (size/BUFFER_SIZE)+1;
+ let total_iteration = iteration;
+ println!("Size = {}", size);
+ println!("Iteration = {}", iteration);
+ while iteration != 0
+ {
+ iteration -= 1;
+ let mut buffer = [0u8;BUFFER_SIZE as usize];
+ self.recv_exact(&mut buffer, stream, debug_mode);
+ if iteration != 0
+ {
+ self.save_exact(&buffer, debug_mode);
+ }
+ else
+ {
+ self.save_exact(&buffer[..(size%BUFFER_SIZE) as usize], debug_mode);
+ }
+ println!("%{}", 100 as f64 -((iteration as f64/total_iteration as f64)*100 as f64));
+ }
+ }
}
- fn open_file(&mut self, debug_mode: &bool) {
- match File::options()
- .read(true)
- .write(true)
- .open(self.location.as_ref().unwrap())
+enum DebugMode
+ {
+ On,
+ Off
+ }
+impl DebugMode {
+ fn debug_mode(self) -> bool
{
- Ok(file) => {
- self.file = Some(file);
- if *debug_mode {
- println!("Done : Open File -> {:#?}", self.file);
+ match self
+ {
+ DebugMode::On =>
+ {
+ println!("Debug: ON");
+ let debug = true;
+ debug
+ }
+ DebugMode::Off =>
+ {
+ println!("Debug: OFF");
+ let debug = false;
+ debug
+ }
}
- }
- Err(err_val) => {
- println!(
- "Error: Open File -> {} | Error: {}",
- self.location.as_ref().unwrap(),
- err_val
- );
- }
}
+}
+enum Connection
+ {
+ Server(String, String),
+ Client(String, String),
}
- fn send_file(&mut self, stream: &mut TcpStream, what_type: &u8, debug_mode: &bool) {
- self.size_total = self.metadata.as_ref().unwrap().len();
- let mut iteration = (self.size_total / BUFFER_SIZE) + 1;
- let total_iteration = iteration;
- let path_buf = PathBuf::from(Path::new(self.location.as_ref().unwrap()));
- match what_type {
- 100 => {
- if *debug_mode {
- println!(
- "Done: Symlink Detected -> {}",
- self.location.as_ref().unwrap()
- );
- }
- self.callback_validation(stream, &String::from("100"), debug_mode);
- }
- 101 => {
- if *debug_mode {
- println!("Done: File Detected -> {}", self.location.as_ref().unwrap());
- }
- self.callback_validation(stream, &String::from("101"), debug_mode)
- }
- 102 => {
- if *debug_mode {
- println!(
- "Done: Folder Detected -> {}",
- self.location.as_ref().unwrap()
- );
- }
- self.callback_validation(stream, &String::from("102"), debug_mode)
- }
- _ => {
- println!(
- "Error: Undefined Type Detected ->{}",
- self.location.as_ref().unwrap()
- );
- return;
- }
- }
- self.callback_validation(stream, &(self.size_total.to_string()), debug_mode);
- self.callback_validation(
- stream,
- &path_buf.file_name().unwrap().to_str().unwrap().to_string(),
- debug_mode,
- );
- self.show_info(&iteration, debug_mode);
- while iteration != 0 {
- iteration -= 1;
- let mut buffer = [0u8; BUFFER_SIZE as usize];
- if iteration != 0 {
- self.read_exact(&mut buffer, debug_mode);
- } else {
- self.read_exact(
- &mut buffer[..(self.size_total % BUFFER_SIZE) as usize],
- debug_mode,
- );
+impl Connection
+ {
+ fn server(self, file_info:&mut FileInfo, debug_mode:bool)
+ {
+ print!("Server -> ");
+ let ip:String;
+ let port:String;
+ let address:String;
+ match self
+ {
+ Connection::Server(in1, in2) =>
+ {
+ ip = in1.trim_end().to_string();
+ port = in2.trim_end().to_string();
+ address = format!("{}:{}", ip, port);
+ println!("{}", address);
+ }
+ _ => return
+ }
+ let socket = TcpListener::bind(address);
+ for stream in socket.expect("Error: Can't Check Connections").incoming()
+ {
+ match stream
+ {
+ Ok(mut stream) =>
+ {
+ let mut stay = true;
+ while stay
+ {
+ println!("Connected");
+ let start_time = Instant::now();
+ FileInfo::writing_operations(file_info, &mut stream, &debug_mode);
+ let finish_time = Instant::now();
+ println!("Passed: Total -> {:#?}", finish_time.duration_since(start_time));
+ stay = false;
+ }
+ }
+ Err(e) =>
+ {
+ println!("Error: Can't Visit Stream -> {}", e);
+ return;
+ }
+ }
+ }
}
- if *debug_mode {
- println!("Read Data = {:#?}", buffer);
+ fn client(self, file_info:&mut FileInfo, debug_mode:bool)
+ {
+ print!("Client -> ");
+ let ip:String;
+ let port:String;
+ let address:String;
+ match self
+ {
+ Connection::Client(in1, in2) =>
+ {
+ ip = in1.trim_end().to_string();
+ port = in2.trim_end().to_string();
+ address = format!("{}:{}", ip, port);
+ println!("{}", address);
+ }
+ _ => return
+ }
+ match TcpStream::connect(address)
+ {
+ Ok(mut stream) =>
+ {
+ println!("Connected");
+ let start_time = Instant::now();
+ FileInfo::reading_operations(file_info, &mut stream, &debug_mode);
+ let finish_time = Instant::now();
+ println!("Passed: Total -> {:#?}", finish_time.duration_since(start_time));
+ }
+ Err(e) =>
+ {
+ println!("Error: Connection -> {}", e);
+ }
+ }
+
}
- self.send_exact(&buffer, stream, debug_mode);
- self.show_progress(iteration, total_iteration);
- }
}
- fn callback_validation(&mut self, stream: &mut TcpStream, data: &String, debug_mode: &bool) {
- let mut data_vec: Vec = data.as_bytes().to_vec();
- let mut terminator_vec: Vec = vec![b'\n'; BUFFER_SIZE as usize - data_vec.len()];
- data_vec.append(&mut terminator_vec);
- drop(terminator_vec);
- let mut data_exact: [u8; BUFFER_SIZE as usize] = [b'\n'; BUFFER_SIZE as usize];
- data_exact.swap_with_slice(data_vec[..].as_mut());
- drop(data_vec);
- self.send_exact(&data_exact, stream, debug_mode);
- let mut data_exact_check: [u8; BUFFER_SIZE as usize] = [b'\n'; BUFFER_SIZE as usize];
- self.recv_exact(&mut data_exact_check, stream, debug_mode);
- if data_exact_check == data_exact {
- if *debug_mode {
- println!("Done: Callback -> {}", self.location.as_ref().unwrap());
- println!("{:#?} ", data_exact_check);
- }
- } else {
- println!("Error: Callback -> {}", self.location.as_ref().unwrap());
- println!("{:#?} ", data_exact_check);
- panic!()
- }
- }
- fn read_exact(&mut self, buffer: &mut [u8], debug_mode: &bool) {
- match self.file.as_ref().unwrap().read_exact(buffer) {
- Ok(_) => {
- if *debug_mode {
- println!("Done: Read Bytes -> {}", self.location.as_ref().unwrap());
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) => {
- println!(
- "Error: Read Bytes -> {} | Error: {}",
- self.location.as_ref().unwrap(),
- err_val
- );
- panic!()
- }
- }
- }
- fn send_exact(&mut self, buffer: &[u8], stream: &mut TcpStream, debug_mode: &bool) {
- let mut stream_writer = BufWriter::new(stream.try_clone().unwrap());
- match stream_writer.write_all(buffer) {
- Ok(_) => {
- self.size_current += buffer.len() as u64;
- if *debug_mode {
- println!("Done: Send Bytes -> {:#?}", self.location);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) => {
- println!(
- "Error: Send Bytes -> {:#?} | Error: {}",
- self.location, err_val
- );
- panic!();
- }
- }
- match stream_writer.flush() {
- Ok(_) => {
- if *debug_mode {
- println!("Done: Flush -> {:#?}", self.location);
- }
- }
- Err(err_val) => {
- println!("Error: Flush -> {:#?} | Error: {}", self.location, err_val);
- panic!()
- }
- }
- }
- fn recv_exact(&mut self, buffer: &mut [u8], stream: &mut TcpStream, debug_mode: &bool) {
- match stream.read_exact(buffer) {
- Ok(_) => {
- self.size_current += buffer.len() as u64;
- if *debug_mode {
- println!("Done: Receive Bytes -> {:#?}", self.location);
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) => {
- println!(
- "Error: Receive Bytes -> {:#?} | Error: {}",
- self.location, err_val
- );
- panic!();
- }
- }
- }
- fn forge_file(&mut self, location: String, debug_mode: &bool) {
- //dont forget
- //directory recognition required for received location
- match self.location.as_ref() {
- Some(self_location) => {
- let mut path = PathBuf::from(&self_location);
- path.push(location);
- self.forge_folder(self_location.clone(), debug_mode);
- self.location = Some(path.to_str().unwrap().to_string());
- }
- None => {
- self.location = Some(location);
- }
- }
- match File::create(self.location.as_ref().unwrap()) {
- Ok(file) => {
- if *debug_mode {
- println!("Done Forge File -> {:#?}", file);
- }
- }
- Err(err_val) => {
- println!(
- "Error: Forge File -> {:#?} | Error: {}",
- self.location.as_ref(),
- err_val
- );
- }
- }
- }
- fn forge_folder(&mut self, location: String, debug_mode: &bool) {
- match fs::create_dir_all(&location) {
- Ok(_) => {
- if *debug_mode {
- println!("Done: Forge Folder -> {}", &location);
- }
- }
- Err(err_val) => {
- println!("Error: Forge Folder -> {} | Error: {}", location, err_val);
- }
- }
- }
- fn callback_recv(&mut self, stream: &mut TcpStream, debug_mode: &bool) -> String {
- let mut buffer: [u8; BUFFER_SIZE as usize] = [0; BUFFER_SIZE as usize];
- self.recv_exact(&mut buffer, stream, debug_mode);
- if *debug_mode {
- println!("Done: Callback -> {:#?}", self.location);
- println!("{:#?} ", buffer);
- }
- let data = String::from_utf8(
- buffer
- .split(|element| *element == b'\n')
- .next()
- .unwrap()
- .to_vec(),
- )
- .unwrap();
- if *debug_mode {
- println!("Done: Split -> {:#?}", self.location);
- println!("{:#?}", data);
- }
- self.send_exact(&buffer, stream, debug_mode);
- data
- }
- fn save_exact(&mut self, buffer: &[u8], debug_mode: &bool) {
- let mut file_writer = BufWriter::new(self.file.as_ref().unwrap());
- if *debug_mode {
- println!("{:#?}", file_writer);
- }
- match file_writer.write_all(buffer) {
- Ok(_) => {
- if *debug_mode {
- println!(
- "Done: Write -> {} | {} bytes",
- self.location.as_ref().unwrap(),
- self.size_current
- );
- println!("{:#?}", buffer);
- }
- }
- Err(err_val) => {
- println!(
- "Error: Write -> {} | Error: {}",
- self.location.as_ref().unwrap(),
- err_val
- );
- panic!();
- }
- }
- match file_writer.flush() {
- Ok(_) => {
- if *debug_mode {
- println!("Done: Flush -> {}", self.location.as_ref().unwrap());
- }
- }
- Err(err_val) => {
- println!(
- "Error: Flush -> {} | Error: {}",
- self.location.as_ref().unwrap(),
- err_val
- );
- panic!();
- }
- }
- }
- fn write_file(&mut self, stream: &mut TcpStream, debug_mode: &bool) {
- let what_type: FileType = FileType::what_type(
- self.callback_recv(stream, debug_mode)
- .parse::()
- .unwrap(),
- self,
- debug_mode,
- );
- self.size_total = self.callback_recv(stream, debug_mode).parse().unwrap();
- let location: String = self.callback_recv(stream, debug_mode);
- self.file_forger(what_type, location, debug_mode);
- self.open_file(debug_mode);
- let mut iteration: u64 = (self.size_total / BUFFER_SIZE) + 1;
- let total_iteration = iteration;
- self.show_info(&iteration, debug_mode);
- while iteration != 0 {
- iteration -= 1;
- let mut buffer = [0u8; BUFFER_SIZE as usize];
- self.recv_exact(&mut buffer, stream, debug_mode);
- if iteration != 0 {
- self.save_exact(&buffer, debug_mode);
- } else {
- self.save_exact(
- &buffer[..(self.size_total % BUFFER_SIZE) as usize],
- debug_mode,
- );
- }
- self.show_progress(iteration, total_iteration);
- }
- }
- fn file_forger(&mut self, file_type: FileType, location: String, debug_mode: &bool) {
- match file_type {
- FileType::Symlink => {
- self.forge_file(location, debug_mode);
- }
- FileType::File => {
- self.forge_file(location, debug_mode);
- }
- FileType::Folder => {
- self.forge_file(location, debug_mode);
- }
- }
- }
- fn show_info(&mut self, iteration: &u64, debug_mode: &bool) {
- println!("File = {}", self.location.as_ref().unwrap());
- println!("Size = {}", self.size_total);
- if *debug_mode {
- println!("Iteration = {}", iteration);
- }
- }
- fn show_progress(&mut self, iteration: u64, total_iteration: u64) {
- if iteration % 10 == 0 {
- let progress: u8 =
- 100_u8 - ((iteration as f64 / total_iteration as f64) * 100_f64) as u8;
- if progress != self.progress {
- self.progress = progress;
- println!("%{}", self.progress);
- }
- }
- }
-}
-enum Connection {
- Server(String, String),
- Client(String, String),
-}
-impl Connection {
- fn server(self, file_info: &mut FileInfo, user_environment: &UserEnvironment) {
- print!("Server -> ");
- if user_environment.debug {
- println!("{:#?}", user_environment);
- println!("{:#?}", file_info);
- }
- let ip: String;
- let port: String;
- let address: String;
- match self {
- Connection::Server(in1, in2) => {
- ip = in1.trim_end().to_string();
- port = in2.trim_end().to_string();
- address = format!("{}:{}", ip, port);
- println!("{}", address);
- }
- _ => return,
- }
- let socket = TcpListener::bind(address);
- for stream in socket.expect("Error: Can't Check Connections").incoming() {
- match stream {
- Ok(mut stream) => {
- println!("Connected");
- send_or_receive(
- file_info,
- &mut stream,
- &user_environment.debug,
- user_environment,
- );
- }
- Err(e) => {
- println!("Error: Can't Visit Stream -> {}", e);
- return;
- }
- }
- }
+fn take_string(output:String) -> String
+ {
+ let mut input = String::new();
+ println!("{}", output);
+ io::stdin().read_line(&mut input).expect("Error: Failed to Read from Console");
+ input
}
- fn client(self, file_info: &mut FileInfo, user_environment: &UserEnvironment) {
- print!("Client -> ");
- if user_environment.debug {
- println!("{:#?}", user_environment);
- println!("{:#?}", file_info);
- }
- let ip: String;
- let port: String;
- let address: String;
- match self {
- Connection::Client(in1, in2) => {
- ip = in1.trim_end().to_string();
- port = in2.trim_end().to_string();
- address = format!("{}:{}", ip, port);
- println!("{}", address);
- }
- _ => return,
- }
- match TcpStream::connect(address) {
- Ok(mut stream) => {
- println!("Connected");
- send_or_receive(
- file_info,
- &mut stream,
- &user_environment.debug,
- user_environment,
- );
- }
- Err(e) => {
- println!("Error: Connection -> {}", e);
- }
- }
+fn take_arg() -> String
+ {
+ env::args().last().as_deref().unwrap_or("default").to_string()
}
-}
-fn send_or_receive(
- file_info: &mut FileInfo,
- stream: &mut TcpStream,
- debug_mode: &bool,
- user_environment: &UserEnvironment,
-) {
- match user_environment.send {
- true => {
- let start_time = Instant::now();
- FileInfo::reading_operations(file_info, stream, debug_mode);
- let finish_time = Instant::now();
- println!("Done: Transfer");
- println!(
- "Passed: Total -> {:#?}",
- finish_time.duration_since(start_time)
- );
- }
- false => {
- let start_time = Instant::now();
- FileInfo::writing_operations(file_info, stream, debug_mode);
- let finish_time = Instant::now();
- println!("Done: Transfer");
- println!(
- "Passed: Total -> {:#?}",
- finish_time.duration_since(start_time)
- );
- }
+fn debug_mod() -> DebugMode
+ {
+ match &take_string("Input: Debug -> On '1', Debug -> Off '0'".to_string())[0..1]
+ {
+ "1" =>
+ {
+ DebugMode::On
+ }
+ "0" =>
+ {
+ DebugMode::Off
+ }
+ input =>
+ {
+ println!("Error: Give Valid Input, You Gave : {}", input);
+ panic!()
+ }
+ }
}
-}
-fn take_args(mut user_environment: UserEnvironment) -> Option {
- let env_args: Vec = env::args().collect();
- if env_args.len() > 16 {
- println!(
- "Error: Too Many Arguments, You Gave {} Arguments",
- env_args.len()
- );
- return None;
+fn main()
+ {
+ //DONT FORGET
+ //First we should check folder structure and validation then make connection.
+ println!("Hello, world!");
+
+ let mut data = FileInfo
+ {
+ file:None,
+ location:take_arg(),
+ size_current:0 as usize,
+ metadata:None,
+ };
+ match &take_string("Input: Server 's', Client 'c'".to_string())[0..1]
+ {
+ "s" =>
+ {
+ Connection::server
+ (Connection::Server(take_string("Input: Server Stream IP Address".to_string()),
+ take_string("Input: Server Stream Port Address".to_string())),
+ &mut data, DebugMode::debug_mode(debug_mod()));
+ },
+ "c" =>
+ {
+ Connection::client
+ (Connection::Client(take_string("Input: Server IP Address to Connect".to_string()),
+ take_string("Input: Server Port Address to Connect".to_string())),
+ &mut data, DebugMode::debug_mode(debug_mod()));
+ }
+ input =>
+ {
+ println!("Error: Give Valid Input, You Gave : {}", input);
+ return;
+ }
+ }
}
- let mut i = 1;
- while i < env_args.len() {
- match env_args[i].as_str() {
- "--ip" | "-i" => {
- user_environment.ip = env_args[i + 1].parse().unwrap();
- i += 1;
- }
- "--port" | "-p" => {
- user_environment.port = env_args[i + 1].parse().unwrap();
- i += 1;
- }
- "--location" | "-l" => {
- user_environment.location = Some(env_args[i + 1].parse().unwrap());
- i += 1;
- }
- "--server" | "-sv" => {
- user_environment.server = true;
- }
- "--client" | "-cl" => {
- user_environment.server = false;
- }
- "--send" | "-s" => {
- user_environment.send = true;
- }
- "--receive" | "-r" => {
- user_environment.send = false;
- }
- "--debug" | "-d" => {
- user_environment.debug = true;
- }
- "--help" | "-h" => {
- show_help();
- return None;
- }
- err => {
- println!("Error: Invalid Argument, You Gave {}", err);
- return None;
- }
- }
- i += 1;
- }
- Some(user_environment)
-}
-fn show_help() {
- println!("\n\n\n");
- println!(" Arguments | Details | Defaults");
- println!("----------------------------------------------------------------------");
- println!(" -i -> --ip | Specifies IP Address | 127.0.0.1");
- println!(" -p -> --port | Specifies Port Address | 2121");
- println!(" -l -> --location | Specifies Location Address | Same as Program");
- println!(" -sv -> --server | Starts as a Server | False");
- println!(" -cl -> --client | Starts as a Client | True");
- println!(" -s -> --send | Starts as a Sender | False");
- println!(" -r -> --receive | Starts as a Receiver | True");
- println!(" -d -> --debug | Starts in Debug Mode | False");
- println!(" -h -> --help | Shows Help | False");
- println!("\n\n\n");
-}
-fn main() {
- //DONT FORGET
- //First we should check folder structure and validation then make connection.
- println!("Hello, world!");
- let mut file_info: FileInfo = FileInfo::new();
- let user_environment: UserEnvironment = match take_args(UserEnvironment::new()) {
- Some(usr_env) => usr_env,
- None => {
- return;
- }
- };
- file_info.pass_user_environment(&user_environment);
- match user_environment.server {
- true => {
- Connection::server(
- Connection::Server(
- user_environment.ip.to_string(),
- user_environment.port.to_string(),
- ),
- &mut file_info,
- &user_environment,
- );
- }
- false => {
- Connection::client(
- Connection::Client(
- user_environment.ip.to_string(),
- user_environment.port.to_string(),
- ),
- &mut file_info,
- &user_environment,
- );
- }
- }
-}