You've already forked rust-tutor
generic types, traits and lifetimes
- get claude to create rustlings style exercises based on chapter 10 of the book - update CLAUDE.md to prevent it from producing incorrect exercises - complete exercises/01-generic-cache-system
This commit is contained in:
6
exercises/03-basic-traits/Cargo.toml
Normal file
6
exercises/03-basic-traits/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "basic_traits"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
15
exercises/03-basic-traits/README.md
Normal file
15
exercises/03-basic-traits/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Configuration System Traits
|
||||
|
||||
Build a configuration system where different components (database, cache, logger) can be configured uniformly. You'll define traits that allow different types to be configured, validated, and reset in a standard way.
|
||||
|
||||
## What you'll practice
|
||||
|
||||
- Defining traits that establish contracts between types
|
||||
- Implementing the same trait for different struct types
|
||||
- Using traits as function parameters with `impl Trait` syntax
|
||||
- Creating functions that work with any type implementing specific traits
|
||||
|
||||
## Further information
|
||||
|
||||
- [Traits: Defining Shared Behavior](https://doc.rust-lang.org/book/ch10-02-traits.html)
|
||||
- [Traits as Parameters](https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters)
|
||||
119
exercises/03-basic-traits/src/main.rs
Normal file
119
exercises/03-basic-traits/src/main.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
// TODO: Define a trait called `Configurable` with these methods:
|
||||
// - configure(&mut self, setting: &str, value: &str) -> Result<(), String>
|
||||
// - is_valid(&self) -> bool
|
||||
// - reset(&mut self)
|
||||
trait Configurable {
|
||||
// Add your methods here
|
||||
}
|
||||
|
||||
struct DatabaseConfig {
|
||||
host: String,
|
||||
port: u16,
|
||||
max_connections: u32,
|
||||
}
|
||||
|
||||
struct CacheConfig {
|
||||
size_mb: u32,
|
||||
ttl_seconds: u64,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
struct LoggerConfig {
|
||||
level: String,
|
||||
output_file: Option<String>,
|
||||
timestamp_format: String,
|
||||
}
|
||||
|
||||
// TODO: Implement Configurable for DatabaseConfig
|
||||
// configure should handle: "host", "port", "max_connections"
|
||||
// is_valid should return true if host is not empty and port > 0
|
||||
// reset should set host="localhost", port=5432, max_connections=10
|
||||
|
||||
// TODO: Implement Configurable for CacheConfig
|
||||
// configure should handle: "size_mb", "ttl_seconds", "enabled"
|
||||
// is_valid should return true if size_mb > 0 and ttl_seconds > 0
|
||||
// reset should set size_mb=100, ttl_seconds=300, enabled=true
|
||||
|
||||
// TODO: Implement Configurable for LoggerConfig
|
||||
// configure should handle: "level", "output_file", "timestamp_format"
|
||||
// is_valid should return true if level is one of: "debug", "info", "warn", "error"
|
||||
// reset should set level="info", output_file=None, timestamp_format="%Y-%m-%d %H:%M:%S"
|
||||
|
||||
// TODO: Write a function `apply_config` that takes any Configurable and a list of (key, value) pairs
|
||||
// It should try to configure each setting and return a Vec of error messages for failed configurations
|
||||
fn apply_config(config: &mut impl Configurable, settings: &[(&str, &str)]) -> Vec<String> {
|
||||
todo!("Apply configuration settings")
|
||||
}
|
||||
|
||||
// TODO: Write a function `validate_and_reset_if_invalid` that takes any Configurable
|
||||
// If the config is invalid, reset it and return true. If valid, return false.
|
||||
fn validate_and_reset_if_invalid(config: &mut impl Configurable) -> bool {
|
||||
todo!("Validate config and reset if invalid")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_database_config() {
|
||||
let mut db = DatabaseConfig {
|
||||
host: "".to_string(),
|
||||
port: 0,
|
||||
max_connections: 0,
|
||||
};
|
||||
|
||||
assert!(!db.is_valid());
|
||||
db.reset();
|
||||
assert!(db.is_valid());
|
||||
assert_eq!(db.host, "localhost");
|
||||
assert_eq!(db.port, 5432);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cache_configure() {
|
||||
let mut cache = CacheConfig {
|
||||
size_mb: 0,
|
||||
ttl_seconds: 0,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
cache.reset();
|
||||
assert!(cache.is_valid());
|
||||
|
||||
assert!(cache.configure("size_mb", "200").is_ok());
|
||||
assert_eq!(cache.size_mb, 200);
|
||||
|
||||
assert!(cache.configure("invalid_key", "value").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logger_validation() {
|
||||
let mut logger = LoggerConfig {
|
||||
level: "invalid".to_string(),
|
||||
output_file: None,
|
||||
timestamp_format: "".to_string(),
|
||||
};
|
||||
|
||||
assert!(!logger.is_valid());
|
||||
assert!(validate_and_reset_if_invalid(&mut logger));
|
||||
assert!(logger.is_valid());
|
||||
assert_eq!(logger.level, "info");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_config() {
|
||||
let mut db = DatabaseConfig {
|
||||
host: "".to_string(),
|
||||
port: 0,
|
||||
max_connections: 0,
|
||||
};
|
||||
|
||||
let settings = [("host", "example.com"), ("port", "3306"), ("invalid", "value")];
|
||||
let errors = apply_config(&mut db, &settings);
|
||||
|
||||
assert_eq!(errors.len(), 1); // Only "invalid" should fail
|
||||
assert_eq!(db.host, "example.com");
|
||||
assert_eq!(db.port, 3306);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user