rust_toolchain_file/
legacy.rs1use std::path::{Path, PathBuf};
2use std::str::FromStr;
3
4#[cfg(test)]
5mod tests;
6
7pub struct Parser<'content> {
9 content: &'content str,
10
11 strict: bool,
15}
16
17impl<'content> Parser<'content> {
18 pub fn new(content: &'content str) -> Self {
20 Self {
21 content,
22 strict: false,
23 }
24 }
25
26 pub fn strict(content: &'content str) -> Self {
28 Self {
29 content,
30 strict: true,
31 }
32 }
33}
34
35impl Parser<'_> {
36 pub fn parse(&self) -> Result<LegacyToolchainFile, ParserError> {
37 if self.strict && !self.content.is_ascii() {
39 return Err(ParserError::InvalidEncodingStrict);
40 }
41
42 let content = self.content.trim();
43
44 if content.is_empty() {
46 return Err(ParserError::IsEmpty);
47 }
48
49 let line_count = content.lines().count();
51
52 if line_count != 1 {
53 return Err(ParserError::TooManyLines(line_count));
54 }
55
56 let channel = if Path::new(content).is_absolute() {
58 LegacyChannel::Path(content.into())
59 } else {
60 LegacyChannel::Spec(content.to_string())
61 };
62
63 Ok(LegacyToolchainFile { channel })
64 }
65}
66
67#[derive(Debug, thiserror::Error, PartialEq)]
68pub enum ParserError {
69 #[error("Unable to parse legacy toolchain file: toolchain file was empty")]
70 IsEmpty,
71
72 #[error("Encountered invalid encoding while parsing legacy rust-toolchain file. The expected encoding to be US-ASCII, and lenient encoding was disabled.")]
73 InvalidEncodingStrict,
74
75 #[error("Expected a single line containing the toolchain specifier but found '{0}' lines.")]
76 TooManyLines(usize),
77}
78
79#[derive(Debug, PartialEq)]
81pub struct LegacyToolchainFile {
82 channel: LegacyChannel,
83}
84
85impl LegacyToolchainFile {
86 pub fn new(channel: LegacyChannel) -> Self {
87 Self { channel }
88 }
89}
90
91impl FromStr for LegacyToolchainFile {
92 type Err = ParserError;
93
94 fn from_str(content: &str) -> Result<Self, Self::Err> {
95 let parser = Parser::new(content);
96
97 parser.parse()
98 }
99}
100
101impl LegacyToolchainFile {
102 pub fn channel(&self) -> &LegacyChannel {
103 &self.channel
104 }
105
106 pub fn path(&self) -> Option<&Path> {
109 match self.channel {
110 LegacyChannel::Path(ref p) => Some(p.as_path()),
111 _ => None,
112 }
113 }
114
115 pub fn spec(&self) -> Option<&str> {
118 match self.channel {
119 LegacyChannel::Spec(ref p) => Some(p.as_str()),
120 _ => None,
121 }
122 }
123}
124
125#[derive(Debug, PartialEq)]
127pub enum LegacyChannel {
128 Path(PathBuf),
129 Spec(String),
130}