fuchsia_url/
builtin_url.rs1pub use crate::errors::ParseError;
6pub use crate::parse::{validate_package_path_segment, validate_resource_path};
7use crate::{Scheme, UrlParts};
8
9pub const SCHEME: &str = "fuchsia-builtin";
10
11#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct BuiltinUrl {
21 resource: Option<String>,
22}
23
24impl BuiltinUrl {
25 pub fn parse(input: &str) -> Result<Self, ParseError> {
26 Self::try_from_parts(UrlParts::parse(input)?)
27 }
28
29 fn try_from_parts(
30 UrlParts { scheme, host, path, hash, resource }: UrlParts,
31 ) -> Result<Self, ParseError> {
32 if scheme.ok_or(ParseError::MissingScheme)? != Scheme::Builtin {
33 return Err(ParseError::InvalidScheme);
34 }
35
36 if host.is_some() {
37 return Err(ParseError::HostMustBeEmpty);
38 }
39
40 if hash.is_some() {
41 return Err(ParseError::CannotContainHash);
42 }
43
44 if path != "/" {
45 return Err(ParseError::PathMustBeRoot);
46 }
47
48 Ok(Self { resource })
49 }
50
51 pub fn resource(&self) -> Option<&str> {
52 self.resource.as_ref().map(|s| s.as_str())
53 }
54}
55
56impl std::fmt::Display for BuiltinUrl {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(f, "{}://", SCHEME)?;
59 if let Some(ref resource) = self.resource {
60 write!(f, "#{}", percent_encoding::utf8_percent_encode(resource, crate::FRAGMENT))?;
61 }
62
63 Ok(())
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::errors::{PackagePathSegmentError, ResourcePathError};
71 use assert_matches::assert_matches;
72
73 #[test]
74 fn test_parse_ok() {
75 assert_eq!(BuiltinUrl::parse("fuchsia-builtin://").unwrap().resource(), None);
76 assert_eq!(BuiltinUrl::parse("fuchsia-builtin://#a").unwrap().resource(), Some("a"));
77 assert_eq!(
78 BuiltinUrl::parse("fuchsia-builtin://#elf_runner.cm").unwrap().resource(),
79 Some("elf_runner.cm")
80 );
81 }
82
83 #[test]
84 fn test_parse_error_wrong_scheme() {
85 assert_matches!(BuiltinUrl::parse("foobar://").unwrap_err(), ParseError::InvalidScheme);
86 assert_matches!(
87 BuiltinUrl::parse("fuchsia-boot://").unwrap_err(),
88 ParseError::InvalidScheme
89 );
90 assert_matches!(
91 BuiltinUrl::parse("fuchsia-pkg://").unwrap_err(),
92 ParseError::InvalidScheme
93 );
94 }
95
96 #[test]
97 fn test_parse_error_missing_scheme() {
98 assert_matches!(BuiltinUrl::parse("package").unwrap_err(), ParseError::MissingScheme);
99 }
100
101 #[test]
102 fn test_parse_error_invalid_path() {
103 assert_matches!(
104 BuiltinUrl::parse("fuchsia-builtin:////").unwrap_err(),
105 ParseError::InvalidPathSegment(PackagePathSegmentError::Empty)
106 );
107 }
108
109 #[test]
110 fn test_parse_error_invalid_character() {
111 assert_matches!(
112 BuiltinUrl::parse("fuchsia-builtin:///package:1234").unwrap_err(),
113 ParseError::InvalidPathSegment(PackagePathSegmentError::InvalidCharacter {
114 character: ':'
115 })
116 );
117 }
118
119 #[test]
120 fn test_parse_error_host_must_be_empty() {
121 assert_matches!(
122 BuiltinUrl::parse("fuchsia-builtin://hello").unwrap_err(),
123 ParseError::HostMustBeEmpty
124 );
125 }
126
127 #[test]
128 fn test_parse_error_resource_cannot_be_slash() {
129 assert_matches!(
130 BuiltinUrl::parse("fuchsia-builtin://#/").unwrap_err(),
131 ParseError::InvalidResourcePath(ResourcePathError::PathStartsWithSlash)
132 );
133 }
134}