initial commit
This commit is contained in:
10
exercises/05_ticket_v2/14_source/Cargo.toml
Normal file
10
exercises/05_ticket_v2/14_source/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "source"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.59"
|
||||
|
||||
[dev-dependencies]
|
||||
common = { path = "../../../helpers/common" }
|
||||
76
exercises/05_ticket_v2/14_source/src/lib.rs
Normal file
76
exercises/05_ticket_v2/14_source/src/lib.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use crate::status::Status;
|
||||
|
||||
// We've seen how to declare modules in one of the earliest exercises, but
|
||||
// we haven't seen how to extract them into separate files.
|
||||
// Let's fix that now!
|
||||
//
|
||||
// In the simplest case, when the extracted module is a single file, it is enough to
|
||||
// create a new file with the same name as the module and move the module content there.
|
||||
// The module file should be placed in the same directory as the file that declares the module.
|
||||
// In this case, `src/lib.rs`, thus `status.rs` should be placed in the `src` directory.
|
||||
mod status;
|
||||
|
||||
// TODO: Add a new error variant to `TicketNewError` for when the status string is invalid.
|
||||
// When calling `source` on an error of that variant, it should return a `ParseStatusError` rather than `None`.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum TicketNewError {
|
||||
#[error("Title cannot be empty")]
|
||||
TitleCannotBeEmpty,
|
||||
#[error("Title cannot be longer than 50 bytes")]
|
||||
TitleTooLong,
|
||||
#[error("Description cannot be empty")]
|
||||
DescriptionCannotBeEmpty,
|
||||
#[error("Description cannot be longer than 500 bytes")]
|
||||
DescriptionTooLong,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Ticket {
|
||||
title: String,
|
||||
description: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
impl Ticket {
|
||||
pub fn new(title: String, description: String, status: String) -> Result<Self, TicketNewError> {
|
||||
if title.is_empty() {
|
||||
return Err(TicketNewError::TitleCannotBeEmpty);
|
||||
}
|
||||
if title.len() > 50 {
|
||||
return Err(TicketNewError::TitleTooLong);
|
||||
}
|
||||
if description.is_empty() {
|
||||
return Err(TicketNewError::DescriptionCannotBeEmpty);
|
||||
}
|
||||
if description.len() > 500 {
|
||||
return Err(TicketNewError::DescriptionTooLong);
|
||||
}
|
||||
|
||||
// TODO: Parse the status string into a `Status` enum.
|
||||
|
||||
Ok(Ticket {
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use common::{valid_description, valid_title};
|
||||
use std::error::Error;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn invalid_status() {
|
||||
let err = Ticket::new(valid_title(), valid_description(), "invalid".into()).unwrap_err();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"`invalid` is not a valid status. Use one of: ToDo, InProgress, Done"
|
||||
);
|
||||
assert!(err.source().is_some());
|
||||
}
|
||||
}
|
||||
46
exercises/05_ticket_v2/14_source/src/status.rs
Normal file
46
exercises/05_ticket_v2/14_source/src/status.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Status {
|
||||
ToDo,
|
||||
InProgress,
|
||||
Done,
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Status {
|
||||
type Error = ParseStatusError;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let value = value.to_lowercase();
|
||||
match value.as_str() {
|
||||
"todo" => Ok(Status::ToDo),
|
||||
"inprogress" => Ok(Status::InProgress),
|
||||
"done" => Ok(Status::Done),
|
||||
_ => Err(ParseStatusError {
|
||||
invalid_status: value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("`{invalid_status}` is not a valid status. Use one of: ToDo, InProgress, Done")]
|
||||
pub struct ParseStatusError {
|
||||
invalid_status: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
fn test_try_from_string() {
|
||||
let status = Status::try_from("ToDO".to_string()).unwrap();
|
||||
assert_eq!(status, Status::ToDo);
|
||||
|
||||
let status = Status::try_from("inproGress".to_string()).unwrap();
|
||||
assert_eq!(status, Status::InProgress);
|
||||
|
||||
let status = Status::try_from("Done".to_string()).unwrap();
|
||||
assert_eq!(status, Status::Done);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user