winget_types/locale/
release_notes.rs1use std::{borrow::Cow, str::FromStr};
2
3use derive_more::{Deref, Display};
4use serde::Serialize;
5use serde_with::DeserializeFromStr;
6use thiserror::Error;
7
8#[derive(
9 Clone,
10 Debug,
11 Default,
12 Deref,
13 Display,
14 Eq,
15 PartialEq,
16 Ord,
17 PartialOrd,
18 Hash,
19 Serialize,
20 DeserializeFromStr,
21)]
22pub struct ReleaseNotes(String);
23
24#[derive(Error, Debug, Eq, PartialEq)]
25pub enum ReleaseNotesError {
26 #[error("Release notes cannot be empty")]
27 Empty,
28}
29
30impl ReleaseNotes {
31 pub const MAX_CHAR_LENGTH: usize = 10000;
32
33 pub fn new<S: AsRef<str>>(value: S) -> Result<Self, ReleaseNotesError> {
34 let result = truncate_with_lines::<{ Self::MAX_CHAR_LENGTH }>(value.as_ref().trim());
35 if result.is_empty() {
36 Err(ReleaseNotesError::Empty)
37 } else {
38 Ok(Self(result.into_owned()))
39 }
40 }
41}
42
43impl FromStr for ReleaseNotes {
44 type Err = ReleaseNotesError;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 Self::new(s)
48 }
49}
50
51fn truncate_with_lines<const N: usize>(value: &str) -> Cow<str> {
52 if value.chars().count() <= N {
53 return Cow::Borrowed(value);
54 }
55
56 let mut result = String::with_capacity(N);
57 let mut current_size = 0;
58
59 for (iter_count, line) in value.lines().enumerate() {
60 let prospective_size = current_size + line.chars().count() + "\n".len();
61 if prospective_size > N {
62 break;
63 }
64 if iter_count != 0 {
65 result.push('\n');
66 }
67 result.push_str(line);
68 current_size = prospective_size;
69 }
70
71 Cow::Owned(result)
72}
73
74#[cfg(test)]
75mod tests {
76 use crate::locale::release_notes::truncate_with_lines;
77
78 #[test]
79 fn test_truncate_to_lines() {
80 use std::fmt::Write;
81
82 const CHAR_LIMIT: usize = 100;
83
84 let mut buffer = String::new();
85 let mut line_count = 0;
86 while buffer.chars().count() <= CHAR_LIMIT {
87 line_count += 1;
88 writeln!(buffer, "Line {line_count}").unwrap();
89 }
90 let formatted = truncate_with_lines::<CHAR_LIMIT>(&buffer);
91 let formatted_char_count = formatted.chars().count();
92 assert!(formatted_char_count < buffer.chars().count());
93 assert_eq!(formatted.trim().chars().count(), formatted_char_count);
94 }
95}