Skip to main content

fuchsia_url/
builtin_url.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub use crate::errors::ParseError;
6use crate::{Scheme, UrlParts};
7
8pub const SCHEME: &str = "fuchsia-builtin";
9
10/// Decoded representation of a builtin URL.
11///
12/// fuchsia-builtin://#resource
13///
14/// Builtin component declarations are used to bootstrap the ELF runner.
15/// They are never packaged.
16///
17/// The path in builtin URLs must be "/". Following that, they may contain a fragment.
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct BuiltinUrl {
20    resource: Option<crate::Resource>,
21}
22
23impl BuiltinUrl {
24    pub fn parse(input: &str) -> Result<Self, ParseError> {
25        Self::try_from_parts(UrlParts::parse(input)?)
26    }
27
28    fn try_from_parts(
29        UrlParts { scheme, host, path, hash, resource }: UrlParts,
30    ) -> Result<Self, ParseError> {
31        if scheme.ok_or(ParseError::MissingScheme)? != Scheme::Builtin {
32            return Err(ParseError::InvalidScheme);
33        }
34
35        if host.is_some() {
36            return Err(ParseError::HostMustBeEmpty);
37        }
38
39        if hash.is_some() {
40            return Err(ParseError::CannotContainHash);
41        }
42
43        if path.is_some() {
44            return Err(ParseError::PathMustBeRoot);
45        }
46
47        Ok(Self { resource })
48    }
49
50    pub fn resource(&self) -> Option<&crate::Resource> {
51        self.resource.as_ref()
52    }
53}
54
55impl std::fmt::Display for BuiltinUrl {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(f, "{}://", SCHEME)?;
58        if let Some(ref resource) = self.resource {
59            write!(f, "#{}", resource.percent_encode())?;
60        }
61        Ok(())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::errors::PackagePathSegmentError;
69    use crate::resource::ResourcePathError;
70    use assert_matches::assert_matches;
71
72    #[test]
73    fn test_parse_ok() {
74        assert_eq!(BuiltinUrl::parse("fuchsia-builtin://").unwrap().resource(), None);
75        assert_eq!(
76            BuiltinUrl::parse("fuchsia-builtin://#a").unwrap().resource().map(|r| r.as_ref()),
77            Some("a")
78        );
79        assert_eq!(
80            BuiltinUrl::parse("fuchsia-builtin://#elf_runner.cm")
81                .unwrap()
82                .resource()
83                .map(|r| r.as_ref()),
84            Some("elf_runner.cm")
85        );
86    }
87
88    #[test]
89    fn test_parse_error_wrong_scheme() {
90        assert_matches!(BuiltinUrl::parse("foobar://").unwrap_err(), ParseError::InvalidScheme);
91        assert_matches!(
92            BuiltinUrl::parse("fuchsia-boot://").unwrap_err(),
93            ParseError::InvalidScheme
94        );
95        assert_matches!(
96            BuiltinUrl::parse("fuchsia-pkg://").unwrap_err(),
97            ParseError::InvalidScheme
98        );
99    }
100
101    #[test]
102    fn test_parse_error_missing_scheme() {
103        assert_matches!(BuiltinUrl::parse("package").unwrap_err(), ParseError::MissingScheme);
104    }
105
106    #[test]
107    fn test_parse_error_invalid_path() {
108        assert_matches!(
109            BuiltinUrl::parse("fuchsia-builtin:////").unwrap_err(),
110            ParseError::InvalidPathSegment(PackagePathSegmentError::Empty)
111        );
112    }
113
114    #[test]
115    fn test_parse_error_invalid_character() {
116        assert_matches!(
117            BuiltinUrl::parse("fuchsia-builtin:///package:1234").unwrap_err(),
118            ParseError::InvalidPathSegment(PackagePathSegmentError::InvalidCharacter {
119                character: ':'
120            })
121        );
122    }
123
124    #[test]
125    fn test_parse_error_host_must_be_empty() {
126        assert_matches!(
127            BuiltinUrl::parse("fuchsia-builtin://hello").unwrap_err(),
128            ParseError::HostMustBeEmpty
129        );
130    }
131
132    #[test]
133    fn test_parse_error_resource_cannot_be_slash() {
134        assert_matches!(
135            BuiltinUrl::parse("fuchsia-builtin://#/").unwrap_err(),
136            ParseError::InvalidResourcePath(ResourcePathError::PathStartsWithSlash)
137        );
138    }
139}