Language Migration #21
+1
-1
@@ -10,8 +10,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
futures = { version = "0.3.30" }
|
||||
reqwest = { version = "0.12.4", features = ["json", "blocking", "multipart", "stream"] }
|
||||
http = { version = "1.1.0" }
|
||||
reqwest = { version = "0.12.4", features = ["json", "blocking", "multipart", "stream"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
serde_json = "1.0.115"
|
||||
tokio = { version = "1.37", features = ["full"] }
|
||||
|
||||
@@ -1,86 +1,67 @@
|
||||
# IcarusDownloadManager
|
||||
|
||||
IcarusDownloadManager is a Linux CLI software client application that has the feature of uploading and downloading songs from the [Icarus](https://github.com/kdeng00/Icarus) Music Server.
|
||||
IcarusDownloadManager is a CLI software client application that has the feature of uploading and downloading songs from the [Icarus](https://github.com/kdeng00/Icarus) Music Server.
|
||||
|
||||
|
||||
## Built With
|
||||
|
||||
* C++ with C++20 features
|
||||
* CMake
|
||||
* GCC >= 10 or Visual Studio >= 17 [2022]
|
||||
* [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* [json](https://github.com/nlohmann/json)
|
||||
* [openssl](https://github.com/openssl/openssl)
|
||||
* [curl](https://github.com/curl/curl)
|
||||
* [cpr](https://github.com/libcpr/cpr)
|
||||
* Rust
|
||||
* Cargo
|
||||
* futures
|
||||
* http
|
||||
* reqwst
|
||||
* serde
|
||||
* serde_json
|
||||
* tokio
|
||||
* tokio-util
|
||||
|
||||
|
||||
### Getting Started
|
||||
|
||||
Clone the repo
|
||||
|
||||
```
|
||||
```BASH
|
||||
git clone --recursive https://github.com/kdeng00/IcarusDownloadManager
|
||||
```
|
||||
|
||||
Install packages
|
||||
|
||||
```
|
||||
vcpkg install nlohman-json
|
||||
vcpkg install openssl
|
||||
vcpkg install curl
|
||||
vcpkg install cpr
|
||||
```
|
||||
|
||||
|
||||
Build the project:
|
||||
|
||||
```
|
||||
```BASH
|
||||
cd IcarusDownloadManager
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
cmake ..
|
||||
cmake --build . --config release -j
|
||||
cargo build
|
||||
```
|
||||
|
||||
The program has been built and can be executed by the binary file *icd*. For information on how to use icd, merely execute the program without any command line arguments.
|
||||
The program has been built and can be executed by the binary file *icarus-dm*. For information on how to use icarua-dm, merely execute the program without any command line arguments.
|
||||
|
||||
### Downloading Song
|
||||
|
||||
```BASH
|
||||
icd download -u spacecadet -p stellar40 -h https://icarus.com -b 15
|
||||
```
|
||||
|
||||
### Uploading Song
|
||||
|
||||
```BASH
|
||||
icd upload -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.mp3
|
||||
icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -b 15
|
||||
```
|
||||
|
||||
### Uploading Song with metadata
|
||||
|
||||
```BASH
|
||||
icd upload-meta -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.mp3 -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png
|
||||
icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.mp3 -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png
|
||||
```
|
||||
|
||||
### Uploading Song with metadata from directory
|
||||
|
||||
```BASH
|
||||
icd upload-meta -u spacecadet -p stellar40 -h https://icarus.com -smca /path/where/songs/and/metadata/exists/
|
||||
icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -smca /path/where/songs/and/metadata/exists/
|
||||
```
|
||||
|
||||
|
||||
### Retrieving Song in json
|
||||
|
||||
```Bash
|
||||
icd retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs
|
||||
icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs
|
||||
```
|
||||
|
||||
### Deleting Song
|
||||
|
||||
```BASH
|
||||
icd delete -u spacecadet -p stellar40 -h https://icarus.com -D 15
|
||||
icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -D 15
|
||||
```
|
||||
|
||||
|
||||
@@ -97,12 +78,6 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduc
|
||||
[v0.1.1](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/v0.1.1)
|
||||
[v0.1.0](https://github.com/kdeng00/IcarusDownloadManager/releases/tag/0.1.0)
|
||||
|
||||
## Authors
|
||||
|
||||
* **Kun Deng** - [kdeng00](https://github.com/kdeng00)
|
||||
|
||||
See also the list of [contributors](https://github.com/kdeng00/Icarus/graphs/contributors) who participated in this project.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
pub const WAV_FILE_EXTENSION: &str = ".wav";
|
||||
pub const MP3_FILE_EXTENSION: &str = ".mp3";
|
||||
pub const _MP3_FILE_EXTENSION: &str = ".mp3";
|
||||
pub const JPG_FILE_EXTENSION: &str = ".jpg";
|
||||
|
||||
@@ -18,7 +18,6 @@ fn print_help() {
|
||||
|
||||
Actions
|
||||
download
|
||||
upload (Search NOTE)
|
||||
upload-meta
|
||||
retrieve
|
||||
delete
|
||||
@@ -29,12 +28,6 @@ fn print_help() {
|
||||
-p password
|
||||
-h host
|
||||
|
||||
Required for upload
|
||||
-s path of song
|
||||
-sd directory where to search for songs to upload (Optional)
|
||||
-sr directory where to recursively search for songs to upload (Optional)
|
||||
-nc will not prompt the user when uploading from a directory
|
||||
|
||||
Required for upload with metadata
|
||||
-s path of song
|
||||
-t track number
|
||||
|
||||
@@ -124,7 +124,7 @@ impl ActionManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_action(&self) {
|
||||
fn _print_action(&self) {
|
||||
if self.action.len() == 0 {
|
||||
println!("Action is empty");
|
||||
} else {
|
||||
@@ -132,7 +132,7 @@ impl ActionManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn print_flags(&self) {
|
||||
fn _print_flags(&self) {
|
||||
println!("Printing flags...");
|
||||
for flag in &self.flags {
|
||||
println!("Flag {}", flag.flag);
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
use std::default::Default;
|
||||
use std::fmt::Display;
|
||||
use std::fs::{read_dir, DirEntry};
|
||||
use std::io::{Error, Read, Result, Write};
|
||||
use std::path::Path;
|
||||
use std::fs::read_dir;
|
||||
use std::io::{Result, Write};
|
||||
use std::str::FromStr;
|
||||
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::managers;
|
||||
use crate::models::song::Album;
|
||||
use crate::models::{self, song};
|
||||
use crate::models::{self};
|
||||
use crate::syncers;
|
||||
use crate::utilities;
|
||||
use crate::{constants, parsers};
|
||||
use crate::{exit_program, managers};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CommitManager {
|
||||
@@ -32,7 +29,7 @@ enum ActionValues {
|
||||
None,
|
||||
}
|
||||
|
||||
enum RetrieveTypes {
|
||||
enum _RetrieveTypes {
|
||||
Songs,
|
||||
}
|
||||
|
||||
@@ -45,7 +42,7 @@ enum En {
|
||||
}
|
||||
|
||||
impl Album {
|
||||
pub fn print_info(&self) {
|
||||
pub fn _print_info(&self) {
|
||||
println!("Album: {}", self.title);
|
||||
println!("Album Artist: {}", self.album_artist);
|
||||
println!("Genre: {}", self.genre);
|
||||
@@ -126,7 +123,6 @@ impl CommitManager {
|
||||
return actions;
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
fn delete_song(&self) {
|
||||
let mut prsr = parsers::api_parser::APIParser {
|
||||
ica_act: self.ica_action.clone(),
|
||||
@@ -150,11 +146,20 @@ impl CommitManager {
|
||||
}
|
||||
}
|
||||
|
||||
let del = syncers::delete::Delete { api: api };
|
||||
let mut del = syncers::delete::Delete { api: api.clone() };
|
||||
|
||||
println!("Deleting song..");
|
||||
|
||||
del.delete_song(&token, &song);
|
||||
let res_fut = del.delete_song(&token, &song);
|
||||
let result = Runtime::new().unwrap().block_on(res_fut);
|
||||
match result {
|
||||
Ok(o) => {
|
||||
println!("Song (Id {:?}) has been successfully deleted", o.id);
|
||||
}
|
||||
Err(er) => {
|
||||
println!("Error {:?}", er);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn download_song(&self) {
|
||||
@@ -213,8 +218,8 @@ impl CommitManager {
|
||||
let result = Runtime::new().unwrap().block_on(result_fut);
|
||||
match result {
|
||||
Ok(o) => {
|
||||
for song in o {
|
||||
println!("{:?} - {:?}", song.artist, song.title);
|
||||
for son in o {
|
||||
son.print_info();
|
||||
}
|
||||
}
|
||||
Err(er) => {
|
||||
@@ -280,9 +285,9 @@ impl CommitManager {
|
||||
println!("metadata path: {}", metadata_path);
|
||||
println!("cover art path: {}", coverpath);
|
||||
|
||||
self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath);
|
||||
let _ = self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath);
|
||||
} else if multitarget {
|
||||
self.multi_target_upload(&uni);
|
||||
let _ = self.multi_target_upload(&uni);
|
||||
} else {
|
||||
println!("Single or Multi target has not been chosen");
|
||||
}
|
||||
@@ -681,8 +686,8 @@ impl CommitManager {
|
||||
song.track = Some(track);
|
||||
}
|
||||
|
||||
fn parse_disc_and_track(&self, song: &mut models::song::Song, track_id: &String) {
|
||||
let sep = |a: &char, b: &char| -> bool {
|
||||
fn _parse_disc_and_track(&self, song: &mut models::song::Song, track_id: &String) {
|
||||
let sep = |_a: &char, _b: &char| -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -690,7 +695,7 @@ impl CommitManager {
|
||||
|
||||
if index == -1 {
|
||||
let mut d_str: String = String::new();
|
||||
let mut t_str = String::new();
|
||||
let t_str = String::new();
|
||||
|
||||
for c in track_id.chars().skip(0).take(index as usize) {
|
||||
d_str.push(c);
|
||||
@@ -712,7 +717,7 @@ impl CommitManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_no_confirm(&self) -> bool {
|
||||
fn _check_for_no_confirm(&self) -> bool {
|
||||
for flag in self.ica_action.flags.iter() {
|
||||
if flag.flag == "-nc" {
|
||||
return true;
|
||||
|
||||
@@ -24,9 +24,7 @@ impl TokenManager {
|
||||
pub async fn request_token(&self) -> Result<models::token::Token, std::io::Error> {
|
||||
println!("Sending request for a token");
|
||||
|
||||
// let endpoint = self.construct_endpoint();
|
||||
let mut url = self.retrieve_url();
|
||||
// url += &endpoint;
|
||||
let url = self.retrieve_url();
|
||||
|
||||
println!("URL: {}", url);
|
||||
|
||||
@@ -59,7 +57,7 @@ impl TokenManager {
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
let mut api = &mut self.api;
|
||||
let api = &mut self.api;
|
||||
api.version = String::from("v1");
|
||||
api.endpoint = String::from(format!("api/{}/login", api.version));
|
||||
}
|
||||
@@ -67,9 +65,6 @@ impl TokenManager {
|
||||
pub fn retrieve_url(&self) -> String {
|
||||
let api = &self.api;
|
||||
let mut url = String::from(&api.url);
|
||||
// url += &String::from("api/");
|
||||
// url += &String::from(&api.version);
|
||||
// url += &String::from("/");
|
||||
url += &String::from(&api.endpoint);
|
||||
url += &String::from("/");
|
||||
|
||||
@@ -77,7 +72,7 @@ impl TokenManager {
|
||||
}
|
||||
|
||||
// NOTE: This can get deleted. Redundant
|
||||
fn construct_endpoint(&self) -> String {
|
||||
fn _construct_endpoint(&self) -> String {
|
||||
let mut endpoint: String = String::from("api/");
|
||||
endpoint += &self.api.version;
|
||||
endpoint += "/login";
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::default::Default;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::models::{self, user::User};
|
||||
use crate::models::{self};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UserManager {
|
||||
|
||||
+4
-3
@@ -9,6 +9,7 @@ use crate::constants;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Song {
|
||||
#[serde(alias = "song_id")]
|
||||
pub id: Option<i32>,
|
||||
pub title: Option<String>,
|
||||
pub artist: Option<String>,
|
||||
@@ -111,7 +112,7 @@ impl Song {
|
||||
}
|
||||
|
||||
// if 1 - wav, if 0 - mp3, anything else defaults to wav
|
||||
pub fn generate_filename_from_track(&mut self, i_type: i32) -> i32 {
|
||||
pub fn _generate_filename_from_track(&mut self, i_type: i32) -> i32 {
|
||||
let mut filename: String = String::new();
|
||||
if self.track.unwrap() < 10 {
|
||||
filename += "0";
|
||||
@@ -120,7 +121,7 @@ impl Song {
|
||||
filename += &self.track.unwrap().to_string();
|
||||
|
||||
if i_type == 0 {
|
||||
filename += constants::file_extensions::MP3_FILE_EXTENSION;
|
||||
filename += constants::file_extensions::_MP3_FILE_EXTENSION;
|
||||
} else {
|
||||
filename += constants::file_extensions::WAV_FILE_EXTENSION;
|
||||
}
|
||||
@@ -130,7 +131,7 @@ impl Song {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn to_metadata_json(&self) -> Result<String, serde_json::Error> {
|
||||
pub fn _to_metadata_json(&self) -> Result<String, serde_json::Error> {
|
||||
return serde_json::to_string_pretty(&self);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ impl Default for User {
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn to_json(&self) -> Result<String, serde_json::Error> {
|
||||
pub fn _to_json(&self) -> Result<String, serde_json::Error> {
|
||||
return serde_json::to_string_pretty(&self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,18 @@ impl APIParser {
|
||||
let flags = self.ica_act.flags.clone();
|
||||
println!("Parsing api");
|
||||
|
||||
let mut i = 0;
|
||||
// for (i, elem) in flags {
|
||||
for elem in flags {
|
||||
let arg = elem.flag;
|
||||
let value = elem.value;
|
||||
|
||||
if arg == "-h" {
|
||||
if value.chars().nth((value.len() - 1)) == Some('/') {
|
||||
if value.chars().nth(value.len() - 1) == Some('/') {
|
||||
self.api.url = value;
|
||||
} else {
|
||||
self.api.url = value + "/";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
self.api.version = "v1".to_string();
|
||||
|
||||
+20
-9
@@ -1,16 +1,9 @@
|
||||
use std::default::Default;
|
||||
use std::io::Error;
|
||||
|
||||
use reqwest;
|
||||
use reqwest::Response;
|
||||
use serde;
|
||||
// use serde::Deserialize;
|
||||
// use serde::Serialize;
|
||||
|
||||
use crate::models;
|
||||
|
||||
use super::syncer_base;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Delete {
|
||||
pub api: models::api::API,
|
||||
@@ -25,9 +18,14 @@ impl Default for Delete {
|
||||
}
|
||||
|
||||
impl Delete {
|
||||
pub async fn delete_song(&self, token: &models::token::Token, song: &models::song::Song) {
|
||||
pub async fn delete_song(
|
||||
&mut self,
|
||||
token: &models::token::Token,
|
||||
song: &models::song::Song,
|
||||
) -> Result<models::song::Song, std::io::Error> {
|
||||
self.api.endpoint = "song/data/delete".to_owned();
|
||||
let url = self.retrieve_url(&song);
|
||||
let client = reqwest::Client::new();
|
||||
let client = reqwest::Client::builder().build().unwrap();
|
||||
let access_token = token.bearer_token();
|
||||
let response = client
|
||||
.delete(&url)
|
||||
@@ -35,15 +33,28 @@ impl Delete {
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
let mut sng = models::song::Song::default();
|
||||
|
||||
match response.status() {
|
||||
reqwest::StatusCode::OK => {
|
||||
println!("Success!");
|
||||
let s = response.json::<models::song::Song>().await;
|
||||
match s {
|
||||
//
|
||||
Ok(parsed) => {
|
||||
sng = parsed;
|
||||
}
|
||||
Err(er) => {
|
||||
println!("Error {:?}", er);
|
||||
}
|
||||
};
|
||||
}
|
||||
other => {
|
||||
panic!("Issue occurred: {:?}", other);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(sng);
|
||||
}
|
||||
|
||||
fn retrieve_url(&self, song: &models::song::Song) -> String {
|
||||
|
||||
@@ -16,7 +16,7 @@ impl Default for Download {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MyError {
|
||||
Request(reqwest::Error),
|
||||
_Request(reqwest::Error),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod delete;
|
||||
pub mod download;
|
||||
pub mod retrieve_records;
|
||||
pub mod syncer_base;
|
||||
pub mod upload;
|
||||
|
||||
@@ -3,8 +3,6 @@ use std::io::Error;
|
||||
|
||||
use crate::models;
|
||||
|
||||
// use super::syncer_base::Result;
|
||||
|
||||
pub struct RetrieveRecords {
|
||||
pub api: models::api::API,
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// use crate::models;
|
||||
|
||||
pub struct Syncer {
|
||||
// pub api: models::api::API,
|
||||
ok: i32,
|
||||
unauthorized: i32,
|
||||
not_found: i32,
|
||||
}
|
||||
|
||||
impl Syncer {
|
||||
pub fn ok() -> i32 {
|
||||
return 200;
|
||||
}
|
||||
|
||||
pub fn unauthorized() -> i32 {
|
||||
return 401;
|
||||
}
|
||||
|
||||
pub fn not_found() -> i32 {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Result {
|
||||
OK,
|
||||
UNAUTHORIZED,
|
||||
NOTFOUND,
|
||||
}
|
||||
@@ -5,7 +5,6 @@ use http::HeaderValue;
|
||||
use reqwest;
|
||||
use reqwest::multipart::Form;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::fs::File;
|
||||
|
||||
use crate::constants;
|
||||
use crate::models;
|
||||
@@ -112,7 +111,7 @@ impl Upload {
|
||||
return Ok(response_text);
|
||||
}
|
||||
|
||||
fn initialize_form(
|
||||
fn _initialize_form(
|
||||
&self,
|
||||
song_raw_data: Vec<u8>,
|
||||
cover_raw_data: Vec<u8>,
|
||||
@@ -124,14 +123,14 @@ impl Upload {
|
||||
http::HeaderValue::from_static("application/octet-stream"),
|
||||
);
|
||||
|
||||
let mut file = reqwest::multipart::Part::bytes(song_raw_data).headers(headers);
|
||||
let file = reqwest::multipart::Part::bytes(song_raw_data).headers(headers);
|
||||
let mut headers_i = HeaderMap::new();
|
||||
headers_i.insert(
|
||||
http::header::CONTENT_TYPE,
|
||||
http::HeaderValue::from_static("image/jpeg"),
|
||||
);
|
||||
|
||||
let mut cover = reqwest::multipart::Part::bytes(cover_raw_data).headers(headers_i);
|
||||
let cover = reqwest::multipart::Part::bytes(cover_raw_data).headers(headers_i);
|
||||
|
||||
let mut song_filename = String::from("audio");
|
||||
song_filename += constants::file_extensions::WAV_FILE_EXTENSION;
|
||||
|
||||
@@ -9,24 +9,23 @@ impl Checks {
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
pub fn item_in_container(
|
||||
container: &Vec<String>,
|
||||
item: &String,
|
||||
func: fn(a: &String, b: &String) -> String,
|
||||
pub fn _item_in_container(
|
||||
_container: &Vec<String>,
|
||||
_item: &String,
|
||||
_func: fn(a: &String, b: &String) -> String,
|
||||
) -> String {
|
||||
return String::from("");
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
pub fn item_iter_in_container(
|
||||
container: &Vec<String>,
|
||||
item: &String,
|
||||
func: fn(a: &String, b: &String) -> String,
|
||||
pub fn _item_iter_in_container(
|
||||
_container: &Vec<String>,
|
||||
_item: &String,
|
||||
_func: fn(a: &String, b: &String) -> String,
|
||||
) -> String {
|
||||
return String::from("");
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
pub fn index_of_item_in_container<F>(container: &String, item: &char, func: F) -> i32
|
||||
where
|
||||
F: Fn(&char, &char) -> bool,
|
||||
|
||||
@@ -4,15 +4,15 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct Conversions {}
|
||||
|
||||
impl Conversions {
|
||||
pub fn to_lower_char(val: &mut char) {
|
||||
pub fn _to_lower_char(val: &mut char) {
|
||||
if val.is_alphabetic() {
|
||||
*val = val.to_lowercase().next().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initialize_values(&self) {}
|
||||
pub fn _initialize_values(&self) {}
|
||||
|
||||
pub fn print_value<T: std::fmt::Debug>(&self, val: T) {
|
||||
pub fn _print_value<T: std::fmt::Debug>(&self, val: T) {
|
||||
println!("Going to print value");
|
||||
println!("{:?}", val);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user