use super::error::Result;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NonEmptyPath(PathBuf);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmptyPathError;
impl std::fmt::Display for EmptyPathError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Path cannot be empty")
}
}
impl std::error::Error for EmptyPathError {}
impl NonEmptyPath {
pub fn new(path: PathBuf) -> std::result::Result<Self, EmptyPathError> {
if path.as_os_str().is_empty() {
Err(EmptyPathError)
} else {
Ok(Self(path))
}
}
pub fn from_string(s: &str) -> std::result::Result<Self, EmptyPathError> {
if s.is_empty() {
Err(EmptyPathError)
} else {
Ok(Self(PathBuf::from(s)))
}
}
pub fn as_path(&self) -> &Path {
&self.0
}
pub fn into_path_buf(self) -> PathBuf {
self.0
}
pub fn join(&self, path: impl AsRef<Path>) -> NonEmptyPath {
NonEmptyPath(self.0.join(path))
}
}
impl std::str::FromStr for NonEmptyPath {
type Err = EmptyPathError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s.is_empty() {
Err(EmptyPathError)
} else {
Ok(Self(PathBuf::from(s)))
}
}
}
impl AsRef<Path> for NonEmptyPath {
fn as_ref(&self) -> &Path {
&self.0
}
}
impl std::fmt::Display for NonEmptyPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.display())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NonEmptyString(String);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EmptyStringError;
impl std::fmt::Display for EmptyStringError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "String cannot be empty")
}
}
impl std::error::Error for EmptyStringError {}
impl NonEmptyString {
pub fn new(s: String) -> std::result::Result<Self, EmptyStringError> {
if s.is_empty() {
Err(EmptyStringError)
} else {
Ok(Self(s))
}
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn into_string(self) -> String {
self.0
}
}
impl AsRef<str> for NonEmptyString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for NonEmptyString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Counter {
value: u32, }
impl Counter {
pub fn new(value: u32) -> Self {
Self { value }
}
pub fn increment(&mut self) {
self.value = self.value.saturating_add(1);
}
pub fn decrement(&mut self) {
self.value = self.value.saturating_sub(1);
}
pub fn add(&mut self, n: u32) {
self.value = self.value.saturating_add(n);
}
pub fn sub(&mut self, n: u32) {
self.value = self.value.saturating_sub(n);
}
pub fn get(&self) -> u32 {
self.value
}
pub fn reset(&mut self) {
self.value = 0;
}
}
impl Default for Counter {
fn default() -> Self {
Self::new(0)
}
}
use std::marker::PhantomData;
pub struct Open;
pub struct Closed;
#[derive(Debug)]
pub struct FileHandle<State> {
file: std::fs::File,
_state: PhantomData<State>,
}
impl FileHandle<Open> {
pub fn open(path: &Path) -> Result<Self> {
let file = std::fs::File::open(path)?;
Ok(Self {
file,
_state: PhantomData,
})
}
pub fn read(&mut self) -> Result<Vec<u8>> {
use std::io::Read;
let mut buffer = Vec::new();
self.file.read_to_end(&mut buffer)?;
Ok(buffer)
}
pub fn close(self) -> FileHandle<Closed> {
FileHandle {
file: self.file,
_state: PhantomData,
}
}
}
impl FileHandle<Closed> {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_non_empty_path_valid() {
let path = NonEmptyPath::new(PathBuf::from("test.txt")).unwrap();
assert_eq!(path.as_path(), Path::new("test.txt"));
}
#[test]
fn test_non_empty_path_empty_rejected() {
let empty = NonEmptyPath::new(PathBuf::from(""));
assert!(empty.is_err());
assert_eq!(empty.unwrap_err(), EmptyPathError);
}
#[test]
fn test_non_empty_path_join() {
let base: NonEmptyPath = "base".parse().unwrap();
let joined = base.join("file.txt");
assert!(joined.as_path().to_str().unwrap().contains("base"));
assert!(joined.as_path().to_str().unwrap().contains("file.txt"));
}
#[test]
fn test_non_empty_string_valid() {
let s = NonEmptyString::new("hello".to_string()).unwrap();
assert_eq!(s.as_str(), "hello");
}
#[test]
fn test_non_empty_string_empty_rejected() {
let empty = NonEmptyString::new("".to_string());
assert!(empty.is_err());
assert_eq!(empty.unwrap_err(), EmptyStringError);
}
#[test]
fn test_counter_cannot_be_negative() {
let mut counter = Counter::new(0);
counter.decrement(); assert_eq!(counter.get(), 0);
counter.decrement(); assert_eq!(counter.get(), 0);
}
#[test]
fn test_counter_cannot_overflow() {
let mut counter = Counter::new(u32::MAX);
counter.increment(); assert_eq!(counter.get(), u32::MAX);
}
#[test]
fn test_counter_normal_operations() {
let mut counter = Counter::new(5);
counter.increment();
assert_eq!(counter.get(), 6);
counter.decrement();
assert_eq!(counter.get(), 5);
counter.add(10);
assert_eq!(counter.get(), 15);
counter.sub(5);
assert_eq!(counter.get(), 10);
counter.reset();
assert_eq!(counter.get(), 0);
}
#[test]
fn test_file_handle_type_safety() {
let _open_handle: Result<FileHandle<Open>> = FileHandle::open(Path::new("nonexistent"));
}
}