[INFO] fetching crate yok 0.1.9... [INFO] testing yok-0.1.9 against try#b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b for pr-145330-1 [INFO] extracting crate yok 0.1.9 into /workspace/builds/worker-0-tc2/source [INFO] started tweaking crates.io crate yok 0.1.9 [INFO] finished tweaking crates.io crate yok 0.1.9 [INFO] tweaked toml for crates.io crate yok 0.1.9 written to /workspace/builds/worker-0-tc2/source/Cargo.toml [INFO] validating manifest of crates.io crate yok 0.1.9 on toolchain b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b [INFO] running `Command { std: CARGO_HOME="/workspace/cargo-home" RUSTUP_HOME="/workspace/rustup-home" "/workspace/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "metadata" "--manifest-path" "Cargo.toml" "--no-deps", kill_on_drop: false }` [INFO] crate crates.io crate yok 0.1.9 already has a lockfile, it will not be regenerated [INFO] running `Command { std: CARGO_HOME="/workspace/cargo-home" RUSTUP_HOME="/workspace/rustup-home" "/workspace/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "fetch" "--manifest-path" "Cargo.toml", kill_on_drop: false }` [INFO] running `Command { std: "docker" "create" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/target:/opt/rustwide/target:rw,Z" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/source:/opt/rustwide/workdir:ro,Z" "-v" "/var/lib/crater-agent-workspace/cargo-home:/opt/rustwide/cargo-home:ro,Z" "-v" "/var/lib/crater-agent-workspace/rustup-home:/opt/rustwide/rustup-home:ro,Z" "-e" "SOURCE_DIR=/opt/rustwide/workdir" "-e" "CARGO_TARGET_DIR=/opt/rustwide/target" "-e" "CARGO_HOME=/opt/rustwide/cargo-home" "-e" "RUSTUP_HOME=/opt/rustwide/rustup-home" "-w" "/opt/rustwide/workdir" "-m" "1610612736" "--user" "0:0" "--network" "none" "ghcr.io/rust-lang/crates-build-env/linux@sha256:e90291280db7d1fac5b66fc6dad9f9662629e7365a55743daf9bdf73ebc4ea79" "/opt/rustwide/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "metadata" "--no-deps" "--format-version=1", kill_on_drop: false }` [INFO] [stdout] ac6bcf9fc50412273ed0b68199673c15c2f03b91508b5399594284f626fc5fd7 [INFO] running `Command { std: "docker" "start" "-a" "ac6bcf9fc50412273ed0b68199673c15c2f03b91508b5399594284f626fc5fd7", kill_on_drop: false }` [INFO] running `Command { std: "docker" "inspect" "ac6bcf9fc50412273ed0b68199673c15c2f03b91508b5399594284f626fc5fd7", kill_on_drop: false }` [INFO] running `Command { std: "docker" "rm" "-f" "ac6bcf9fc50412273ed0b68199673c15c2f03b91508b5399594284f626fc5fd7", kill_on_drop: false }` [INFO] [stdout] ac6bcf9fc50412273ed0b68199673c15c2f03b91508b5399594284f626fc5fd7 [INFO] running `Command { std: "docker" "create" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/target:/opt/rustwide/target:rw,Z" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/source:/opt/rustwide/workdir:ro,Z" "-v" "/var/lib/crater-agent-workspace/cargo-home:/opt/rustwide/cargo-home:ro,Z" "-v" "/var/lib/crater-agent-workspace/rustup-home:/opt/rustwide/rustup-home:ro,Z" "-e" "SOURCE_DIR=/opt/rustwide/workdir" "-e" "CARGO_TARGET_DIR=/opt/rustwide/target" "-e" "CARGO_INCREMENTAL=0" "-e" "RUST_BACKTRACE=full" "-e" "RUSTFLAGS=--cap-lints=forbid" "-e" "RUSTDOCFLAGS=--cap-lints=forbid" "-e" "CARGO_HOME=/opt/rustwide/cargo-home" "-e" "RUSTUP_HOME=/opt/rustwide/rustup-home" "-w" "/opt/rustwide/workdir" "-m" "1610612736" "--user" "0:0" "--network" "none" "ghcr.io/rust-lang/crates-build-env/linux@sha256:e90291280db7d1fac5b66fc6dad9f9662629e7365a55743daf9bdf73ebc4ea79" "/opt/rustwide/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "build" "--frozen" "--message-format=json", kill_on_drop: false }` [INFO] [stdout] 55dbf966cc10700db3a3a742e026cd9cfce1f365592733bd77daf2c8d406775c [INFO] running `Command { std: "docker" "start" "-a" "55dbf966cc10700db3a3a742e026cd9cfce1f365592733bd77daf2c8d406775c", kill_on_drop: false }` [INFO] [stderr] Compiling yok v0.1.9 (/opt/rustwide/workdir) [INFO] [stderr] Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.03s [INFO] running `Command { std: "docker" "inspect" "55dbf966cc10700db3a3a742e026cd9cfce1f365592733bd77daf2c8d406775c", kill_on_drop: false }` [INFO] running `Command { std: "docker" "rm" "-f" "55dbf966cc10700db3a3a742e026cd9cfce1f365592733bd77daf2c8d406775c", kill_on_drop: false }` [INFO] [stdout] 55dbf966cc10700db3a3a742e026cd9cfce1f365592733bd77daf2c8d406775c [INFO] running `Command { std: "docker" "create" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/target:/opt/rustwide/target:rw,Z" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/source:/opt/rustwide/workdir:ro,Z" "-v" "/var/lib/crater-agent-workspace/cargo-home:/opt/rustwide/cargo-home:ro,Z" "-v" "/var/lib/crater-agent-workspace/rustup-home:/opt/rustwide/rustup-home:ro,Z" "-e" "SOURCE_DIR=/opt/rustwide/workdir" "-e" "CARGO_TARGET_DIR=/opt/rustwide/target" "-e" "CARGO_INCREMENTAL=0" "-e" "RUST_BACKTRACE=full" "-e" "RUSTFLAGS=--cap-lints=forbid" "-e" "RUSTDOCFLAGS=--cap-lints=forbid" "-e" "CARGO_HOME=/opt/rustwide/cargo-home" "-e" "RUSTUP_HOME=/opt/rustwide/rustup-home" "-w" "/opt/rustwide/workdir" "-m" "1610612736" "--user" "0:0" "--network" "none" "ghcr.io/rust-lang/crates-build-env/linux@sha256:e90291280db7d1fac5b66fc6dad9f9662629e7365a55743daf9bdf73ebc4ea79" "/opt/rustwide/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "test" "--frozen" "--no-run" "--message-format=json", kill_on_drop: false }` [INFO] [stdout] cf5991c602df69db61f359cc120873c9f657406de938163810d4c9f0c2701c48 [INFO] running `Command { std: "docker" "start" "-a" "cf5991c602df69db61f359cc120873c9f657406de938163810d4c9f0c2701c48", kill_on_drop: false }` [INFO] [stderr] Compiling yok v0.1.9 (/opt/rustwide/workdir) [INFO] [stderr] Finished `test` profile [unoptimized + debuginfo] target(s) in 0.72s [INFO] running `Command { std: "docker" "inspect" "cf5991c602df69db61f359cc120873c9f657406de938163810d4c9f0c2701c48", kill_on_drop: false }` [INFO] running `Command { std: "docker" "rm" "-f" "cf5991c602df69db61f359cc120873c9f657406de938163810d4c9f0c2701c48", kill_on_drop: false }` [INFO] [stdout] cf5991c602df69db61f359cc120873c9f657406de938163810d4c9f0c2701c48 [INFO] running `Command { std: "docker" "create" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/target:/opt/rustwide/target:rw,Z" "-v" "/var/lib/crater-agent-workspace/builds/worker-0-tc2/source:/opt/rustwide/workdir:ro,Z" "-v" "/var/lib/crater-agent-workspace/cargo-home:/opt/rustwide/cargo-home:ro,Z" "-v" "/var/lib/crater-agent-workspace/rustup-home:/opt/rustwide/rustup-home:ro,Z" "-e" "SOURCE_DIR=/opt/rustwide/workdir" "-e" "CARGO_TARGET_DIR=/opt/rustwide/target" "-e" "CARGO_INCREMENTAL=0" "-e" "RUST_BACKTRACE=full" "-e" "RUSTFLAGS=--cap-lints=forbid" "-e" "RUSTDOCFLAGS=--cap-lints=forbid" "-e" "CARGO_HOME=/opt/rustwide/cargo-home" "-e" "RUSTUP_HOME=/opt/rustwide/rustup-home" "-w" "/opt/rustwide/workdir" "-m" "1610612736" "--user" "0:0" "--network" "none" "ghcr.io/rust-lang/crates-build-env/linux@sha256:e90291280db7d1fac5b66fc6dad9f9662629e7365a55743daf9bdf73ebc4ea79" "/opt/rustwide/cargo-home/bin/cargo" "+b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b" "test" "--frozen", kill_on_drop: false }` [INFO] [stdout] 8904e9d24b667eaa29fa4673a8c7a80a614852a10280e2c634184af6e7ae4e2b [INFO] running `Command { std: "docker" "start" "-a" "8904e9d24b667eaa29fa4673a8c7a80a614852a10280e2c634184af6e7ae4e2b", kill_on_drop: false }` [INFO] [stderr] Compiling yok v0.1.9 (/opt/rustwide/workdir) [INFO] [stderr] Finished `test` profile [unoptimized + debuginfo] target(s) in 0.96s [INFO] [stderr] Running unittests src/lib.rs (/opt/rustwide/target/debug/deps/yok-e9badd654e162118) [INFO] [stderr] Running unittests src/main.rs (/opt/rustwide/target/debug/deps/yok-8137b069d8226b45) [INFO] [stdout] [INFO] [stdout] running 0 tests [INFO] [stdout] [INFO] [stdout] test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] running 0 tests [INFO] [stdout] [INFO] [stdout] test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s [INFO] [stdout] [INFO] [stderr] Doc-tests yok [INFO] [stdout] [INFO] [stdout] running 3 tests [INFO] [stdout] test src/lib.rs - Dir::extract (line 218) ... FAILED [INFO] [stdout] test src/lib.rs - include_dir (line 282) ... FAILED [INFO] [stdout] test src/lib.rs - DirEntry (line 10) ... FAILED [INFO] [stdout] [INFO] [stdout] failures: [INFO] [stdout] [INFO] [stdout] ---- src/lib.rs - Dir::extract (line 218) stdout ---- [INFO] [stdout] Test executable failed (exit status: 101). [INFO] [stdout] [INFO] [stdout] stdout: [INFO] [stdout] rm -rf path [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] image: rustlang/rust:nightly [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] push:cargo: [INFO] [stdout] script: [INFO] [stdout] - rustc --version && cargo --version [INFO] [stdout] - cargo publish --allow-dirty --token cioHYTp8cpgbRbBqcJvqGF7rUIjJQSlBvH3 [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] edition = "2021" [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] readme = "README.md" [INFO] [stdout] keywords = ["include","dir","compile-time"] [INFO] [stdout] categories = ["development-tools","game-engines","web-programming"] [INFO] [stdout] license = "MIT" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] build = "build.rs" [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [build-dependencies] [INFO] [stdout] use std::fs::File; [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] use std::io::{BufWriter, Write}; [INFO] [stdout] use std::path::Path; [INFO] [stdout] use zip::write::FileOptions; [INFO] [stdout] use zip::ZipWriter; [INFO] [stdout] use std::io::Read; [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] // Path to the directory to compress [INFO] [stdout] let dir_path = Path::new("."); [INFO] [stdout] [INFO] [stdout] // Path to the output ZIP file [INFO] [stdout] let zip_path = Path::new("app.zip"); [INFO] [stdout] [INFO] [stdout] // Create a new ZIP file [INFO] [stdout] let file = File::create(&zip_path).unwrap(); [INFO] [stdout] let writer = BufWriter::new(file); [INFO] [stdout] let mut zip = ZipWriter::new(writer); [INFO] [stdout] [INFO] [stdout] // Recursively add all files and directories in the input directory to the ZIP file [INFO] [stdout] add_directory_to_zip(&mut zip, dir_path, "").unwrap(); [INFO] [stdout] [INFO] [stdout] // Finalize the ZIP file [INFO] [stdout] zip.finish().unwrap(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Recursively adds a directory (and all its contents) to a ZipWriter [INFO] [stdout] fn add_directory_to_zip(zip: &mut ZipWriter>, dir_path: &Path, base_path: &str) -> zip::result::ZipResult<()> { [INFO] [stdout] for entry in dir_path.read_dir()? { [INFO] [stdout] let path = entry?.path(); [INFO] [stdout] [INFO] [stdout] if path.is_dir() { [INFO] [stdout] // Recursively add the subdirectory [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let new_base_path = format!("{}/{}", base_path, name); [INFO] [stdout] add_directory_to_zip(zip, &path, &new_base_path)?; [INFO] [stdout] } else { [INFO] [stdout] // Add the file to the ZIP archive [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let options = FileOptions::default() [INFO] [stdout] .compression_method(zip::CompressionMethod::Deflated) [INFO] [stdout] .unix_permissions(0o755); // Set appropriate permissions for the file [INFO] [stdout] zip.start_file(format!("{}/{}", base_path, name), options)?; [INFO] [stdout] let mut file = File::open(&path)?; [INFO] [stdout] let mut buffer = Vec::new(); [INFO] [stdout] file.read_to_end(&mut buffer)?; [INFO] [stdout] zip.write_all(&buffer)?; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] MIT License [INFO] [stdout] [INFO] [stdout] Copyright (c) [year] [fullname] [INFO] [stdout] [INFO] [stdout] Permission is hereby granted, free of charge, to any person obtaining a copy [INFO] [stdout] of this software and associated documentation files (the "Software"), to deal [INFO] [stdout] in the Software without restriction, including without limitation the rights [INFO] [stdout] to use, copy, modify, merge, publish, distribute, sublicense, and/or sell [INFO] [stdout] copies of the Software, and to permit persons to whom the Software is [INFO] [stdout] furnished to do so, subject to the following conditions: [INFO] [stdout] [INFO] [stdout] The above copyright notice and this permission notice shall be included in all [INFO] [stdout] copies or substantial portions of the Software. [INFO] [stdout] [INFO] [stdout] THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR [INFO] [stdout] IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, [INFO] [stdout] FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE [INFO] [stdout] AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER [INFO] [stdout] LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, [INFO] [stdout] OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE [INFO] [stdout] SOFTWARE. [INFO] [stdout] use std::{ [INFO] [stdout] fs, io, [INFO] [stdout] path::{Path, PathBuf}, [INFO] [stdout] }; [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] use std::mem; [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] path: String, [INFO] [stdout] is_dir: bool, [INFO] [stdout] is_file: bool, [INFO] [stdout] content: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] data: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.content.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.content.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.content.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] content, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] pub fn walk_dir(path: impl ToString) -> io::Result> { [INFO] [stdout] let path = path.to_string(); [INFO] [stdout] let dir = Path::new(&path); [INFO] [stdout] let mut files = Vec::new(); [INFO] [stdout] for entry in fs::read_dir(dir)? { [INFO] [stdout] let entry = entry?; [INFO] [stdout] let newpath = entry.path(); [INFO] [stdout] [INFO] [stdout] if newpath.is_dir() { [INFO] [stdout] files.extend(walk_dir(&newpath.display())?); [INFO] [stdout] files.push(newpath); [INFO] [stdout] } else { [INFO] [stdout] files.push(newpath); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(files) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] use std::env; [INFO] [stdout] let yok_path = match env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] println!("cargo:rerun-if-env-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed=main.rs"); [INFO] [stdout] println!("cargo:rerun-if-changed=lib.rs"); [INFO] [stdout] let yok_path = std::path::Path::new(&yok_path); [INFO] [stdout] if yok_path.is_dir() { [INFO] [stdout] let mut res_dir: Vec = vec![]; [INFO] [stdout] let dir_list = walk_dir(yok_path.display()).expect("walk dir error"); [INFO] [stdout] for dir in dir_list { [INFO] [stdout] if dir.is_file() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: false, [INFO] [stdout] is_file: true, [INFO] [stdout] content: std::fs::read(dir).expect("read dir error"), [INFO] [stdout] }); [INFO] [stdout] } else if dir.is_dir() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: true, [INFO] [stdout] is_file: false, [INFO] [stdout] content: vec![], [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] let all_dir: Dir = Dir { data: res_dir }; [INFO] [stdout] let all_dir_bytes = all_dir.as_bytes(); [INFO] [stdout] if let Ok(_) = std::fs::write(format!("../.yok"), all_dir_bytes){} [INFO] [stdout] } else { [INFO] [stdout] eprintln!("Environment variable 'YOK_PATH' not dir"); [INFO] [stdout] return; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] # This file is automatically @generated by Cargo. [INFO] [stdout] # It is not intended for manual editing. [INFO] [stdout] version = 3 [INFO] [stdout] [INFO] [stdout] [[package]] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] /target [INFO] [stdout] Cargo.lock [INFO] [stdout] .DS_Store [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::include_dir; [INFO] [stdout] use yok::Bytes; [INFO] [stdout] use yok::Dir; [INFO] [stdout] use yok::DirEntry; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] // println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] // println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] use std::{mem, path::PathBuf, str::FromStr}; [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] pub path: String, [INFO] [stdout] pub is_dir: bool, [INFO] [stdout] pub is_file: bool, [INFO] [stdout] pub contents: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] pub data: Vec, [INFO] [stdout] } [INFO] [stdout] pub trait Bytes { [INFO] [stdout] fn as_bytes(&self) -> Vec; [INFO] [stdout] fn from_bytes(&self) -> Dir; [INFO] [stdout] fn into_bytes(&self) -> Vec; [INFO] [stdout] fn into_dir(&self) -> Dir; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for [u8] { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for Dir { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.contents.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.contents.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.contents.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let contents = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] contents, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] fn get_new_path(path: &str) -> String { [INFO] [stdout] let yok_path = match std::env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] let path = path.to_string().replace("\\", "/").to_string(); [INFO] [stdout] if yok_path != "."{ [INFO] [stdout] return path; [INFO] [stdout] }else { [INFO] [stdout] return path; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] [INFO] [stdout] impl Dir { [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub fn extract(&self, base_dir: impl ToString) ->std::io::Result<()>{ [INFO] [stdout] std::fs::create_dir_all(base_dir.to_string()).expect("create to dir error"); [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_dir { [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] std::fs::create_dir_all(path.clone()).expect("create to dir error"); [INFO] [stdout] if cfg!(target_os = "windows") { [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] }else{ [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_file { [INFO] [stdout] use std::fs::{self, File, OpenOptions}; [INFO] [stdout] use std::io::prelude::*; [INFO] [stdout] use std::os::unix::fs::OpenOptionsExt; [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] if !path.exists(){ [INFO] [stdout] let mut open_options = OpenOptions::new(); [INFO] [stdout] open_options.mode(0o777); // Set read/write permission for owner, read-only for others [INFO] [stdout] let file = open_options.create(true).write(true).open(path).unwrap(); [INFO] [stdout] let mut file_writer = std::io::BufWriter::new(file); [INFO] [stdout] file_writer.write_all(&entry.contents).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub const fn include_dir() -> &'static [u8] { [INFO] [stdout] include_bytes!("../../.yok") [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] src [INFO] [stdout] # yok [INFO] [stdout] [INFO] [stdout] [![Crates.io](https://img.shields.io/crates/v/yok.svg)](https://crates.io/crates/yok) [INFO] [stdout] [![Rust](https://img.shields.io/badge/rust-1.56.1%2B-blue.svg?maxAge=3600)](https://gitlab.com/andrew_ryan/yok) [INFO] [stdout] [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitlab.com/andrew_ryan/yok/-/raw/master/LICENSE) [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] A crate for Embed the contents of a directory in your binary at compile time. [INFO] [stdout] [INFO] [stdout] ## Getting Started [INFO] [stdout] ```sh [INFO] [stdout] cargo add yok [INFO] [stdout] ``` [INFO] [stdout] ```rust [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::{Dir, Bytes,include_dir}; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] } [INFO] [stdout] ``` [INFO] [stdout] ## Set YOK_PATH env and run or default path is cuttent dir [INFO] [stdout] ```sh [INFO] [stdout] # linux,macos [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] # windows cmd [INFO] [stdout] set YOK_PATH="C:\path\to\your\directory" [INFO] [stdout] # windows powershell [INFO] [stdout] $env:YOK_PATH = "C:\path\to\your\directory" [INFO] [stdout] [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] ``` [INFO] [stdout] [build-dependencies] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] build = "build.rs" [INFO] [stdout] categories = ["development-tools", "game-engines", "web-programming"] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] edition = "2021" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] keywords = ["include", "dir", "compile-time"] [INFO] [stdout] license = "MIT" [INFO] [stdout] name = "yok" [INFO] [stdout] readme = "README.md" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] stderr: [INFO] [stdout] [INFO] [stdout] thread 'main' (134) panicked at /opt/rustwide/workdir/src/lib.rs:236:55: [INFO] [stdout] create to dir error: Os { code: 30, kind: ReadOnlyFilesystem, message: "Read-only file system" } [INFO] [stdout] stack backtrace: [INFO] [stdout] 0: 0x5c1d9ae343b2 - std::backtrace_rs::backtrace::libunwind::trace::hc4a5f428cfb78751 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9 [INFO] [stdout] 1: 0x5c1d9ae343b2 - std::backtrace_rs::backtrace::trace_unsynchronized::h20e1095684b4c296 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14 [INFO] [stdout] 2: 0x5c1d9ae343b2 - std::sys::backtrace::_print_fmt::h461f2e3a8f6b29e2 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:66:9 [INFO] [stdout] 3: 0x5c1d9ae343b2 - ::fmt::h4ee3a75aa71a2c45 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:39:26 [INFO] [stdout] 4: 0x5c1d9ae5094f - core::fmt::rt::Argument::fmt::h6f1564705cd089af [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/rt.rs:173:76 [INFO] [stdout] 5: 0x5c1d9ae5094f - core::fmt::write::h21ca93b65a7c281a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/mod.rs:1468:25 [INFO] [stdout] 6: 0x5c1d9ae26393 - std::io::default_write_fmt::h745a1edf2999cb3b [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:639:11 [INFO] [stdout] 7: 0x5c1d9ae26393 - std::io::Write::write_fmt::hfc56e66d6e189682 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:1954:13 [INFO] [stdout] 8: 0x5c1d9ae349c2 - std::sys::backtrace::BacktraceLock::print::h58d5d73f9e953cf1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:42:9 [INFO] [stdout] 9: 0x5c1d9ae23a1c - std::panicking::default_hook::{{closure}}::h1457fbe47c9457d1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:301:27 [INFO] [stdout] 10: 0x5c1d9ae2385c - std::panicking::default_hook::hccb5e73b206c0830 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:328:9 [INFO] [stdout] 11: 0x5c1d9ae2456a - std::panicking::panic_with_hook::h3190ecc6229cdd29 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:834:13 [INFO] [stdout] 12: 0x5c1d9ae347ca - std::panicking::panic_handler::{{closure}}::ha1f1b769bc2bb40c [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:707:13 [INFO] [stdout] 13: 0x5c1d9ae34729 - std::sys::backtrace::__rust_end_short_backtrace::h5f9cf66f19c2a172 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:174:18 [INFO] [stdout] 14: 0x5c1d9ae2431d - __rustc[a93bd50104b99ad4]::rust_begin_unwind [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:698:5 [INFO] [stdout] 15: 0x5c1d9ae53570 - core::panicking::panic_fmt::hc70c3c83f13c1375 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/panicking.rs:75:14 [INFO] [stdout] 16: 0x5c1d9ae53e36 - core::result::unwrap_failed::ha809bf80017a514a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/result.rs:1852:5 [INFO] [stdout] 17: 0x5c1d9ae0b6c0 - core::result::Result::expect::h8b118d826c5ab3da [INFO] [stdout] 18: 0x5c1d9ae08f88 - yok::Dir::extract::h48506bd16689e8ca [INFO] [stdout] 19: 0x5c1d9ae0b35c - rust_out::main::_doctest_main_src_lib_rs_218_0::ha6e8d018c16d5027 [INFO] [stdout] 20: 0x5c1d9ae0b4e6 - rust_out::main::h61a73a86de211918 [INFO] [stdout] 21: 0x5c1d9ae0aeb3 - core::ops::function::FnOnce::call_once::h6c9c00fd6ee79245 [INFO] [stdout] 22: 0x5c1d9ae0bae6 - std::sys::backtrace::__rust_begin_short_backtrace::h916f7d6bfb06988c [INFO] [stdout] 23: 0x5c1d9ae0b7d9 - std::rt::lang_start::{{closure}}::h8b630493ef2474c7 [INFO] [stderr] error: doctest failed, to rerun pass `--doc` [INFO] [stdout] 24: 0x5c1d9ae35140 - core::ops::function::impls:: for &F>::call_once::hae16bf8966719858 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/ops/function.rs:290:21 [INFO] [stdout] 25: 0x5c1d9ae35140 - std::panicking::catch_unwind::do_call::hd4eb7cf01d0f66cd [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 26: 0x5c1d9ae35140 - std::panicking::catch_unwind::h35b4afc45093edf5 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 27: 0x5c1d9ae35140 - std::panic::catch_unwind::h5a7ef11ac2fa7980 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 28: 0x5c1d9ae35140 - std::rt::lang_start_internal::{{closure}}::h18f8e059e8eb5f5a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:175:24 [INFO] [stdout] 29: 0x5c1d9ae35140 - std::panicking::catch_unwind::do_call::hb4f94f34ea41283f [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 30: 0x5c1d9ae35140 - std::panicking::catch_unwind::hfbb31091ea806204 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 31: 0x5c1d9ae35140 - std::panic::catch_unwind::hc88f452387620cfb [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 32: 0x5c1d9ae35140 - std::rt::lang_start_internal::h23cfb6dcbf9c9010 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:171:5 [INFO] [stdout] 33: 0x5c1d9ae0b7c1 - std::rt::lang_start::h2676fe224d780b30 [INFO] [stdout] 34: 0x5c1d9ae0bb15 - main [INFO] [stdout] 35: 0x757a014cb1ca - [INFO] [stdout] 36: 0x757a014cb28b - __libc_start_main [INFO] [stdout] 37: 0x5c1d9ae075d5 - _start [INFO] [stdout] 38: 0x0 - [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] ---- src/lib.rs - include_dir (line 282) stdout ---- [INFO] [stdout] Test executable failed (exit status: 101). [INFO] [stdout] [INFO] [stdout] stdout: [INFO] [stdout] rm -rf path [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] image: rustlang/rust:nightly [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] push:cargo: [INFO] [stdout] script: [INFO] [stdout] - rustc --version && cargo --version [INFO] [stdout] - cargo publish --allow-dirty --token cioHYTp8cpgbRbBqcJvqGF7rUIjJQSlBvH3 [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] edition = "2021" [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] readme = "README.md" [INFO] [stdout] keywords = ["include","dir","compile-time"] [INFO] [stdout] categories = ["development-tools","game-engines","web-programming"] [INFO] [stdout] license = "MIT" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] build = "build.rs" [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [build-dependencies] [INFO] [stdout] use std::fs::File; [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] use std::io::{BufWriter, Write}; [INFO] [stdout] use std::path::Path; [INFO] [stdout] use zip::write::FileOptions; [INFO] [stdout] use zip::ZipWriter; [INFO] [stdout] use std::io::Read; [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] // Path to the directory to compress [INFO] [stdout] let dir_path = Path::new("."); [INFO] [stdout] [INFO] [stdout] // Path to the output ZIP file [INFO] [stdout] let zip_path = Path::new("app.zip"); [INFO] [stdout] [INFO] [stdout] // Create a new ZIP file [INFO] [stdout] let file = File::create(&zip_path).unwrap(); [INFO] [stdout] let writer = BufWriter::new(file); [INFO] [stdout] let mut zip = ZipWriter::new(writer); [INFO] [stdout] [INFO] [stdout] // Recursively add all files and directories in the input directory to the ZIP file [INFO] [stdout] add_directory_to_zip(&mut zip, dir_path, "").unwrap(); [INFO] [stdout] [INFO] [stdout] // Finalize the ZIP file [INFO] [stdout] zip.finish().unwrap(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Recursively adds a directory (and all its contents) to a ZipWriter [INFO] [stdout] fn add_directory_to_zip(zip: &mut ZipWriter>, dir_path: &Path, base_path: &str) -> zip::result::ZipResult<()> { [INFO] [stdout] for entry in dir_path.read_dir()? { [INFO] [stdout] let path = entry?.path(); [INFO] [stdout] [INFO] [stdout] if path.is_dir() { [INFO] [stdout] // Recursively add the subdirectory [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let new_base_path = format!("{}/{}", base_path, name); [INFO] [stdout] add_directory_to_zip(zip, &path, &new_base_path)?; [INFO] [stdout] } else { [INFO] [stdout] // Add the file to the ZIP archive [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let options = FileOptions::default() [INFO] [stdout] .compression_method(zip::CompressionMethod::Deflated) [INFO] [stdout] .unix_permissions(0o755); // Set appropriate permissions for the file [INFO] [stdout] zip.start_file(format!("{}/{}", base_path, name), options)?; [INFO] [stdout] let mut file = File::open(&path)?; [INFO] [stdout] let mut buffer = Vec::new(); [INFO] [stdout] file.read_to_end(&mut buffer)?; [INFO] [stdout] zip.write_all(&buffer)?; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] MIT License [INFO] [stdout] [INFO] [stdout] Copyright (c) [year] [fullname] [INFO] [stdout] [INFO] [stdout] Permission is hereby granted, free of charge, to any person obtaining a copy [INFO] [stdout] of this software and associated documentation files (the "Software"), to deal [INFO] [stdout] in the Software without restriction, including without limitation the rights [INFO] [stdout] to use, copy, modify, merge, publish, distribute, sublicense, and/or sell [INFO] [stdout] copies of the Software, and to permit persons to whom the Software is [INFO] [stdout] furnished to do so, subject to the following conditions: [INFO] [stdout] [INFO] [stdout] The above copyright notice and this permission notice shall be included in all [INFO] [stdout] copies or substantial portions of the Software. [INFO] [stdout] [INFO] [stdout] THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR [INFO] [stdout] IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, [INFO] [stdout] FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE [INFO] [stdout] AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER [INFO] [stdout] LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, [INFO] [stdout] OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE [INFO] [stdout] SOFTWARE. [INFO] [stdout] use std::{ [INFO] [stdout] fs, io, [INFO] [stdout] path::{Path, PathBuf}, [INFO] [stdout] }; [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] use std::mem; [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] path: String, [INFO] [stdout] is_dir: bool, [INFO] [stdout] is_file: bool, [INFO] [stdout] content: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] data: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.content.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.content.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.content.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] content, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] pub fn walk_dir(path: impl ToString) -> io::Result> { [INFO] [stdout] let path = path.to_string(); [INFO] [stdout] let dir = Path::new(&path); [INFO] [stdout] let mut files = Vec::new(); [INFO] [stdout] for entry in fs::read_dir(dir)? { [INFO] [stdout] let entry = entry?; [INFO] [stdout] let newpath = entry.path(); [INFO] [stdout] [INFO] [stdout] if newpath.is_dir() { [INFO] [stdout] files.extend(walk_dir(&newpath.display())?); [INFO] [stdout] files.push(newpath); [INFO] [stdout] } else { [INFO] [stdout] files.push(newpath); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(files) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] use std::env; [INFO] [stdout] let yok_path = match env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] println!("cargo:rerun-if-env-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed=main.rs"); [INFO] [stdout] println!("cargo:rerun-if-changed=lib.rs"); [INFO] [stdout] let yok_path = std::path::Path::new(&yok_path); [INFO] [stdout] if yok_path.is_dir() { [INFO] [stdout] let mut res_dir: Vec = vec![]; [INFO] [stdout] let dir_list = walk_dir(yok_path.display()).expect("walk dir error"); [INFO] [stdout] for dir in dir_list { [INFO] [stdout] if dir.is_file() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: false, [INFO] [stdout] is_file: true, [INFO] [stdout] content: std::fs::read(dir).expect("read dir error"), [INFO] [stdout] }); [INFO] [stdout] } else if dir.is_dir() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: true, [INFO] [stdout] is_file: false, [INFO] [stdout] content: vec![], [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] let all_dir: Dir = Dir { data: res_dir }; [INFO] [stdout] let all_dir_bytes = all_dir.as_bytes(); [INFO] [stdout] if let Ok(_) = std::fs::write(format!("../.yok"), all_dir_bytes){} [INFO] [stdout] } else { [INFO] [stdout] eprintln!("Environment variable 'YOK_PATH' not dir"); [INFO] [stdout] return; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] # This file is automatically @generated by Cargo. [INFO] [stdout] # It is not intended for manual editing. [INFO] [stdout] version = 3 [INFO] [stdout] [INFO] [stdout] [[package]] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] /target [INFO] [stdout] Cargo.lock [INFO] [stdout] .DS_Store [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::include_dir; [INFO] [stdout] use yok::Bytes; [INFO] [stdout] use yok::Dir; [INFO] [stdout] use yok::DirEntry; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] // println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] // println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] use std::{mem, path::PathBuf, str::FromStr}; [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] pub path: String, [INFO] [stdout] pub is_dir: bool, [INFO] [stdout] pub is_file: bool, [INFO] [stdout] pub contents: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] pub data: Vec, [INFO] [stdout] } [INFO] [stdout] pub trait Bytes { [INFO] [stdout] fn as_bytes(&self) -> Vec; [INFO] [stdout] fn from_bytes(&self) -> Dir; [INFO] [stdout] fn into_bytes(&self) -> Vec; [INFO] [stdout] fn into_dir(&self) -> Dir; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for [u8] { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for Dir { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.contents.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.contents.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.contents.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let contents = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] contents, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] fn get_new_path(path: &str) -> String { [INFO] [stdout] let yok_path = match std::env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] let path = path.to_string().replace("\\", "/").to_string(); [INFO] [stdout] if yok_path != "."{ [INFO] [stdout] return path; [INFO] [stdout] }else { [INFO] [stdout] return path; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] [INFO] [stdout] impl Dir { [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub fn extract(&self, base_dir: impl ToString) ->std::io::Result<()>{ [INFO] [stdout] std::fs::create_dir_all(base_dir.to_string()).expect("create to dir error"); [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_dir { [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] std::fs::create_dir_all(path.clone()).expect("create to dir error"); [INFO] [stdout] if cfg!(target_os = "windows") { [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] }else{ [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_file { [INFO] [stdout] use std::fs::{self, File, OpenOptions}; [INFO] [stdout] use std::io::prelude::*; [INFO] [stdout] use std::os::unix::fs::OpenOptionsExt; [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] if !path.exists(){ [INFO] [stdout] let mut open_options = OpenOptions::new(); [INFO] [stdout] open_options.mode(0o777); // Set read/write permission for owner, read-only for others [INFO] [stdout] let file = open_options.create(true).write(true).open(path).unwrap(); [INFO] [stdout] let mut file_writer = std::io::BufWriter::new(file); [INFO] [stdout] file_writer.write_all(&entry.contents).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub const fn include_dir() -> &'static [u8] { [INFO] [stdout] include_bytes!("../../.yok") [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] src [INFO] [stdout] # yok [INFO] [stdout] [INFO] [stdout] [![Crates.io](https://img.shields.io/crates/v/yok.svg)](https://crates.io/crates/yok) [INFO] [stdout] [![Rust](https://img.shields.io/badge/rust-1.56.1%2B-blue.svg?maxAge=3600)](https://gitlab.com/andrew_ryan/yok) [INFO] [stdout] [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitlab.com/andrew_ryan/yok/-/raw/master/LICENSE) [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] A crate for Embed the contents of a directory in your binary at compile time. [INFO] [stdout] [INFO] [stdout] ## Getting Started [INFO] [stdout] ```sh [INFO] [stdout] cargo add yok [INFO] [stdout] ``` [INFO] [stdout] ```rust [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::{Dir, Bytes,include_dir}; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] } [INFO] [stdout] ``` [INFO] [stdout] ## Set YOK_PATH env and run or default path is cuttent dir [INFO] [stdout] ```sh [INFO] [stdout] # linux,macos [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] # windows cmd [INFO] [stdout] set YOK_PATH="C:\path\to\your\directory" [INFO] [stdout] # windows powershell [INFO] [stdout] $env:YOK_PATH = "C:\path\to\your\directory" [INFO] [stdout] [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] ``` [INFO] [stdout] [build-dependencies] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] build = "build.rs" [INFO] [stdout] categories = ["development-tools", "game-engines", "web-programming"] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] edition = "2021" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] keywords = ["include", "dir", "compile-time"] [INFO] [stdout] license = "MIT" [INFO] [stdout] name = "yok" [INFO] [stdout] readme = "README.md" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] stderr: [INFO] [stdout] [INFO] [stdout] thread 'main' (133) panicked at /opt/rustwide/workdir/src/lib.rs:236:55: [INFO] [stdout] create to dir error: Os { code: 30, kind: ReadOnlyFilesystem, message: "Read-only file system" } [INFO] [stdout] stack backtrace: [INFO] [stdout] 0: 0x59c8dacf43b2 - std::backtrace_rs::backtrace::libunwind::trace::hc4a5f428cfb78751 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9 [INFO] [stdout] 1: 0x59c8dacf43b2 - std::backtrace_rs::backtrace::trace_unsynchronized::h20e1095684b4c296 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14 [INFO] [stdout] 2: 0x59c8dacf43b2 - std::sys::backtrace::_print_fmt::h461f2e3a8f6b29e2 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:66:9 [INFO] [stdout] 3: 0x59c8dacf43b2 - ::fmt::h4ee3a75aa71a2c45 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:39:26 [INFO] [stdout] 4: 0x59c8dad1094f - core::fmt::rt::Argument::fmt::h6f1564705cd089af [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/rt.rs:173:76 [INFO] [stdout] 5: 0x59c8dad1094f - core::fmt::write::h21ca93b65a7c281a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/mod.rs:1468:25 [INFO] [stdout] 6: 0x59c8dace6393 - std::io::default_write_fmt::h745a1edf2999cb3b [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:639:11 [INFO] [stdout] 7: 0x59c8dace6393 - std::io::Write::write_fmt::hfc56e66d6e189682 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:1954:13 [INFO] [stdout] 8: 0x59c8dacf49c2 - std::sys::backtrace::BacktraceLock::print::h58d5d73f9e953cf1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:42:9 [INFO] [stdout] 9: 0x59c8dace3a1c - std::panicking::default_hook::{{closure}}::h1457fbe47c9457d1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:301:27 [INFO] [stdout] 10: 0x59c8dace385c - std::panicking::default_hook::hccb5e73b206c0830 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:328:9 [INFO] [stdout] 11: 0x59c8dace456a - std::panicking::panic_with_hook::h3190ecc6229cdd29 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:834:13 [INFO] [stdout] 12: 0x59c8dacf47ca - std::panicking::panic_handler::{{closure}}::ha1f1b769bc2bb40c [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:707:13 [INFO] [stdout] 13: 0x59c8dacf4729 - std::sys::backtrace::__rust_end_short_backtrace::h5f9cf66f19c2a172 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:174:18 [INFO] [stdout] 14: 0x59c8dace431d - __rustc[a93bd50104b99ad4]::rust_begin_unwind [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:698:5 [INFO] [stdout] 15: 0x59c8dad13570 - core::panicking::panic_fmt::hc70c3c83f13c1375 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/panicking.rs:75:14 [INFO] [stdout] 16: 0x59c8dad13e36 - core::result::unwrap_failed::ha809bf80017a514a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/result.rs:1852:5 [INFO] [stdout] 17: 0x59c8daccb6c0 - core::result::Result::expect::h8b118d826c5ab3da [INFO] [stdout] 18: 0x59c8dacc8f88 - yok::Dir::extract::h48506bd16689e8ca [INFO] [stdout] 19: 0x59c8daccb35c - rust_out::main::_doctest_main_src_lib_rs_282_0::h75241769c97330da [INFO] [stdout] 20: 0x59c8daccb4e6 - rust_out::main::h61a73a86de211918 [INFO] [stdout] 21: 0x59c8daccaeb3 - core::ops::function::FnOnce::call_once::h6c9c00fd6ee79245 [INFO] [stdout] 22: 0x59c8daccbae6 - std::sys::backtrace::__rust_begin_short_backtrace::h916f7d6bfb06988c [INFO] [stdout] 23: 0x59c8daccb7d9 - std::rt::lang_start::{{closure}}::h8b630493ef2474c7 [INFO] [stdout] 24: 0x59c8dacf5140 - core::ops::function::impls:: for &F>::call_once::hae16bf8966719858 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/ops/function.rs:290:21 [INFO] [stdout] 25: 0x59c8dacf5140 - std::panicking::catch_unwind::do_call::hd4eb7cf01d0f66cd [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 26: 0x59c8dacf5140 - std::panicking::catch_unwind::h35b4afc45093edf5 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 27: 0x59c8dacf5140 - std::panic::catch_unwind::h5a7ef11ac2fa7980 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 28: 0x59c8dacf5140 - std::rt::lang_start_internal::{{closure}}::h18f8e059e8eb5f5a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:175:24 [INFO] [stdout] 29: 0x59c8dacf5140 - std::panicking::catch_unwind::do_call::hb4f94f34ea41283f [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 30: 0x59c8dacf5140 - std::panicking::catch_unwind::hfbb31091ea806204 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 31: 0x59c8dacf5140 - std::panic::catch_unwind::hc88f452387620cfb [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 32: 0x59c8dacf5140 - std::rt::lang_start_internal::h23cfb6dcbf9c9010 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:171:5 [INFO] [stdout] 33: 0x59c8daccb7c1 - std::rt::lang_start::h2676fe224d780b30 [INFO] [stdout] 34: 0x59c8daccbb15 - main [INFO] [stdout] 35: 0x782f424911ca - [INFO] [stdout] 36: 0x782f4249128b - __libc_start_main [INFO] [stdout] 37: 0x59c8dacc75d5 - _start [INFO] [stdout] 38: 0x0 - [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] ---- src/lib.rs - DirEntry (line 10) stdout ---- [INFO] [stdout] Test executable failed (exit status: 101). [INFO] [stdout] [INFO] [stdout] stdout: [INFO] [stdout] rm -rf path [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] image: rustlang/rust:nightly [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] push:cargo: [INFO] [stdout] script: [INFO] [stdout] - rustc --version && cargo --version [INFO] [stdout] - cargo publish --allow-dirty --token cioHYTp8cpgbRbBqcJvqGF7rUIjJQSlBvH3 [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] edition = "2021" [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] readme = "README.md" [INFO] [stdout] keywords = ["include","dir","compile-time"] [INFO] [stdout] categories = ["development-tools","game-engines","web-programming"] [INFO] [stdout] license = "MIT" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] build = "build.rs" [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [build-dependencies] [INFO] [stdout] use std::fs::File; [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] use std::io::{BufWriter, Write}; [INFO] [stdout] use std::path::Path; [INFO] [stdout] use zip::write::FileOptions; [INFO] [stdout] use zip::ZipWriter; [INFO] [stdout] use std::io::Read; [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] // Path to the directory to compress [INFO] [stdout] let dir_path = Path::new("."); [INFO] [stdout] [INFO] [stdout] // Path to the output ZIP file [INFO] [stdout] let zip_path = Path::new("app.zip"); [INFO] [stdout] [INFO] [stdout] // Create a new ZIP file [INFO] [stdout] let file = File::create(&zip_path).unwrap(); [INFO] [stdout] let writer = BufWriter::new(file); [INFO] [stdout] let mut zip = ZipWriter::new(writer); [INFO] [stdout] [INFO] [stdout] // Recursively add all files and directories in the input directory to the ZIP file [INFO] [stdout] add_directory_to_zip(&mut zip, dir_path, "").unwrap(); [INFO] [stdout] [INFO] [stdout] // Finalize the ZIP file [INFO] [stdout] zip.finish().unwrap(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Recursively adds a directory (and all its contents) to a ZipWriter [INFO] [stdout] fn add_directory_to_zip(zip: &mut ZipWriter>, dir_path: &Path, base_path: &str) -> zip::result::ZipResult<()> { [INFO] [stdout] for entry in dir_path.read_dir()? { [INFO] [stdout] let path = entry?.path(); [INFO] [stdout] [INFO] [stdout] if path.is_dir() { [INFO] [stdout] // Recursively add the subdirectory [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let new_base_path = format!("{}/{}", base_path, name); [INFO] [stdout] add_directory_to_zip(zip, &path, &new_base_path)?; [INFO] [stdout] } else { [INFO] [stdout] // Add the file to the ZIP archive [INFO] [stdout] let name = path.file_name().unwrap().to_str().unwrap(); [INFO] [stdout] let options = FileOptions::default() [INFO] [stdout] .compression_method(zip::CompressionMethod::Deflated) [INFO] [stdout] .unix_permissions(0o755); // Set appropriate permissions for the file [INFO] [stdout] zip.start_file(format!("{}/{}", base_path, name), options)?; [INFO] [stdout] let mut file = File::open(&path)?; [INFO] [stdout] let mut buffer = Vec::new(); [INFO] [stdout] file.read_to_end(&mut buffer)?; [INFO] [stdout] zip.write_all(&buffer)?; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] MIT License [INFO] [stdout] [INFO] [stdout] Copyright (c) [year] [fullname] [INFO] [stdout] [INFO] [stdout] Permission is hereby granted, free of charge, to any person obtaining a copy [INFO] [stdout] of this software and associated documentation files (the "Software"), to deal [INFO] [stdout] in the Software without restriction, including without limitation the rights [INFO] [stdout] to use, copy, modify, merge, publish, distribute, sublicense, and/or sell [INFO] [stdout] copies of the Software, and to permit persons to whom the Software is [INFO] [stdout] furnished to do so, subject to the following conditions: [INFO] [stdout] [INFO] [stdout] The above copyright notice and this permission notice shall be included in all [INFO] [stdout] copies or substantial portions of the Software. [INFO] [stdout] [INFO] [stdout] THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR [INFO] [stdout] IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, [INFO] [stdout] FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE [INFO] [stdout] AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER [INFO] [stdout] LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, [INFO] [stdout] OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE [INFO] [stdout] SOFTWARE. [INFO] [stdout] use std::{ [INFO] [stdout] fs, io, [INFO] [stdout] path::{Path, PathBuf}, [INFO] [stdout] }; [INFO] [stdout] [INFO] [stdout] fn main() { [INFO] [stdout] use std::mem; [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] path: String, [INFO] [stdout] is_dir: bool, [INFO] [stdout] is_file: bool, [INFO] [stdout] content: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] data: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.content.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.content.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.content.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] content, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] pub fn walk_dir(path: impl ToString) -> io::Result> { [INFO] [stdout] let path = path.to_string(); [INFO] [stdout] let dir = Path::new(&path); [INFO] [stdout] let mut files = Vec::new(); [INFO] [stdout] for entry in fs::read_dir(dir)? { [INFO] [stdout] let entry = entry?; [INFO] [stdout] let newpath = entry.path(); [INFO] [stdout] [INFO] [stdout] if newpath.is_dir() { [INFO] [stdout] files.extend(walk_dir(&newpath.display())?); [INFO] [stdout] files.push(newpath); [INFO] [stdout] } else { [INFO] [stdout] files.push(newpath); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(files) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] use std::env; [INFO] [stdout] let yok_path = match env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] println!("cargo:rerun-if-env-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed={}", yok_path); [INFO] [stdout] println!("cargo:rerun-if-changed=main.rs"); [INFO] [stdout] println!("cargo:rerun-if-changed=lib.rs"); [INFO] [stdout] let yok_path = std::path::Path::new(&yok_path); [INFO] [stdout] if yok_path.is_dir() { [INFO] [stdout] let mut res_dir: Vec = vec![]; [INFO] [stdout] let dir_list = walk_dir(yok_path.display()).expect("walk dir error"); [INFO] [stdout] for dir in dir_list { [INFO] [stdout] if dir.is_file() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: false, [INFO] [stdout] is_file: true, [INFO] [stdout] content: std::fs::read(dir).expect("read dir error"), [INFO] [stdout] }); [INFO] [stdout] } else if dir.is_dir() { [INFO] [stdout] res_dir.push(DirEntry { [INFO] [stdout] path: dir.strip_prefix(yok_path).unwrap().display().to_string(), [INFO] [stdout] is_dir: true, [INFO] [stdout] is_file: false, [INFO] [stdout] content: vec![], [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] let all_dir: Dir = Dir { data: res_dir }; [INFO] [stdout] let all_dir_bytes = all_dir.as_bytes(); [INFO] [stdout] if let Ok(_) = std::fs::write(format!("../.yok"), all_dir_bytes){} [INFO] [stdout] } else { [INFO] [stdout] eprintln!("Environment variable 'YOK_PATH' not dir"); [INFO] [stdout] return; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] # This file is automatically @generated by Cargo. [INFO] [stdout] # It is not intended for manual editing. [INFO] [stdout] version = 3 [INFO] [stdout] [INFO] [stdout] [[package]] [INFO] [stdout] name = "yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] /target [INFO] [stdout] Cargo.lock [INFO] [stdout] .DS_Store [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::include_dir; [INFO] [stdout] use yok::Bytes; [INFO] [stdout] use yok::Dir; [INFO] [stdout] use yok::DirEntry; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] // println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] // println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] use std::{mem, path::PathBuf, str::FromStr}; [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct DirEntry { [INFO] [stdout] pub path: String, [INFO] [stdout] pub is_dir: bool, [INFO] [stdout] pub is_file: bool, [INFO] [stdout] pub contents: Vec, [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] #[derive(Debug, Clone, PartialEq, Eq)] [INFO] [stdout] pub struct Dir { [INFO] [stdout] pub data: Vec, [INFO] [stdout] } [INFO] [stdout] pub trait Bytes { [INFO] [stdout] fn as_bytes(&self) -> Vec; [INFO] [stdout] fn from_bytes(&self) -> Dir; [INFO] [stdout] fn into_bytes(&self) -> Vec; [INFO] [stdout] fn into_dir(&self) -> Dir; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for [u8] { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] self.to_vec() [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] Dir::from_bytes(self).expect("from bytes error") [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] impl Bytes for Dir { [INFO] [stdout] fn as_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] fn into_bytes(&self) -> Vec { [INFO] [stdout] Dir::as_bytes(&self) [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] fn from_bytes(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] fn into_dir(&self) -> Dir { [INFO] [stdout] self.clone() [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement as_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn as_bytes(&self) -> Vec { [INFO] [stdout] // Calculate the total size of the serialized data [INFO] [stdout] let mut size = mem::size_of::(); // Size of the length prefix [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] size += mem::size_of::(); // Size of the entry size prefix [INFO] [stdout] size += entry.path.as_bytes().len(); [INFO] [stdout] size += mem::size_of::() * 2; // Sizes of is_dir and is_file fields [INFO] [stdout] size += mem::size_of::(); // Size of content size prefix [INFO] [stdout] size += entry.contents.len(); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let mut bytes = Vec::with_capacity(size); [INFO] [stdout] [INFO] [stdout] // Write the length prefix [INFO] [stdout] bytes.extend((self.data.len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] for entry in &self.data { [INFO] [stdout] // Write the size prefix for this entry [INFO] [stdout] bytes.extend((entry.path.as_bytes().len() as u32).to_le_bytes().iter()); [INFO] [stdout] [INFO] [stdout] // Write the entry data [INFO] [stdout] bytes.extend(entry.path.as_bytes().iter()); [INFO] [stdout] bytes.extend(&[entry.is_dir as u8, entry.is_file as u8]); [INFO] [stdout] bytes.extend((entry.contents.len() as u32).to_le_bytes().iter()); [INFO] [stdout] bytes.extend(entry.contents.iter()); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] bytes [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] // Implement from_bytes() for Dir [INFO] [stdout] impl Dir { [INFO] [stdout] pub fn from_bytes(bytes: &[u8]) -> Option { [INFO] [stdout] let mut cursor = 0; [INFO] [stdout] let len_prefix_size = mem::size_of::(); [INFO] [stdout] [INFO] [stdout] if bytes.len() < len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let len = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let mut data = Vec::with_capacity(len); [INFO] [stdout] [INFO] [stdout] for _ in 0..len { [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let entry_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] let end_pos = cursor + entry_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < end_pos { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let path = String::from_utf8(bytes[cursor..end_pos].to_vec()).ok()?; [INFO] [stdout] cursor = end_pos; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + 2 { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let is_dir = bytes[cursor] != 0; [INFO] [stdout] let is_file = bytes[cursor + 1] != 0; [INFO] [stdout] cursor += 2; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + len_prefix_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let content_size = u32::from_le_bytes([ [INFO] [stdout] bytes[cursor], [INFO] [stdout] bytes[cursor + 1], [INFO] [stdout] bytes[cursor + 2], [INFO] [stdout] bytes[cursor + 3], [INFO] [stdout] ]) as usize; [INFO] [stdout] [INFO] [stdout] cursor += len_prefix_size; [INFO] [stdout] [INFO] [stdout] if bytes.len() < cursor + content_size { [INFO] [stdout] return None; [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] let contents = bytes[cursor..cursor + content_size].to_vec(); [INFO] [stdout] cursor += content_size; [INFO] [stdout] [INFO] [stdout] data.push(DirEntry { [INFO] [stdout] path, [INFO] [stdout] is_dir, [INFO] [stdout] is_file, [INFO] [stdout] contents, [INFO] [stdout] }); [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] Some(Dir { data }) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] fn get_new_path(path: &str) -> String { [INFO] [stdout] let yok_path = match std::env::var("YOK_PATH") { [INFO] [stdout] Ok(value) => value, [INFO] [stdout] Err(_) => ".".to_string(), [INFO] [stdout] }; [INFO] [stdout] let path = path.to_string().replace("\\", "/").to_string(); [INFO] [stdout] if yok_path != "."{ [INFO] [stdout] return path; [INFO] [stdout] }else { [INFO] [stdout] return path; [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] [INFO] [stdout] impl Dir { [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub fn extract(&self, base_dir: impl ToString) ->std::io::Result<()>{ [INFO] [stdout] std::fs::create_dir_all(base_dir.to_string()).expect("create to dir error"); [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_dir { [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] std::fs::create_dir_all(path.clone()).expect("create to dir error"); [INFO] [stdout] if cfg!(target_os = "windows") { [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] }else{ [INFO] [stdout] use std::os::unix::fs::PermissionsExt; [INFO] [stdout] let permissions = std::fs::Permissions::from_mode(0o777); [INFO] [stdout] std::fs::set_permissions(path.clone(), permissions).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] for entry in self.data.clone().into_iter() { [INFO] [stdout] if entry.is_file { [INFO] [stdout] use std::fs::{self, File, OpenOptions}; [INFO] [stdout] use std::io::prelude::*; [INFO] [stdout] use std::os::unix::fs::OpenOptionsExt; [INFO] [stdout] let path = PathBuf::from_str(&base_dir.to_string()).expect("parse to to PathBuf error"); [INFO] [stdout] let path = path.join(get_new_path(&entry.path.to_string())); [INFO] [stdout] if !path.exists(){ [INFO] [stdout] let mut open_options = OpenOptions::new(); [INFO] [stdout] open_options.mode(0o777); // Set read/write permission for owner, read-only for others [INFO] [stdout] let file = open_options.create(true).write(true).open(path).unwrap(); [INFO] [stdout] let mut file_writer = std::io::BufWriter::new(file); [INFO] [stdout] file_writer.write_all(&entry.contents).unwrap(); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] Ok(()) [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] /// include dir at compile time [INFO] [stdout] /// in default die path is current dir [INFO] [stdout] /// [INFO] [stdout] /// `export YOK_PATH="/home/andrew/code/gitlab/test_code/"` [INFO] [stdout] /// [INFO] [stdout] /// you can also set YOK_PATH env before cargo run [INFO] [stdout] /// [INFO] [stdout] /// If the dir path is not the path you set, run `cargo clean` before `cargo run` [INFO] [stdout] /// ```rust [INFO] [stdout] /// use yok::include_dir; [INFO] [stdout] /// use yok::Dir; [INFO] [stdout] /// use yok::DirEntry; [INFO] [stdout] /// use yok::Bytes; [INFO] [stdout] /// const DATA:&[u8] = include_dir(); [INFO] [stdout] /// let dir:Dir = DATA.into_dir(); [INFO] [stdout] /// for entry in &dir.data{ [INFO] [stdout] /// if entry.is_file{ [INFO] [stdout] /// println!("{}",String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] /// }else if entry.is_dir { [INFO] [stdout] /// println!("{}",entry.path); [INFO] [stdout] /// } [INFO] [stdout] /// } [INFO] [stdout] /// dir.extract("./demo"); [INFO] [stdout] /// ``` [INFO] [stdout] /// [INFO] [stdout] pub const fn include_dir() -> &'static [u8] { [INFO] [stdout] include_bytes!("../../.yok") [INFO] [stdout] } [INFO] [stdout] [INFO] [stdout] src [INFO] [stdout] # yok [INFO] [stdout] [INFO] [stdout] [![Crates.io](https://img.shields.io/crates/v/yok.svg)](https://crates.io/crates/yok) [INFO] [stdout] [![Rust](https://img.shields.io/badge/rust-1.56.1%2B-blue.svg?maxAge=3600)](https://gitlab.com/andrew_ryan/yok) [INFO] [stdout] [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitlab.com/andrew_ryan/yok/-/raw/master/LICENSE) [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] A crate for Embed the contents of a directory in your binary at compile time. [INFO] [stdout] [INFO] [stdout] ## Getting Started [INFO] [stdout] ```sh [INFO] [stdout] cargo add yok [INFO] [stdout] ``` [INFO] [stdout] ```rust [INFO] [stdout] #[allow(warnings)] [INFO] [stdout] fn main() { [INFO] [stdout] use yok::{Dir, Bytes,include_dir}; [INFO] [stdout] const DATA: &[u8] = include_dir(); [INFO] [stdout] let dir: Dir = DATA.into_dir(); [INFO] [stdout] for entry in &dir.data { [INFO] [stdout] if entry.is_file { [INFO] [stdout] println!("{}", String::from_utf8_lossy(&entry.contents)); [INFO] [stdout] } else if entry.is_dir { [INFO] [stdout] println!("{}", entry.path); [INFO] [stdout] } [INFO] [stdout] } [INFO] [stdout] dir.extract("./path"); [INFO] [stdout] } [INFO] [stdout] ``` [INFO] [stdout] ## Set YOK_PATH env and run or default path is cuttent dir [INFO] [stdout] ```sh [INFO] [stdout] # linux,macos [INFO] [stdout] export YOK_PATH="/home/andrew/code/gitlab/test_code/" [INFO] [stdout] # windows cmd [INFO] [stdout] set YOK_PATH="C:\path\to\your\directory" [INFO] [stdout] # windows powershell [INFO] [stdout] $env:YOK_PATH = "C:\path\to\your\directory" [INFO] [stdout] [INFO] [stdout] cargo clean [INFO] [stdout] cargo r [INFO] [stdout] ``` [INFO] [stdout] [build-dependencies] [INFO] [stdout] [INFO] [stdout] [dependencies] [INFO] [stdout] [INFO] [stdout] [package] [INFO] [stdout] authors = ["Anonymous "] [INFO] [stdout] build = "build.rs" [INFO] [stdout] categories = ["development-tools", "game-engines", "web-programming"] [INFO] [stdout] description = "Embed the contents of a directory in your binary" [INFO] [stdout] documentation = "https://docs.rs/yok" [INFO] [stdout] edition = "2021" [INFO] [stdout] homepage = "https://dnrops.gitee.io" [INFO] [stdout] keywords = ["include", "dir", "compile-time"] [INFO] [stdout] license = "MIT" [INFO] [stdout] name = "yok" [INFO] [stdout] readme = "README.md" [INFO] [stdout] repository = "https://gitlab.com/andrew_ryan/yok" [INFO] [stdout] version = "0.1.9" [INFO] [stdout] [INFO] [stdout] [package.metadata.docs.rs] [INFO] [stdout] rustc-args = ["--cfg", "docsrs"] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] stderr: [INFO] [stdout] [INFO] [stdout] thread 'main' (135) panicked at /opt/rustwide/workdir/src/lib.rs:236:55: [INFO] [stdout] create to dir error: Os { code: 30, kind: ReadOnlyFilesystem, message: "Read-only file system" } [INFO] [stdout] stack backtrace: [INFO] [stdout] 0: 0x607f6d4cb3b2 - std::backtrace_rs::backtrace::libunwind::trace::hc4a5f428cfb78751 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9 [INFO] [stdout] 1: 0x607f6d4cb3b2 - std::backtrace_rs::backtrace::trace_unsynchronized::h20e1095684b4c296 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14 [INFO] [stdout] 2: 0x607f6d4cb3b2 - std::sys::backtrace::_print_fmt::h461f2e3a8f6b29e2 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:66:9 [INFO] [stdout] 3: 0x607f6d4cb3b2 - ::fmt::h4ee3a75aa71a2c45 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:39:26 [INFO] [stdout] 4: 0x607f6d4e794f - core::fmt::rt::Argument::fmt::h6f1564705cd089af [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/rt.rs:173:76 [INFO] [stdout] 5: 0x607f6d4e794f - core::fmt::write::h21ca93b65a7c281a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/fmt/mod.rs:1468:25 [INFO] [stdout] 6: 0x607f6d4bd393 - std::io::default_write_fmt::h745a1edf2999cb3b [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:639:11 [INFO] [stdout] 7: 0x607f6d4bd393 - std::io::Write::write_fmt::hfc56e66d6e189682 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/io/mod.rs:1954:13 [INFO] [stdout] 8: 0x607f6d4cb9c2 - std::sys::backtrace::BacktraceLock::print::h58d5d73f9e953cf1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:42:9 [INFO] [stdout] 9: 0x607f6d4baa1c - std::panicking::default_hook::{{closure}}::h1457fbe47c9457d1 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:301:27 [INFO] [stdout] 10: 0x607f6d4ba85c - std::panicking::default_hook::hccb5e73b206c0830 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:328:9 [INFO] [stdout] 11: 0x607f6d4bb56a - std::panicking::panic_with_hook::h3190ecc6229cdd29 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:834:13 [INFO] [stdout] 12: 0x607f6d4cb7ca - std::panicking::panic_handler::{{closure}}::ha1f1b769bc2bb40c [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:707:13 [INFO] [stdout] 13: 0x607f6d4cb729 - std::sys::backtrace::__rust_end_short_backtrace::h5f9cf66f19c2a172 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/sys/backtrace.rs:174:18 [INFO] [stdout] 14: 0x607f6d4bb31d - __rustc[a93bd50104b99ad4]::rust_begin_unwind [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:698:5 [INFO] [stdout] 15: 0x607f6d4ea570 - core::panicking::panic_fmt::hc70c3c83f13c1375 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/panicking.rs:75:14 [INFO] [stdout] 16: 0x607f6d4eae36 - core::result::unwrap_failed::ha809bf80017a514a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/result.rs:1852:5 [INFO] [stdout] 17: 0x607f6d4a26c0 - core::result::Result::expect::h8b118d826c5ab3da [INFO] [stdout] 18: 0x607f6d49ff88 - yok::Dir::extract::h48506bd16689e8ca [INFO] [stdout] 19: 0x607f6d4a235c - rust_out::main::_doctest_main_src_lib_rs_10_0::h61e8ec329afaf420 [INFO] [stdout] 20: 0x607f6d4a24e6 - rust_out::main::h61a73a86de211918 [INFO] [stdout] 21: 0x607f6d4a1eb3 - core::ops::function::FnOnce::call_once::h6c9c00fd6ee79245 [INFO] [stdout] 22: 0x607f6d4a2ae6 - std::sys::backtrace::__rust_begin_short_backtrace::h916f7d6bfb06988c [INFO] [stdout] 23: 0x607f6d4a27d9 - std::rt::lang_start::{{closure}}::h8b630493ef2474c7 [INFO] [stdout] 24: 0x607f6d4cc140 - core::ops::function::impls:: for &F>::call_once::hae16bf8966719858 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/core/src/ops/function.rs:290:21 [INFO] [stdout] 25: 0x607f6d4cc140 - std::panicking::catch_unwind::do_call::hd4eb7cf01d0f66cd [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 26: 0x607f6d4cc140 - std::panicking::catch_unwind::h35b4afc45093edf5 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 27: 0x607f6d4cc140 - std::panic::catch_unwind::h5a7ef11ac2fa7980 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 28: 0x607f6d4cc140 - std::rt::lang_start_internal::{{closure}}::h18f8e059e8eb5f5a [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:175:24 [INFO] [stdout] 29: 0x607f6d4cc140 - std::panicking::catch_unwind::do_call::hb4f94f34ea41283f [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:590:40 [INFO] [stdout] 30: 0x607f6d4cc140 - std::panicking::catch_unwind::hfbb31091ea806204 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panicking.rs:553:19 [INFO] [stdout] 31: 0x607f6d4cc140 - std::panic::catch_unwind::hc88f452387620cfb [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/panic.rs:359:14 [INFO] [stdout] 32: 0x607f6d4cc140 - std::rt::lang_start_internal::h23cfb6dcbf9c9010 [INFO] [stdout] at /rustc/b6ae10aa7319b7ebb6c7b3331dd71a9d6c9c5b4b/library/std/src/rt.rs:171:5 [INFO] [stdout] 33: 0x607f6d4a27c1 - std::rt::lang_start::h2676fe224d780b30 [INFO] [stdout] 34: 0x607f6d4a2b15 - main [INFO] [stdout] 35: 0x7b8d61b021ca - [INFO] [stdout] 36: 0x7b8d61b0228b - __libc_start_main [INFO] [stdout] 37: 0x607f6d49e5d5 - _start [INFO] [stdout] 38: 0x0 - [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] [INFO] [stdout] failures: [INFO] [stdout] src/lib.rs - Dir::extract (line 218) [INFO] [stdout] src/lib.rs - DirEntry (line 10) [INFO] [stdout] src/lib.rs - include_dir (line 282) [INFO] [stdout] [INFO] [stdout] test result: FAILED. 0 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.77s [INFO] [stdout] [INFO] running `Command { std: "docker" "inspect" "8904e9d24b667eaa29fa4673a8c7a80a614852a10280e2c634184af6e7ae4e2b", kill_on_drop: false }` [INFO] running `Command { std: "docker" "rm" "-f" "8904e9d24b667eaa29fa4673a8c7a80a614852a10280e2c634184af6e7ae4e2b", kill_on_drop: false }` [INFO] [stdout] 8904e9d24b667eaa29fa4673a8c7a80a614852a10280e2c634184af6e7ae4e2b