Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Side by Side Diff: lib/packagemap.dart

Issue 1142363005: Added .packages, packages/ discovery. (Closed) Base URL: https://github.com/dart-lang/package_config.git@master
Patch Set: Addressed comments. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/discovery.dart ('k') | lib/packages.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library package_config.packagemap;
6
7 class Packages {
8 static const int _EQUALS = 0x3d;
9 static const int _CR = 0x0d;
10 static const int _NL = 0x0a;
11 static const int _NUMBER_SIGN = 0x23;
12
13 final Map<String, Uri> packageMapping;
14
15 Packages(this.packageMapping);
16
17 /// Resolves a URI to a non-package URI.
18 ///
19 /// If [uri] is a `package:` URI, the location is resolved wrt. the
20 /// [packageMapping].
21 /// Otherwise the original URI is returned.
22 Uri resolve(Uri uri) {
23 if (uri.scheme.toLowerCase() != "package") {
24 return uri;
25 }
26 if (uri.hasAuthority) {
27 throw new ArgumentError.value(uri, "uri", "Must not have authority");
28 }
29 if (uri.path.startsWith("/")) {
30 throw new ArgumentError.value(
31 uri, "uri", "Path must not start with '/'.");
32 }
33 // Normalizes the path by removing '.' and '..' segments.
34 uri = uri.normalizePath();
35 String path = uri.path;
36 var slashIndex = path.indexOf('/');
37 String packageName;
38 String rest;
39 if (slashIndex < 0) {
40 packageName = path;
41 rest = "";
42 } else {
43 packageName = path.substring(0, slashIndex);
44 rest = path.substring(slashIndex + 1);
45 }
46 Uri packageLocation = packageMapping[packageName];
47 if (packageLocation == null) {
48 throw new ArgumentError.value(
49 uri, "uri", "Unknown package name: $packageName");
50 }
51 return packageLocation.resolveUri(new Uri(path: rest));
52 }
53
54 /// Parses a `packages.cfg` file into a `Packages` object.
55 ///
56 /// The [baseLocation] is used as a base URI to resolve all relative
57 /// URI references against.
58 ///
59 /// The `Packages` object allows resolving package: URIs and writing
60 /// the mapping back to a file or string.
61 /// The [packageMapping] will contain a simple mapping from package name
62 /// to package location.
63 static Packages parse(String source, Uri baseLocation) {
64 int index = 0;
65 Map<String, Uri> result = <String, Uri>{};
66 while (index < source.length) {
67 bool isComment = false;
68 int start = index;
69 int eqIndex = -1;
70 int end = source.length;
71 int char = source.codeUnitAt(index++);
72 if (char == _CR || char == _NL) {
73 continue;
74 }
75 if (char == _EQUALS) {
76 throw new FormatException("Missing package name", source, index - 1);
77 }
78 isComment = char == _NUMBER_SIGN;
79 while (index < source.length) {
80 char = source.codeUnitAt(index++);
81 if (char == _EQUALS && eqIndex < 0) {
82 eqIndex = index - 1;
83 } else if (char == _NL || char == _CR) {
84 end = index - 1;
85 break;
86 }
87 }
88 if (isComment) continue;
89 if (eqIndex < 0) {
90 throw new FormatException("No '=' on line", source, index - 1);
91 }
92 _checkIdentifier(source, start, eqIndex);
93 var packageName = source.substring(start, eqIndex);
94
95 var packageLocation = Uri.parse(source, eqIndex + 1, end);
96 if (!packageLocation.path.endsWith('/')) {
97 packageLocation =
98 packageLocation.replace(path: packageLocation.path + "/");
99 }
100 packageLocation = baseLocation.resolveUri(packageLocation);
101 if (result.containsKey(packageName)) {
102 throw new FormatException(
103 "Same package name occured twice.", source, start);
104 }
105 result[packageName] = packageLocation;
106 }
107 return new Packages(result);
108 }
109
110 /**
111 * Writes the mapping to a [StringSink].
112 *
113 * If [comment] is provided, the output will contain this comment
114 * with `#` in front of each line.
115 *
116 * If [baseUri] is provided, package locations will be made relative
117 * to the base URI, if possible, before writing.
118 */
119 void write(StringSink output, {Uri baseUri, String comment}) {
120 if (baseUri != null && !baseUri.isAbsolute) {
121 throw new ArgumentError.value(baseUri, "baseUri", "Must be absolute");
122 }
123
124 if (comment != null) {
125 for (var commentLine in comment.split('\n')) {
126 output.write('#');
127 output.writeln(commentLine);
128 }
129 } else {
130 output.write("# generated by package:packagecfg at ");
131 output.write(new DateTime.now());
132 output.writeln();
133 }
134
135 packageMapping.forEach((String packageName, Uri uri) {
136 // Validate packageName.
137 _checkIdentifier(packageName, 0, packageName.length);
138 output.write(packageName);
139
140 output.write('=');
141
142 // If baseUri provided, make uri relative.
143 if (baseUri != null) {
144 uri = relativize(uri, baseUri);
145 }
146 output.write(uri);
147 if (!uri.path.endsWith('/')) {
148 output.write('/');
149 }
150 output.writeln();
151 });
152 }
153
154 String toString() {
155 StringBuffer buffer = new StringBuffer();
156 write(buffer);
157 return buffer.toString();
158 }
159
160 static Uri relativize(Uri uri, Uri baseUri) {
161 if (uri.hasQuery || uri.hasFragment) {
162 uri = new Uri(
163 scheme: uri.scheme,
164 userInfo: uri.hasAuthority ? uri.userInfo : null,
165 host: uri.hasAuthority ? uri.host : null,
166 port: uri.hasAuthority ? uri.port : null,
167 path: uri.path);
168 }
169 if (!baseUri.isAbsolute) {
170 throw new ArgumentError("Base uri '$baseUri' must be absolute.");
171 }
172 // Already relative.
173 if (!uri.isAbsolute) return uri;
174
175 if (baseUri.scheme.toLowerCase() != uri.scheme.toLowerCase()) {
176 return uri;
177 }
178 // If authority differs, we could remove the scheme, but it's not worth it.
179 if (uri.hasAuthority != baseUri.hasAuthority) return uri;
180 if (uri.hasAuthority) {
181 if (uri.userInfo != baseUri.userInfo ||
182 uri.host.toLowerCase() != baseUri.host.toLowerCase() ||
183 uri.port != baseUri.port) {
184 return uri;
185 }
186 }
187
188 baseUri = baseUri.normalizePath();
189 List<String> base = baseUri.pathSegments.toList();
190 if (base.isNotEmpty) {
191 base = new List<String>.from(base)..removeLast();
192 }
193 uri = uri.normalizePath();
194 List<String> target = uri.pathSegments.toList();
195 int index = 0;
196 while (index < base.length && index < target.length) {
197 if (base[index] != target[index]) {
198 break;
199 }
200 index++;
201 }
202 if (index == base.length) {
203 return new Uri(path: target.skip(index).join('/'));
204 } else if (index > 0) {
205 return new Uri(
206 path: '../' * (base.length - index) + target.skip(index).join('/'));
207 } else {
208 return uri;
209 }
210 }
211
212 static bool _checkIdentifier(String string, int start, int end) {
213 const int a = 0x61;
214 const int z = 0x7a;
215 const int _ = 0x5f;
216 const int $ = 0x24;
217 if (start == end) return false;
218 for (int i = start; i < end; i++) {
219 var char = string.codeUnitAt(i);
220 if (char == _ || char == $) continue;
221 if ((char ^ 0x30) <= 9 && i > 0) continue;
222 char |= 0x20; // Lower-case letters.
223 if (char >= a && char <= z) continue;
224 throw new FormatException("Not an identifier", string, i);
225 }
226 return true;
227 }
228 }
OLDNEW
« no previous file with comments | « lib/discovery.dart ('k') | lib/packages.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698