pkg/mime: layout as mini-libraries

Update version for publishing
Updated max unittest dependency version

R=ajohnsen@google.com

Review URL: https://codereview.chromium.org//272353002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/mime@36115 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/mime.dart b/lib/mime.dart
index d1372d4..11b2ca9 100644
--- a/lib/mime.dart
+++ b/lib/mime.dart
@@ -14,11 +14,5 @@
  */
 library mime;
 
-import 'dart:async';
-import 'dart:convert';
-import 'dart:typed_data';
-
-part 'src/mime_type.dart';
-part 'src/extension_map.dart';
-part 'src/magic_number.dart';
-part 'src/mime_multipart_transformer.dart';
+export 'src/mime_multipart_transformer.dart';
+export 'src/mime_type.dart';
diff --git a/lib/src/extension_map.dart b/lib/src/default_extension_map.dart
similarity index 99%
rename from lib/src/extension_map.dart
rename to lib/src/default_extension_map.dart
index 745556b..7e74648 100644
--- a/lib/src/extension_map.dart
+++ b/lib/src/default_extension_map.dart
@@ -2,11 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-part of mime;
-
+library mime.extension_map;
 
 // TODO(ajohnsen): Use sorted list and binary search?
-const Map<String, String> _DEFAULT_EXTENSION_MAP = const <String, String>{
+const Map<String, String> DEFAULT_EXTENSION_MAP = const <String, String>{
 '123':'application/vnd.lotus-1-2-3',
 '3dml':'text/vnd.in3d.3dml',
 '3ds':'image/x-3ds',
diff --git a/lib/src/magic_number.dart b/lib/src/magic_number.dart
index 1229b6d..6c554aa 100644
--- a/lib/src/magic_number.dart
+++ b/lib/src/magic_number.dart
@@ -2,15 +2,14 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-part of mime;
+library mime.magic_number;
 
-
-class _MagicNumber {
+class MagicNumber {
   final String mimeType;
   final List<int> numbers;
   final List<int> mask;
 
-  const _MagicNumber(this.mimeType, this.numbers, {this.mask});
+  const MagicNumber(this.mimeType, this.numbers, {this.mask});
 
   bool matches(List<int> header) {
     if (header.length < numbers.length) return false;
@@ -28,19 +27,17 @@
 
 }
 
-const int _DEFAULT_MAGIC_NUMBERS_MAX_LENGTH = 16;
-
-const List<_MagicNumber> _DEFAULT_MAGIC_NUMBERS = const [
-  const _MagicNumber('application/pdf', const [0x25, 0x50, 0x44, 0x46]),
-  const _MagicNumber('application/postscript', const [0x25, 0x51]),
-  const _MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]),
-  const _MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]),
-  const _MagicNumber('image/jpeg', const [0xFF, 0xD8]),
-  const _MagicNumber('image/png',
+const List<MagicNumber> DEFAULT_MAGIC_NUMBERS = const [
+  const MagicNumber('application/pdf', const [0x25, 0x50, 0x44, 0x46]),
+  const MagicNumber('application/postscript', const [0x25, 0x51]),
+  const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x37, 0x61]),
+  const MagicNumber('image/gif', const [0x47, 0x49, 0x46, 0x38, 0x39, 0x61]),
+  const MagicNumber('image/jpeg', const [0xFF, 0xD8]),
+  const MagicNumber('image/png',
       const [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]),
-  const _MagicNumber('image/tiff', const [0x49, 0x49, 0x2A, 0x00]),
-  const _MagicNumber('image/tiff', const [0x4D, 0x4D, 0x00, 0x2A]),
-  const _MagicNumber(
+  const MagicNumber('image/tiff', const [0x49, 0x49, 0x2A, 0x00]),
+  const MagicNumber('image/tiff', const [0x4D, 0x4D, 0x00, 0x2A]),
+  const MagicNumber(
       'video/mp4',
       const [0x00, 0x00, 0x00, 0x00, 0x66, 0x74,
              0x79, 0x70, 0x33, 0x67, 0x70, 0x35],
diff --git a/lib/src/mime_multipart_transformer.dart b/lib/src/mime_multipart_transformer.dart
index a35b491..75d10ff 100644
--- a/lib/src/mime_multipart_transformer.dart
+++ b/lib/src/mime_multipart_transformer.dart
@@ -2,8 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-part of mime;
+library mime.multipart_transformer;
 
+import 'dart:async';
+import 'dart:convert';
+import 'dart:typed_data';
 
 /**
  * A Mime Multipart class representing each part parsed by
@@ -30,12 +33,6 @@
   }
 }
 
-class _Const {
-  // Bytes for '()<>@,;:\\"/[]?={} \t'.
-  static const SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47,
-                                   91, 93, 63, 61, 123, 125, 32, 9];
-}
-
 class _CharCode {
   static const int HT = 9;
   static const int LF = 10;
@@ -71,6 +68,10 @@
   static const int _DONE = 14;
   static const int _FAILURE = 15;
 
+  // Bytes for '()<>@,;:\\"/[]?={} \t'.
+  static const _SEPARATORS = const [40, 41, 60, 62, 64, 44, 59, 58, 92, 34, 47,
+                                   91, 93, 63, 61, 123, 125, 32, 9];
+
   StreamController _controller;
   StreamSubscription _subscription;
 
@@ -387,7 +388,7 @@
   }
 
   bool _isTokenChar(int byte) {
-    return byte > 31 && byte < 128 && _Const.SEPARATORS.indexOf(byte) == -1;
+    return byte > 31 && byte < 128 && _SEPARATORS.indexOf(byte) == -1;
   }
 
   int _toLowerCase(int byte) {
@@ -412,7 +413,9 @@
 
 
 class MimeMultipartException implements Exception {
-  const MimeMultipartException([String this.message = ""]);
-  String toString() => "MimeMultipartException: $message";
   final String message;
+
+  const MimeMultipartException([String this.message = ""]);
+
+  String toString() => "MimeMultipartException: $message";
 }
diff --git a/lib/src/mime_type.dart b/lib/src/mime_type.dart
index c9daa2b..ca4d6c5 100644
--- a/lib/src/mime_type.dart
+++ b/lib/src/mime_type.dart
@@ -2,15 +2,18 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-part of mime;
+library mime.mime_type;
 
+import 'default_extension_map.dart';
+import 'magic_number.dart';
 
 final MimeTypeResolver _globalResolver = new MimeTypeResolver();
 
 /**
  * The maximum number of bytes needed, to match all default magic-numbers.
  */
-int get defaultMagicNumbersMaxLength => _DEFAULT_MAGIC_NUMBERS_MAX_LENGTH;
+// NOTE: this is not formatted AS_A_CONST to avoid a breaking change
+const int defaultMagicNumbersMaxLength = 12;
 
 /**
  * Extract the extension from [path] and use that for MIME-type lookup, using
@@ -32,7 +35,7 @@
  */
 class MimeTypeResolver {
   final Map<String, String> _extensionMap = {};
-  final List<_MagicNumber> _magicNumbers = [];
+  final List<MagicNumber> _magicNumbers = [];
   final bool _useDefault;
   int _magicNumbersMaxLength;
 
@@ -46,7 +49,7 @@
    */
   MimeTypeResolver() :
       _useDefault = true,
-      _magicNumbersMaxLength = _DEFAULT_MAGIC_NUMBERS_MAX_LENGTH;
+      _magicNumbersMaxLength = defaultMagicNumbersMaxLength;
 
   /**
    * Get the maximum number of bytes required to match all magic numbers, when
@@ -71,7 +74,7 @@
       result = _matchMagic(headerBytes, _magicNumbers);
       if (result != null) return result;
       if (_useDefault) {
-        result = _matchMagic(headerBytes, _DEFAULT_MAGIC_NUMBERS);
+        result = _matchMagic(headerBytes, DEFAULT_MAGIC_NUMBERS);
         if (result != null) return result;
       }
     }
@@ -79,7 +82,7 @@
     result = _extensionMap[ext];
     if (result != null) return result;
     if (_useDefault) {
-      result = _DEFAULT_EXTENSION_MAP[ext];
+      result = DEFAULT_EXTENSION_MAP[ext];
       if (result != null) return result;
     }
     return null;
@@ -106,11 +109,11 @@
     if (bytes.length > _magicNumbersMaxLength) {
       _magicNumbersMaxLength = bytes.length;
     }
-    _magicNumbers.add(new _MagicNumber(mimeType, bytes, mask: mask));
+    _magicNumbers.add(new MagicNumber(mimeType, bytes, mask: mask));
   }
 
   static String _matchMagic(List<int> headerBytes,
-                            List<_MagicNumber> magicNumbers) {
+                            List<MagicNumber> magicNumbers) {
     for (var mn in magicNumbers) {
       if (mn.matches(headerBytes)) return mn.mimeType;
     }
diff --git a/pubspec.yaml b/pubspec.yaml
index 0ce9b2a..b44361b 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,9 +1,9 @@
 name: mime
-version: 0.9.0
+version: 0.9.0+1
 author: Dart Team <misc@dartlang.org>
 description: Helper-package for working with MIME.
 homepage: http://www.dartlang.org
-dev_dependencies:
-  unittest: ">=0.9.0 <0.10.0"
 environment:
-  sdk: ">=0.8.10+6 <2.0.0"
+  sdk: '>=1.0.0 <2.0.0'
+dev_dependencies:
+  unittest: '>=0.9.0 <0.11.0'
diff --git a/test/mime_multipart_transformer_test.dart b/test/mime_multipart_transformer_test.dart
index 7d7baca..7bc2d7d 100644
--- a/test/mime_multipart_transformer_test.dart
+++ b/test/mime_multipart_transformer_test.dart
@@ -2,12 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import "package:unittest/unittest.dart";
-import "package:mime/mime.dart";
 import 'dart:async';
 import 'dart:math';
 
-void testParse(String message,
+import "package:unittest/unittest.dart";
+import "package:mime/mime.dart";
+
+void _testParse(String message,
                String boundary,
                [List<Map> expectedHeaders,
                 List expectedParts,
@@ -67,20 +68,9 @@
   });
 }
 
-void testParseValid() {
-  String message;
-  Map headers;
-  Map headers1;
-  Map headers2;
-  Map headers3;
-  Map headers4;
-  String body1;
-  String body2;
-  String body3;
-  String body4;
-
+void _testParseValid() {
   // Sample from Wikipedia.
-  message = """
+  var message = """
 This is a message with multiple parts in MIME format.\r
 --frontier\r
 Content-Type: text/plain\r
@@ -93,14 +83,14 @@
 PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg
 Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg=\r
 --frontier--\r\n""";
-  headers1 = <String, String>{"content-type": "text/plain"};
-  headers2 = <String, String>{"content-type": "application/octet-stream",
+  var headers1 = <String, String>{"content-type": "text/plain"};
+  var headers2 = <String, String>{"content-type": "application/octet-stream",
                               "content-transfer-encoding": "base64"};
-  body1 = "This is the body of the message.";
-  body2 = """
+  var body1 = "This is the body of the message.";
+  var body2 = """
 PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg
 Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg=""";
-  testParse(message, "frontier", [headers1, headers2], [body1, body2]);
+  _testParse(message, "frontier", [headers1, headers2], [body1, body2]);
 
   // Sample from HTML 4.01 Specification.
   message = """
@@ -122,7 +112,7 @@
   };
   body1 = "Larry";
   body2 = "... contents of file1.txt ...";
-  testParse(message, "AaB03x", [headers1, headers2], [body1, body2]);
+  _testParse(message, "AaB03x", [headers1, headers2], [body1, body2]);
 
   // Longer form from submitting the following from Chrome.
   //
@@ -164,15 +154,15 @@
       "content-disposition": "form-data; name=\"text_input\""};
   headers2 = <String, String>{
       "content-disposition": "form-data; name=\"password_input\""};
-  headers3 = <String, String>{
+  var headers3 = <String, String>{
       "content-disposition": "form-data; name=\"checkbox_input\""};
-  headers4 = <String, String>{
+  var headers4 = <String, String>{
       "content-disposition": "form-data; name=\"radio_input\""};
   body1 = "text";
   body2 = "password";
-  body3 = "on";
-  body4 = "on";
-  testParse(message,
+  var body3 = "on";
+  var body4 = "on";
+  _testParse(message,
             "----WebKitFormBoundaryQ3cgYAmGRF8yOeYB",
             [headers1, headers2, headers3, headers4],
             [body1, body2, body3, body4]);
@@ -196,7 +186,7 @@
 \r
 on\r
 -----------------------------52284550912143824192005403738--\r\n""";
-  testParse(message,
+  _testParse(message,
             "---------------------------52284550912143824192005403738",
             [headers1, headers2, headers3, headers4],
             [body1, body2, body3, body4]);
@@ -220,7 +210,7 @@
 \r
 on\r
 -----------------------------7dc8f38c60326--\r\n""";
-  testParse(message,
+  _testParse(message,
             "---------------------------7dc8f38c60326",
             [headers1, headers2, headers3, headers4],
             [body1, body2, body3, body4]);
@@ -261,7 +251,7 @@
 --\r\r\r
 -\r\r
 --boundary--\r\n""";
-  headers = <String, String>{"content-type": "text/plain"};
+  var headers = <String, String>{"content-type": "text/plain"};
   body1 = """
 -\r
 --\r
@@ -282,7 +272,7 @@
 --b\r\r\r\r
 --\r\r\r
 -\r""";
-  testParse(message, "boundary", [headers, headers], [body1, body2]);
+  _testParse(message, "boundary", [headers, headers], [body1, body2]);
 
   // Without initial CRLF.
   message = """
@@ -295,14 +285,12 @@
 \r
 Body2\r
 --xxx--\r\n""";
-  testParse(message, "xxx", null, ["\r\nBody 1", "\r\nBody2"]);
+  _testParse(message, "xxx", null, ["\r\nBody 1", "\r\nBody2"]);
 }
 
-void testParseInvalid() {
-  String message;
-
+void _testParseInvalid() {
   // Missing end boundary.
-  message = """
+  var message = """
 \r
 --xxx\r
 \r
@@ -313,10 +301,10 @@
 \r
 Body2\r
 --xxx\r\n""";
-  testParse(message, "xxx", null, [null, null], true);
+  _testParse(message, "xxx", null, [null, null], true);
 }
 
 void main() {
-  testParseValid();
-  testParseInvalid();
+  _testParseValid();
+  _testParseInvalid();
 }
diff --git a/test/mime_type_test.dart b/test/mime_type_test.dart
index 36c728d..23cc10e 100644
--- a/test/mime_type_test.dart
+++ b/test/mime_type_test.dart
@@ -2,10 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import "package:unittest/unittest.dart";
-import 'package:mime/mime.dart';
+import 'dart:math' as math;
 
-void expectMimeType(String path,
+import 'package:unittest/unittest.dart';
+import 'package:mime/mime.dart';
+import 'package:mime/src/magic_number.dart';
+
+void _expectMimeType(String path,
                     String expectedMimeType,
                     {List<int> headerBytes,
                      MimeTypeResolver resolver}) {
@@ -16,51 +19,49 @@
     mimeType = resolver.lookup(path, headerBytes: headerBytes);
   }
 
-  if (mimeType != expectedMimeType) {
-    throw "Expect '$expectedMimeType' but got '$mimeType'";
-  }
+  expect(mimeType, expectedMimeType);
 }
 
 void main() {
   group('global-lookup', () {
     test('by-path', () {
-      expectMimeType('file.dart', 'application/dart');
+      _expectMimeType('file.dart', 'application/dart');
       // Test mixed-case
-      expectMimeType('file.DaRT', 'application/dart');
-      expectMimeType('file.html', 'text/html');
-      expectMimeType('file.xhtml', 'application/xhtml+xml');
-      expectMimeType('file.jpeg', 'image/jpeg');
-      expectMimeType('file.jpg', 'image/jpeg');
-      expectMimeType('file.png', 'image/png');
-      expectMimeType('file.gif', 'image/gif');
-      expectMimeType('file.cc', 'text/x-c');
-      expectMimeType('file.c', 'text/x-c');
-      expectMimeType('file.css', 'text/css');
-      expectMimeType('file.js', 'application/javascript');
-      expectMimeType('file.ps', 'application/postscript');
-      expectMimeType('file.pdf', 'application/pdf');
-      expectMimeType('file.tiff', 'image/tiff');
-      expectMimeType('file.tif', 'image/tiff');
+      _expectMimeType('file.DaRT', 'application/dart');
+      _expectMimeType('file.html', 'text/html');
+      _expectMimeType('file.xhtml', 'application/xhtml+xml');
+      _expectMimeType('file.jpeg', 'image/jpeg');
+      _expectMimeType('file.jpg', 'image/jpeg');
+      _expectMimeType('file.png', 'image/png');
+      _expectMimeType('file.gif', 'image/gif');
+      _expectMimeType('file.cc', 'text/x-c');
+      _expectMimeType('file.c', 'text/x-c');
+      _expectMimeType('file.css', 'text/css');
+      _expectMimeType('file.js', 'application/javascript');
+      _expectMimeType('file.ps', 'application/postscript');
+      _expectMimeType('file.pdf', 'application/pdf');
+      _expectMimeType('file.tiff', 'image/tiff');
+      _expectMimeType('file.tif', 'image/tiff');
     });
 
     test('unknown-mime-type', () {
-      expectMimeType('file.unsupported-extension', null);
+      _expectMimeType('file.unsupported-extension', null);
     });
 
     test('by-header-bytes', () {
-      expectMimeType('file.jpg',
+      _expectMimeType('file.jpg',
                      'image/png',
                      headerBytes: [0x89, 0x50, 0x4E, 0x47,
                                    0x0D, 0x0A, 0x1A, 0x0A]);
-      expectMimeType('file.jpg',
+      _expectMimeType('file.jpg',
                      'image/gif',
                      headerBytes: [0x47, 0x49, 0x46, 0x38, 0x39,
                                    0x61, 0x0D, 0x0A, 0x1A, 0x0A]);
-      expectMimeType('file.gif',
+      _expectMimeType('file.gif',
                      'image/jpeg',
                      headerBytes: [0xFF, 0xD8, 0x46, 0x38, 0x39,
                                    0x61, 0x0D, 0x0A, 0x1A, 0x0A]);
-      expectMimeType('file.mp4',
+      _expectMimeType('file.mp4',
                      'video/mp4',
                      headerBytes: [0x00, 0x00, 0x00, 0x04, 0x66, 0x74,
                                    0x79, 0x70, 0x33, 0x67, 0x70, 0x35]);
@@ -71,13 +72,13 @@
     test('override-extension', () {
       var resolver = new MimeTypeResolver();
       resolver.addExtension('jpg', 'my-mime-type');
-      expectMimeType('file.jpg', 'my-mime-type', resolver: resolver);
+      _expectMimeType('file.jpg', 'my-mime-type', resolver: resolver);
     });
 
     test('fallthrough-extension', () {
       var resolver = new MimeTypeResolver();
       resolver.addExtension('jpg2', 'my-mime-type');
-      expectMimeType('file.jpg', 'image/jpeg', resolver: resolver);
+      _expectMimeType('file.jpg', 'image/jpeg', resolver: resolver);
     });
 
     test('with-mask', () {
@@ -85,19 +86,26 @@
       resolver.addMagicNumber([0x01, 0x02, 0x03],
                               'my-mime-type',
                               mask: [0x01, 0xFF, 0xFE]);
-      expectMimeType('file',
+      _expectMimeType('file',
                      'my-mime-type',
                      headerBytes: [0x01, 0x02, 0x03],
                      resolver: resolver);
-      expectMimeType('file',
+      _expectMimeType('file',
                      null,
                      headerBytes: [0x01, 0x03, 0x03],
                      resolver: resolver);
-      expectMimeType('file',
+      _expectMimeType('file',
                      'my-mime-type',
                      headerBytes: [0xFF, 0x02, 0x02],
                      resolver: resolver);
     });
   });
-}
 
+  test('default magic number', () {
+    var actualMaxBytes = DEFAULT_MAGIC_NUMBERS.fold(0, (previous, magic) {
+      return math.max(previous, magic.numbers.length);
+    });
+
+    expect(defaultMagicNumbersMaxLength, actualMaxBytes);
+  });
+}