diff --git a/docs/tauri/0.prerequisites/windows.md b/docs/tauri/0.prerequisites/windows.md index 8a91678..2aad18d 100644 --- a/docs/tauri/0.prerequisites/windows.md +++ b/docs/tauri/0.prerequisites/windows.md @@ -9,6 +9,7 @@ title: Windows ## **Step 1: Install Required Software** +### If you do not have a packate manager & JS/TS runtime (node, npm) ### 1. Install Deno (JavaScript/TypeScript Runtime) - **Website**: [https://deno.land/](https://deno.land/) diff --git a/docs/tauri/2.build-your-own-text-editor/0.rust-fundamentals.md b/docs/tauri/2.build-your-own-text-editor/0.rust-fundamentals.md new file mode 100644 index 0000000..0e389ca --- /dev/null +++ b/docs/tauri/2.build-your-own-text-editor/0.rust-fundamentals.md @@ -0,0 +1,103 @@ +--- +title: Rust Essentials +--- + +# πŸ› οΈ Rust Essentials: The Foundation + +**"Before we build the editor, we must calibrate our tools."** + +This section covers the essential setup and the two specific conceptsβ€”**Error Handling** and **Pattern Matching**β€”that are required to complete the upcoming tasks. We will establish a workflow that ensures memory safety without the manual overhead typical of older systems languages. + +--- + +### **1. The Build System: Cargo** + +In languages like C or C++, setting up a project often involves configuring build systems (Make, CMake) and package managers manually. Rust consolidates the compiler (`rustc`), build system, and dependency manager into a single tool: **Cargo**. + +1. **Verify Installation:** + Open your terminal and run: `cargo --version` +2. **Create a Sandbox Project:** + We will create a temporary project to test our logic before adding it to the Tauri app. + ```bash + cargo new rust-warmup + cd rust-warmup + ``` + +### **2. The Concept: The `Result` Type** + +In C or C++, system calls (like opening a file) often return an integer status code (e.g., `-1` for failure) or throw an exception. This requires the developer to remember to check that specific integer or wrap code in try/catch blocks. + +Rust handles this differently to prevent undefined behavior. It uses the **`Result`** type. + +A `Result` is an enum that contains **one** of two possible values: + +- **`Ok(T)`**: The operation succeeded, containing the data (`T`). +- **`Err(E)`**: The operation failed, containing the error details (`E`). + +You **cannot** access the data inside `Ok` without first handling the possibility of `Err`. This ensures that I/O errors are never silently ignored. + +### **3. The Exercise: Safe File Reading** + +Open `src/main.rs` in your `rust-warmup` folder. We will write a function that attempts to read a file that does not exist to demonstrate how Rust forces safety. + +**Step 1: Import the filesystem module** + +```rust +use std::fs; +``` + +**Step 2: Implement the Logic** +We will use **`match`**, which is similar to a `switch` statement in C++, but with a critical difference: it is **exhaustive**. The compiler will refuse to build your code if you do not handle every possible outcome (Success and Failure). + +```rust +use std::fs; + +fn main() { + // Try to read a file that definitely doesn't exist + let filename = "ghost_file.txt"; + + // This call does NOT return a String. + // It returns a Result + let result = fs::read_to_string(filename); + + // We must 'unwrap' the Result to get the value + match result { + Ok(content) => { + // This block executes ONLY if the OS successfully read the file + println!("File content: {}", content); + }, + Err(error) => { + // This block executes if ANY system error occurred (Permission denied, Not found, etc.) + // In C++, this might have caused a crash if not caught. + println!("⚠️ Error reading '{}'", filename); + println!("System Message: {}", error); + } + } +} +``` + +### **4. Execution** + +Run the project: + +```bash +cargo run +``` + +**Expected Output:** + +```text +⚠️ Error reading 'ghost_file.txt' +System Message: The system cannot find the file specified. (os error 2) +``` + +### **Relevance to the Course** + +In the upcoming Text Editor tasks, every interaction between your UI and the Operating System will follow this pattern: + +1. Attempt an OS action (Read/Write). +2. Receive a `Result`. +3. Match the result to send either **Data** or an **Error Message** back to the user. + +**"Safety is not an option you enable; it is the default state of the language."** +_Ready? Let's build the editor._ πŸš€ diff --git a/docs/tauri/2.build-your-own-text-editor/1.tauri-arhitecture.md b/docs/tauri/2.build-your-own-text-editor/1.tauri-arhitecture.md new file mode 100644 index 0000000..1a825e3 --- /dev/null +++ b/docs/tauri/2.build-your-own-text-editor/1.tauri-arhitecture.md @@ -0,0 +1,135 @@ +--- +title: Tauri Architecture +--- + +# Tauri Architecture: The Rust-TS Bridge + +**"A bridge isn't just a path; it's a contract between two distinct lands."** + +You have calibrated your Rust tools. Now, we must understand the "map" of a Tauri application. + +A Tauri app is **not** one single program. It is two applications running in harmony: + +1. **The Backend (Rust):** A native, high-performance system process. This is the "Engine." +2. **The Frontend (WebView):** A modern web browser view running your HTML, CSS, and TypeScript. This is the "Cockpit." + +This document explains the "medium" or "bridge" that allows them to communicate. + +----- + +### **1. The Project Structure** + +When you create a Tauri project, you will see two primary folders. Understanding this separation is key. + +```plaintext +my-text-editor/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ main.ts # Your Frontend (TypeScript) +β”‚ β”œβ”€β”€ index.html # Your Frontend (HTML) +β”‚ └── ... # (CSS, other TS files) +β”‚ +β”œβ”€β”€ src-tauri/ +β”‚ β”œβ”€β”€ src/ +β”‚ β”‚ └── main.rs # Your Backend (Rust) +β”‚ β”œβ”€β”€ Cargo.toml # Your Backend's dependencies +β”‚ └── tauri.conf.json # The "Manifest" that links them +β”‚ +└── ... +``` + + * **`src/`**: This is a standard web project. It has no access to the filesystem or any native features *until* you ask Rust for them. + * **`src-tauri/`**: This is a standard Rust project. It has no UI *until* you attach the WebView to it. + * **`tauri.conf.json`**: This file is the architect. It defines the app's window, sets security permissions, and tells the Rust backend which frontend to load. + +----- + +### **2. The Backend: Exposing a Command** + +You cannot call a Rust function from TypeScript directly. You must "expose" it as a **Tauri Command**. + +This is done with a macro: `#[tauri::command]`. + +Let's look at a simple example in `src-tauri/src/main.rs`. We'll use the `Result` type you learned about in the "Rust Essentials" module. + +```rust +// In src-tauri/src/main.rs + +// This macro transforms the Rust function into something Tauri can manage +#[tauri::command] +fn greet(name: String) -> Result { + if name.is_empty() { + // If we return an Err, it rejects the promise in TypeScript + Err("Name cannot be empty!".to_string()) + } else { + // If we return Ok, it resolves the promise in TypeScript + Ok(format!("Hello, {}!", name)) + } +} +``` + +----- + +### **3. The Handler: The Rust "Switchboard"** + +Just defining the command isn't enough. We must register it with Tauri's **Invoke Handler** in our `main()` function. + +Think of the handler as a "switchboard" or "function router." It listens for string-based messages from the frontend and routes them to the correct Rust function. + +```rust +// In src-tauri/src/main.rs + +fn main() { + tauri::Builder::default() + // This line registers our 'greet' command + .invoke_handler(tauri::generate_handler![ + greet + // All other commands you write will be listed here + ]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +``` + +----- + +### **4. The Frontend: Calling a Command** + +Now for the final piece: calling our `greet` command from TypeScript. We use the **`invoke`** function from the `@tauri-apps/api` library. + +`invoke` is an **async** function. It sends a message to Rust and waits for a response. + +```typescript +// In src/main.ts +import { invoke } from '@tauri-apps/api'; + +async function sayHello() { + try { + // 1. We call 'invoke' with the Rust function's name (snake_case) + // 2. We pass arguments in an object (camelCase keys) + const response: string = await invoke('greet', { name: 'World' }); + + // This runs if Rust returned Ok() + console.log(response); // "Hello, World!" + + } catch (error) { + // This runs if Rust returned Err() + console.error(error); // "Name cannot be empty!" + } +} + +// Example of the error case +sayHello(); +await invoke('greet', { name: '' }); // This will trigger the catch block +``` + +### **The Contract: `Result` ↔ `Promise`** + +You have just seen the "medium" in action. It's a simple, powerful contract: + +> * A Rust `Result::Ok(value)` **resolves** the JavaScript `Promise(value)`. +> * A Rust `Result::Err(error)` **rejects** the JavaScript `Promise(error)`. + +This allows you to write safe, robust Rust code (as seen in "Rust Essentials") and handle errors gracefully in your frontend using standard `try...catch` blocks. + +**"The bridge is built. Now, let's send traffic."** +*Ready to build the editor's core?* πŸ”§ \ No newline at end of file diff --git a/docs/tauri/2.build-your-own-text-editor/1.core-funtionalities.md b/docs/tauri/2.build-your-own-text-editor/2.core-funtionalities.md similarity index 100% rename from docs/tauri/2.build-your-own-text-editor/1.core-funtionalities.md rename to docs/tauri/2.build-your-own-text-editor/2.core-funtionalities.md diff --git a/docs/tauri/2.build-your-own-text-editor/2.file-explorer-sidebar.md b/docs/tauri/2.build-your-own-text-editor/3.file-explorer-sidebar.md similarity index 100% rename from docs/tauri/2.build-your-own-text-editor/2.file-explorer-sidebar.md rename to docs/tauri/2.build-your-own-text-editor/3.file-explorer-sidebar.md diff --git a/docs/tauri/2.build-your-own-text-editor/3.create-files.md b/docs/tauri/2.build-your-own-text-editor/4.create-files.md similarity index 100% rename from docs/tauri/2.build-your-own-text-editor/3.create-files.md rename to docs/tauri/2.build-your-own-text-editor/4.create-files.md diff --git a/docs/tauri/2.build-your-own-text-editor/5.showing-properties.md b/docs/tauri/2.build-your-own-text-editor/5.showing-properties.md new file mode 100644 index 0000000..c2e7d43 --- /dev/null +++ b/docs/tauri/2.build-your-own-text-editor/5.showing-properties.md @@ -0,0 +1,90 @@ +--- +title: The System Inspector +--- + +# Task 4: The System Inspector + +--- + +_"Reading the text is simple. Reading the system attributes is where engineering begins."_ + +In standard C/C++, accessing file metadata (size, permissions, timestamps) involves using the `stat` family of structs and system calls, which can vary significantly between Windows and Linux/macOS. + +Rust abstracts this via `std::fs::metadata`, but introduces a new challenge: **Data Serialization**. + +**Your Mission:** Add a "File Info" panel that displays system-level metadata for the currently open file. + +--- + +### **1. The Concept: Struct Serialization** + +To send a complex C-style struct from Rust to a JavaScript frontend, we cannot simply pass the memory address. We must serialize the data into a format the frontend understands (JSON). + +Rust uses a standard crate for this called **Serde** (SERializer/DEserializer). + +1. Open `Cargo.toml` and add `serde` to your dependencies: + ```toml + [dependencies] + serde = { version = "1.0", features = ["derive"] } + # ... existing dependencies + ``` + +### **2. Backend Challenge (Rust)** + +You need to query the filesystem for metadata and map it to a struct. + +**Requirements:** + +1. Define a Rust `struct` representing the file stats. +2. Derive `Serialize` so Tauri can convert it to JSON automatically. +3. Use `std::fs::metadata` to populate the data. + +**Starter Code:** + +```rust +use std::fs; +use serde::Serialize; + +// This struct will be converted to a JSON object automatically +#[derive(Serialize)] +struct FileStats { + size_bytes: u64, + is_readonly: bool, + // Challenge: 'modified' returns SystemTime, which is not directly serializable to JSON. + // You must figure out how to convert SystemTime to a UNIX timestamp (u64) or String. + last_modified: u64, +} + +#[tauri::command] +fn get_file_stats(path: String) -> Result { + // TODO: + // 1. Call fs::metadata(&path) + // 2. Handle the Result (match or ?) + // 3. Map the Metadata struct to your FileStats struct +} +``` + +### **3. Frontend Challenge (TypeScript)** + +1. Create a "Properties" view or a footer bar in your editor. +2. When a file is active, invoke `get_file_stats`. +3. Display the size. + - _Math Challenge:_ Convert the raw bytes into human-readable units (KB, MB, GB). + +### **4. Deep Dive: The Time Problem** + +In C++, `time_t` is usually just an integer. In Rust, `SystemTime` is an opaque type designed to be platform-independent. + +- **The Hurdle:** You cannot send `SystemTime` directly to JS. +- **The Hint:** Look for `duration_since(UNIX_EPOCH)` in the Rust documentation to convert the time object into a simple number that JavaScript's `new Date()` can parse. + +### **Conclusion Task 4** + +Completing this task demonstrates mastery of: + +1. **System Abstraction:** Using `std::fs` to normalize OS-specific file attributes. +2. **FFI (Foreign Function Interface):** Marshalling complex data structures between Rust and JS via Serde. + +**"You now control not just the content, but the container."** πŸ¦€βœ¨ + +--- \ No newline at end of file