ext/snowcrash/src/ActionParser.h in redsnow-0.2.0 vs ext/snowcrash/src/ActionParser.h in redsnow-0.2.1

- old
+ new

@@ -21,13 +21,11 @@ const char* const ActionHeaderRegex = "^[[:blank:]]*" HTTP_REQUEST_METHOD "[[:blank:]]*" URI_TEMPLATE "?$"; /** Named action matching regex */ const char* const NamedActionHeaderRegex = "^[[:blank:]]*" SYMBOL_IDENTIFIER "\\[" HTTP_REQUEST_METHOD "]$"; - /** Internal type alias for Collection of Action */ - typedef Collection<Action>::type Actions; - + /** Internal type alias for Collection iterator of Action */ typedef Collection<Action>::const_iterator ActionIterator; /** Action Definition Type */ enum ActionType { NotActionType = 0, @@ -44,73 +42,118 @@ static MarkdownNodeIterator processSignature(const MarkdownNodeIterator& node, const MarkdownNodes& siblings, SectionParserData& pd, SectionLayout& layout, - Report& report, - Action& out) { + ParseResult<Action>& out) { - actionHTTPMethodAndName(node, out.method, out.name); - TrimString(out.name); + actionHTTPMethodAndName(node, out.node.method, out.node.name); + TrimString(out.node.name); mdp::ByteBuffer remainingContent; GetFirstLine(node->text, remainingContent); + if (pd.exportSourceMap()) { + if (!out.node.method.empty()) { + out.sourceMap.method.sourceMap = node->sourceMap; + } + + if (!out.node.name.empty()) { + out.sourceMap.name.sourceMap = node->sourceMap; + } + } + if (!remainingContent.empty()) { - out.description += remainingContent; + out.node.description += remainingContent; + + if (pd.exportSourceMap()) { + out.sourceMap.description.sourceMap.append(node->sourceMap); + } } return ++MarkdownNodeIterator(node); } static MarkdownNodeIterator processNestedSection(const MarkdownNodeIterator& node, const MarkdownNodes& siblings, SectionParserData& pd, - Report& report, - Action& out) { + ParseResult<Action>& out) { SectionType sectionType = pd.sectionContext(); MarkdownNodeIterator cur = node; - Payload payload; std::stringstream ss; mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); switch (sectionType) { case ParametersSectionType: - return ParametersParser::parse(node, siblings, pd, report, out.parameters); + { + ParseResult<Parameters> parameters(out.report, out.node.parameters, out.sourceMap.parameters); + return ParametersParser::parse(node, siblings, pd, parameters); + } case RequestSectionType: case RequestBodySectionType: - cur = PayloadParser::parse(node, siblings, pd, report, payload); + { + ParseResult<Payload> payload(out.report); + cur = PayloadParser::parse(node, siblings, pd, payload); - if (out.examples.empty() || !out.examples.back().responses.empty()) { + if (out.node.examples.empty() || !out.node.examples.back().responses.empty()) { TransactionExample transaction; - out.examples.push_back(transaction); + SourceMap<TransactionExample> transactionSM; + + out.node.examples.push_back(transaction); + + if (pd.exportSourceMap()) { + out.sourceMap.examples.collection.push_back(transactionSM); + } } - checkPayload(sectionType, sourceMap, payload, out, report); + checkPayload(sectionType, sourceMap, payload.node, out); - out.examples.back().requests.push_back(payload); + out.node.examples.back().requests.push_back(payload.node); + + if (pd.exportSourceMap()) { + out.sourceMap.examples.collection.back().requests.collection.push_back(payload.sourceMap); + } + break; + } case ResponseSectionType: case ResponseBodySectionType: - cur = PayloadParser::parse(node, siblings, pd, report, payload); + { + ParseResult<Payload> payload(out.report); + cur = PayloadParser::parse(node, siblings, pd, payload); - if (out.examples.empty()) { + if (out.node.examples.empty()) { TransactionExample transaction; - out.examples.push_back(transaction); + SourceMap<TransactionExample> transactionSM; + + out.node.examples.push_back(transaction); + + if (pd.exportSourceMap()) { + out.sourceMap.examples.collection.push_back(transactionSM); + } } - checkPayload(sectionType, sourceMap, payload, out, report); + checkPayload(sectionType, sourceMap, payload.node, out); - out.examples.back().responses.push_back(payload); + out.node.examples.back().responses.push_back(payload.node); + + if (pd.exportSourceMap()) { + out.sourceMap.examples.collection.back().responses.collection.push_back(payload.sourceMap); + } + break; + } case HeadersSectionType: - return SectionProcessor<Action>::handleDeprecatedHeaders(node, siblings, pd, report, out.headers); + { + ParseResult<Headers> headers(out.report, out.node.headers, out.sourceMap.headers); + return SectionProcessor<Action>::handleDeprecatedHeaders(node, siblings, pd, headers); + } default: break; } @@ -118,45 +161,52 @@ } static bool isUnexpectedNode(const MarkdownNodeIterator& node, SectionType sectionType) { - if ( SectionProcessor<Asset>::sectionType(node) != UndefinedSectionType) { + if (SectionProcessor<Asset>::sectionType(node) != UndefinedSectionType) { return true; } return SectionProcessorBase<Action>::isUnexpectedNode(node, sectionType); } static MarkdownNodeIterator processUnexpectedNode(const MarkdownNodeIterator& node, const MarkdownNodes& siblings, SectionParserData& pd, SectionType& sectionType, - Report& report, - Action& out) { + ParseResult<Action>& out) { if ((node->type == mdp::ParagraphMarkdownNodeType || node->type == mdp::CodeMarkdownNodeType) && (sectionType == ResponseBodySectionType || sectionType == ResponseSectionType) && - !out.examples.empty() && - !out.examples.back().responses.empty()) { + !out.node.examples.empty() && + !out.node.examples.back().responses.empty()) { - CodeBlockUtility::addDanglingAsset(node, pd, sectionType, report, out.examples.back().responses.back().body); - + mdp::ByteBuffer content = CodeBlockUtility::addDanglingAsset(node, pd, sectionType, out.report, out.node.examples.back().responses.back().body); + + if (pd.exportSourceMap() && !content.empty()) { + out.sourceMap.examples.collection.back().responses.collection.back().body.sourceMap.append(node->sourceMap); + } + return ++MarkdownNodeIterator(node); } if ((node->type == mdp::ParagraphMarkdownNodeType || node->type == mdp::CodeMarkdownNodeType) && (sectionType == RequestBodySectionType || sectionType == RequestSectionType) && - !out.examples.empty() && - !out.examples.back().requests.empty()) { + !out.node.examples.empty() && + !out.node.examples.back().requests.empty()) { - CodeBlockUtility::addDanglingAsset(node, pd, sectionType, report, out.examples.back().requests.back().body); - + mdp::ByteBuffer content = CodeBlockUtility::addDanglingAsset(node, pd, sectionType, out.report, out.node.examples.back().requests.back().body); + + if (pd.exportSourceMap() && !content.empty()) { + out.sourceMap.examples.collection.back().requests.collection.back().body.sourceMap.append(node->sourceMap); + } + return ++MarkdownNodeIterator(node); } SectionType assetType = SectionProcessor<Asset>::sectionType(node); @@ -167,18 +217,18 @@ mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); ss << "Ignoring " << SectionName(assetType) << " list item, "; ss << SectionName(assetType) << " list item is expected to be indented by 4 spaces or 1 tab"; - report.warnings.push_back(Warning(ss.str(), - IgnoringWarning, - sourceMap)); + out.report.warnings.push_back(Warning(ss.str(), + IgnoringWarning, + sourceMap)); return ++MarkdownNodeIterator(node); } - return SectionProcessorBase<Action>::processUnexpectedNode(node, siblings, pd, sectionType, report, out); + return SectionProcessorBase<Action>::processUnexpectedNode(node, siblings, pd, sectionType, out); } static SectionType sectionType(const MarkdownNodeIterator& node) { if (node->type == mdp::HeaderMarkdownNodeType @@ -240,44 +290,47 @@ return nested; } static void finalize(const MarkdownNodeIterator& node, SectionParserData& pd, - Report& report, - Action& out) { + ParseResult<Action>& out) { - if (!out.headers.empty()) { + if (!out.node.headers.empty()) { - SectionProcessor<Headers>::injectDeprecatedHeaders(out.headers, out.examples); - out.headers.clear(); + SectionProcessor<Headers>::injectDeprecatedHeaders(pd, out.node.headers, out.sourceMap.headers, out.node.examples, out.sourceMap.examples); + out.node.headers.clear(); + + if (pd.exportSourceMap()) { + out.sourceMap.headers.collection.clear(); + } } - if (out.examples.empty()) { + if (out.node.examples.empty()) { // WARN: No response for action mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); - report.warnings.push_back(Warning("action is missing a response", - EmptyDefinitionWarning, - sourceMap)); - } else if (!out.examples.empty() && - !out.examples.back().requests.empty() && - out.examples.back().responses.empty()) { + out.report.warnings.push_back(Warning("action is missing a response", + EmptyDefinitionWarning, + sourceMap)); + } else if (!out.node.examples.empty() && + !out.node.examples.back().requests.empty() && + out.node.examples.back().responses.empty()) { // WARN: No response for request std::stringstream ss; ss << "action is missing a response for "; - if (out.examples.back().requests.back().name.empty()) { + if (out.node.examples.back().requests.back().name.empty()) { ss << "a request"; } else { - ss << "the '" << out.examples.back().requests.back().name << "' request"; + ss << "the '" << out.node.examples.back().requests.back().name << "' request"; } mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); - report.warnings.push_back(Warning(ss.str(), - EmptyDefinitionWarning, - sourceMap)); + out.report.warnings.push_back(Warning(ss.str(), + EmptyDefinitionWarning, + sourceMap)); } } /** * \brief Check & report payload validity. @@ -288,54 +341,53 @@ * \param report Parser report. */ static void checkPayload(SectionType sectionType, const mdp::CharactersRangeSet sourceMap, const Payload& payload, - const Action& action, - Report& report) { + ParseResult<Action>& out) { - if (isPayloadDuplicate(sectionType, payload, action.examples.back())) { + if (isPayloadDuplicate(sectionType, payload, out.node.examples.back())) { // WARN: Duplicate payload std::stringstream ss; ss << SectionName(sectionType) << " payload `" << payload.name << "`"; - ss << " already defined for `" << action.method << "` method"; + ss << " already defined for `" << out.node.method << "` method"; - report.warnings.push_back(Warning(ss.str(), - DuplicateWarning, - sourceMap)); + out.report.warnings.push_back(Warning(ss.str(), + DuplicateWarning, + sourceMap)); } if (sectionType == ResponseSectionType || sectionType == ResponseBodySectionType) { HTTPStatusCode code; if (!payload.name.empty()) { std::stringstream(payload.name) >> code; } - HTTPMethodTraits methodTraits = GetMethodTrait(action.method); + HTTPMethodTraits methodTraits = GetMethodTrait(out.node.method); if (!methodTraits.allowBody && !payload.body.empty()) { // WARN: Edge case for 2xx CONNECT - if (action.method == HTTPMethodName::Connect && code/100 == 2) { + if (out.node.method == HTTPMethodName::Connect && code/100 == 2) { std::stringstream ss; - ss << "the response for " << code << " " << action.method << " request MUST NOT include a " << SectionName(BodySectionType); + ss << "the response for " << code << " " << out.node.method << " request MUST NOT include a " << SectionName(BodySectionType); - report.warnings.push_back(Warning(ss.str(), - EmptyDefinitionWarning, - sourceMap)); - } else if (action.method != HTTPMethodName::Connect && !methodTraits.allowBody) { + out.report.warnings.push_back(Warning(ss.str(), + EmptyDefinitionWarning, + sourceMap)); + } else if (out.node.method != HTTPMethodName::Connect && !methodTraits.allowBody) { std::stringstream ss; - ss << "the response for " << action.method << " request MUST NOT include a " << SectionName(BodySectionType); + ss << "the response for " << out.node.method << " request MUST NOT include a " << SectionName(BodySectionType); - report.warnings.push_back(Warning(ss.str(), - EmptyDefinitionWarning, - sourceMap)); + out.report.warnings.push_back(Warning(ss.str(), + EmptyDefinitionWarning, + sourceMap)); } return; } } @@ -349,38 +401,37 @@ const Payload& payload, const TransactionExample& example) { if (sectionType == RequestSectionType) { - Collection<Request>::const_iterator duplicate = FindRequest(example, payload); + RequestIterator duplicate = SectionProcessor<Payload>::findRequest(example, payload); return duplicate != example.requests.end(); } else if (sectionType == ResponseSectionType) { - Collection<Response>::const_iterator duplicate = FindResponse(example, payload); + ResponseIterator duplicate = SectionProcessor<Payload>::findResponse(example, payload); return duplicate != example.responses.end(); } return false; } /** Warn about deprecated headers */ static MarkdownNodeIterator handleDeprecatedHeaders(const MarkdownNodeIterator& node, const MarkdownNodes& siblings, SectionParserData& pd, - Report& report, - Headers& headers) { + ParseResult<Headers>& out) { - MarkdownNodeIterator cur = HeadersParser::parse(node, siblings, pd, report, headers); + MarkdownNodeIterator cur = HeadersParser::parse(node, siblings, pd, out); // WARN: Deprecated header sections std::stringstream ss; ss << "the 'headers' section at this level is deprecated and will be removed in a future, use respective payload header section(s) instead"; mdp::CharactersRangeSet sourceMap = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceData); - report.warnings.push_back(Warning(ss.str(), - DeprecatedWarning, - sourceMap)); + out.report.warnings.push_back(Warning(ss.str(), + DeprecatedWarning, + sourceMap)); return cur; } /** \return %ActionType of a node */ @@ -427,9 +478,18 @@ name = captureGroups[1]; method = captureGroups[2]; } return; + } + + /** Finds an action inside an actions collection */ + static ActionIterator findAction(const Actions& actions, + const Action& action) { + + return std::find_if(actions.begin(), + actions.end(), + std::bind2nd(MatchAction<Action>(), action)); } }; /** Action Section Parser */ typedef SectionParser<Action, HeaderSectionAdapter> ActionParser;