Skip to content

Commit 9f3c895

Browse files
File content resource
1 parent 55d2efe commit 9f3c895

File tree

6 files changed

+178
-16
lines changed

6 files changed

+178
-16
lines changed

resources/filesys/Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/filesys/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ edition = "2021"
55

66
[dependencies]
77
clap = { version ="4.5", features = ["derive"] }
8+
encoding_rs = "0.8.35"
9+
encoding_rs_io = "0.1.7"
810
serde = "1.0"
911
serde_json = "1.0"
1012
schemars = "0.8"

resources/filesys/src/config.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct Directory {
3636
pub files: Option<Vec<File>>,
3737

3838
/// Recurse into subdirectories.
39+
#[serde(skip_serializing_if = "Option::is_none")]
3940
pub recurse: Option<bool>,
4041

4142
#[serde(rename = "_exist", skip_serializing_if = "Option::is_none")]
@@ -49,15 +50,19 @@ pub struct FileContent
4950
/// The path to the file.
5051
pub path: String,
5152

52-
/// The file hash.
53-
pub hash: String,
53+
/// The file hash. If not provided, the hash is calculated from the content.
54+
#[serde(skip_serializing_if = "Option::is_none")]
55+
pub hash: Option<String>,
5456

55-
/// The file encoding.
56-
pub encoding: Encoding,
57+
/// The file encoding. UTF-8 is the default.
58+
#[serde(skip_serializing_if = "Option::is_none")]
59+
pub encoding: Option<Encoding>,
5760

5861
/// The file content.
59-
pub content: String,
62+
#[serde(skip_serializing_if = "Option::is_none")]
63+
pub content: Option<String>,
6064

65+
/// If the file exists. True is the default.
6166
#[serde(rename = "_exist", skip_serializing_if = "Option::is_none")]
6267
pub exist: Option<bool>,
6368
}
@@ -66,9 +71,6 @@ pub struct FileContent
6671
pub enum Encoding {
6772
Utf8,
6873
Utf16,
69-
Utf32,
7074
Ascii,
71-
Base64,
72-
Hex,
7375
Binary,
74-
}
76+
}

resources/filesys/src/file_helper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ fn compare_file_state(file: &File) -> Result<File, Box<dyn std::error::Error>> {
158158
};
159159
}
160160

161-
fn calculate_hash(path: &str) -> Result<String, Box<dyn std::error::Error>> {
161+
pub fn calculate_hash(path: &str) -> Result<String, Box<dyn std::error::Error>> {
162162
let bytes = fs::read(path)?;
163163
let digest = sha256::digest(&bytes);
164164
Ok(digest)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::config::{FileContent, Encoding};
5+
use std::path::Path;
6+
use std::io;
7+
use tracing::{debug};
8+
9+
impl Encoding {
10+
pub fn to_encoding_rs(&self) -> Option<&'static encoding_rs::Encoding> {
11+
match self {
12+
Encoding::Utf8 => Some(encoding_rs::UTF_8),
13+
Encoding::Utf16 => Some(encoding_rs::UTF_16LE), // or UTF_16BE depending on your needs
14+
Encoding::Ascii => Some(encoding_rs::WINDOWS_1252), // ASCII is a subset of Windows-1252
15+
Encoding::Binary => None,
16+
}
17+
}
18+
}
19+
20+
impl FileContent {
21+
/// Create a new `FileContent`.
22+
///
23+
/// # Arguments
24+
///
25+
/// * `string` - The string for the Path
26+
#[must_use]
27+
pub fn new(path: &str) -> FileContent {
28+
FileContent {
29+
path: path.to_string(),
30+
content: None,
31+
hash: None,
32+
encoding: Some(Encoding::Utf8),
33+
exist: None,
34+
}
35+
}
36+
}
37+
38+
pub fn get_file_content(filecontent: &FileContent) -> Result<FileContent, Box<dyn std::error::Error>> {
39+
debug!("In get_file_content");
40+
match compare_filecontent_state(filecontent) {
41+
Ok(f) => {
42+
Ok(f)
43+
},
44+
Err(e) => {
45+
Err(e)?
46+
}
47+
}
48+
}
49+
50+
pub fn set_file_content(filecontent: &FileContent) -> Result<FileContent, Box<dyn std::error::Error>> {
51+
// debug!("In set_file_content");
52+
// let path = Path::new(&filecontent.path);
53+
// let content = filecontent.content.as_ref().unwrap_or(&String::new());
54+
// let encoding = filecontent.encoding.unwrap_or(Encoding::Utf8).to_encoding_rs().unwrap_or(encoding_rs::UTF_8);
55+
// let mut file = fsFile::create(path)?;
56+
// let mut encoder = encoding.new_encoder();
57+
// let mut bytes = vec![0; content.len() * encoding.new_encoder().max_buffer_length()];
58+
// let (bytes_written, _, _) = encoder.encode_to_slice(content, &mut bytes, true);
59+
// file.write_all(&bytes[..bytes_written])?;
60+
// Ok(filecontent.clone())
61+
Ok(filecontent.clone())
62+
}
63+
64+
pub fn delete_file_content(filecontent: &FileContent) -> Result<FileContent, Box<dyn std::error::Error>> {
65+
debug!("In delete_file_content");
66+
let path = Path::new(&filecontent.path);
67+
68+
let mut filecontent_to_delete = filecontent.clone();
69+
70+
if path.exists() {
71+
72+
filecontent_to_delete.exist = Some(false);
73+
filecontent_to_delete.content = None;
74+
set_file_content(&filecontent)?;
75+
}
76+
77+
Ok(filecontent_to_delete)
78+
}
79+
80+
pub fn read_file_with_encoding(path: &Path, encoding: &'static encoding_rs::Encoding) -> io::Result<String> {
81+
let bytes = std::fs::read(path)?;
82+
let (decoded_str, _encoding_used, had_errors) = encoding.decode(&bytes);
83+
84+
if had_errors {
85+
return Err(io::Error::new(io::ErrorKind::InvalidData, "Invalid encoding"));
86+
}
87+
88+
Ok(decoded_str.to_string())
89+
}
90+
91+
pub fn compare_filecontent_state(filecontent: &FileContent) -> Result<FileContent, Box<dyn std::error::Error>> {
92+
debug!("In compare_filecontent_state");
93+
94+
let rs_encoding = filecontent.encoding.as_ref().unwrap_or(&Encoding::Utf8).to_encoding_rs().unwrap_or(encoding_rs::UTF_8);
95+
96+
let path = Path::new(&filecontent.path);
97+
if path.exists() {
98+
let content = read_file_with_encoding(path, rs_encoding)?;
99+
let content_hash = sha256::digest(content.as_bytes());
100+
let hash = filecontent.hash.as_ref().unwrap_or(&content_hash);
101+
102+
match filecontent.hash.as_ref() {
103+
Some(h) => {
104+
if h.to_lowercase() == hash.to_lowercase() {
105+
let mut updated_file_content = filecontent.clone();
106+
updated_file_content.hash = Some(hash.to_string());
107+
updated_file_content.content = Some(content);
108+
updated_file_content.exist = Some(true);
109+
110+
return Ok(updated_file_content)
111+
}
112+
else {
113+
return Err("Hash does not match")?;
114+
}
115+
},
116+
None => {
117+
let mut updated_file_content = filecontent.clone();
118+
updated_file_content.hash = Some(hash.to_string());
119+
updated_file_content.content = Some(content);
120+
updated_file_content.exist = Some(true);
121+
122+
return Ok(updated_file_content)
123+
}
124+
}
125+
}
126+
else {
127+
match filecontent.exist {
128+
Some(true) | None => {
129+
return Err("File does not exist")?;
130+
},
131+
Some(false) => {
132+
return Ok(filecontent.clone());
133+
}
134+
}
135+
}
136+
}

resources/filesys/src/main.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ use tracing::{debug, error};
88
use crate::config::{File, Directory, FileContent};
99
use file_helper::{get_file, set_file, delete_file, export_file_path};
1010
use dir_helpers::{get_dir, set_dir, delete_dir, export_dir_path};
11+
use filecontent_helper::{get_file_content, delete_file_content};
1112
use schemars::schema_for;
1213

1314
mod args;
1415
pub mod config;
1516
mod file_helper;
1617
mod dir_helpers;
18+
mod filecontent_helper;
1719

1820
const EXIT_SUCCESS: i32 = 0;
1921
const EXIT_INVALID_INPUT: i32 = 2;
@@ -40,9 +42,9 @@ fn main() {
4042
None => {
4143
let filecontent = match is_fillecontent_type(input.as_str()) {
4244
Some(filecontent) => {
43-
// let filecontent = get_file(&filecontent).unwrap();
44-
// let json = serde_json::to_string(&filecontent).unwrap();
45-
// println!("{}", json);
45+
let filecontent = get_file_content(&filecontent).unwrap();
46+
let json = serde_json::to_string(&filecontent).unwrap();
47+
println!("{}", json);
4648
}
4749
None => {
4850
error!("Invalid input.");
@@ -73,9 +75,9 @@ fn main() {
7375
None => {
7476
let filecontent = match is_fillecontent_type(input.as_str()) {
7577
Some(filecontent) => {
76-
// let filecontent = delete_file(&filecontent).unwrap();
77-
// let json = serde_json::to_string(&filecontent).unwrap();
78-
// println!("{}", json);
78+
let filecontent = delete_file_content(&filecontent).unwrap();
79+
let json = serde_json::to_string(&filecontent).unwrap();
80+
println!("{}", json);
7981
}
8082
None => {
8183
error!("Invalid input.");

0 commit comments

Comments
 (0)