diff --git a/src/custom-sort/folder-matching-rules.spec.ts b/src/custom-sort/folder-matching-rules.spec.ts index d52db4c..a69ccdd 100644 --- a/src/custom-sort/folder-matching-rules.spec.ts +++ b/src/custom-sort/folder-matching-rules.spec.ts @@ -3,6 +3,7 @@ import {FolderWildcardMatching} from './folder-matching-rules' type SortingSpec = string const checkIfImplicitSpec = (s: SortingSpec) => false +const checkIfImplicitSpecByPrefix = (s: SortingSpec) => s.startsWith('implicit:') const createMockMatcherRichVersion = (): FolderWildcardMatching => { const matcher: FolderWildcardMatching = new FolderWildcardMatching(checkIfImplicitSpec) @@ -116,6 +117,13 @@ describe('folderMatch', () => { expect(result).toEqual({errorMsg: "Duplicate wildcard '...' specification for /Archive/2020/.../"}) }) + it('should accept duplicate match children definitions for same path, if the former comes from implicit spec', () => { + const matcher: FolderWildcardMatching = new FolderWildcardMatching(checkIfImplicitSpecByPrefix) + matcher.addWildcardDefinition('Archive/2020/...', 'implicit: First occurrence') + const result = matcher.addWildcardDefinition('/Archive/2020/.../', 'Duplicate') + + expect(result).toBeUndefined() + }) it('should detect duplicate match all definitions for same path', () => { const matcher: FolderWildcardMatching = new FolderWildcardMatching(checkIfImplicitSpec) matcher.addWildcardDefinition('/Archive/2019/*', 'First occurrence') @@ -123,6 +131,13 @@ describe('folderMatch', () => { expect(result).toEqual({errorMsg: "Duplicate wildcard '*' specification for Archive/2019/*"}) }) + it('should accept duplicate match all definitions for same path, if the former comes from implicit spec', () => { + const matcher: FolderWildcardMatching = new FolderWildcardMatching(checkIfImplicitSpecByPrefix) + matcher.addWildcardDefinition('/Archive/2019/*', 'implicit: First occurrence') + const result = matcher.addWildcardDefinition('Archive/2019/*', 'Duplicate') + + expect(result).toBeUndefined() + }) it('regexp-match by name works (order of regexp doesn\'t matter) case A', () => { const matcher: FolderWildcardMatching = new FolderWildcardMatching(checkIfImplicitSpec) matcher.addRegexpDefinition(/^daily$/, false, undefined, false, `r1`) diff --git a/src/custom-sort/sorting-spec-processor.spec.ts b/src/custom-sort/sorting-spec-processor.spec.ts index 8018e72..0422a84 100644 --- a/src/custom-sort/sorting-spec-processor.spec.ts +++ b/src/custom-sort/sorting-spec-processor.spec.ts @@ -1415,6 +1415,13 @@ const txtInputTargetFolderMultiSpecD: string = ` target-folder: ./* ` +const txtInputTargetFolderMultiSpecD_implicitCase: string = ` + // typically implicit specs come context-free, no notion of current folder, that's why using explicit target-folder path +target-folder: mock-folder/* + // Reverse order to distinguish between implicit and explicit spec +> a-z +` + const expectedSortSpecForMultiSpecD: { [key: string]: CustomSortSpec } = { 'mock-folder': { groups: [{ @@ -1426,6 +1433,19 @@ const expectedSortSpecForMultiSpecD: { [key: string]: CustomSortSpec } = { } } +const expectedSortSpecForMultiSpecD_implicitCase: { [key: string]: CustomSortSpec } = { + 'mock-folder': { + defaultOrder: CustomSortOrder.alphabeticalReverse, + groups: [{ + order: CustomSortOrder.alphabeticalReverse, + type: CustomSortGroupType.Outsiders + }], + outsidersGroupIdx: 0, + targetFoldersPaths: ['mock-folder/*'], + implicit: true + } +} + const expectedWildcardMatchingTreeForMultiSpecD: FolderMatchingTreeNode = { subtree: { "mock-folder": { @@ -1443,10 +1463,36 @@ const expectedWildcardMatchingTreeForMultiSpecD: FolderMatchingTreeNode = { + subtree: { + "mock-folder": { + matchAll: { + defaultOrder: CustomSortOrder.alphabeticalReverse, + "groups": [{ + "order": CustomSortOrder.alphabeticalReverse, + "type": CustomSortGroupType.Outsiders + }], + "outsidersGroupIdx": 0, + "targetFoldersPaths": ["mock-folder/*"], + implicit: true + }, + name: "mock-folder", + subtree: {} + } + } +} + const txtInputTargetFolderMultiSpecE: string = ` target-folder: mock-folder/... ` +const txtInputTargetFolderMultiSpecE_implicitCase: string = ` + // typically implicit specs come context-free, no notion of current folder, that's why using explicit target-folder path +target-folder: mock-folder/... + // Reverse order to distinguish between implicit and explicit spec +> a-z +` + const expectedSortSpecForMultiSpecE: { [key: string]: CustomSortSpec } = { 'mock-folder': { groups: [{ @@ -1458,6 +1504,19 @@ const expectedSortSpecForMultiSpecE: { [key: string]: CustomSortSpec } = { } } +const expectedSortSpecForMultiSpecE_implicitCase: { [key: string]: CustomSortSpec } = { + 'mock-folder': { + defaultOrder: CustomSortOrder.alphabeticalReverse, + groups: [{ + order: CustomSortOrder.alphabeticalReverse, + type: CustomSortGroupType.Outsiders + }], + outsidersGroupIdx: 0, + targetFoldersPaths: ['mock-folder/...'], + implicit: true + } +} + const expectedWildcardMatchingTreeForMultiSpecE: FolderMatchingTreeNode = { subtree: { "mock-folder": { @@ -1475,6 +1534,72 @@ const expectedWildcardMatchingTreeForMultiSpecE: FolderMatchingTreeNode = { + subtree: { + "mock-folder": { + matchChildren: { + defaultOrder: CustomSortOrder.alphabeticalReverse, + "groups": [{ + "order": CustomSortOrder.alphabeticalReverse, + "type": CustomSortGroupType.Outsiders + }], + "outsidersGroupIdx": 0, + "targetFoldersPaths": ["mock-folder/..."], + implicit: true + }, + name: "mock-folder", + subtree: {} + } + } +} + +const expectedWildcardMatchingTreeForMultiSpecDplusE_implicitCase: FolderMatchingTreeNode = { + subtree: { + "mock-folder": { + matchAll: { + "groups": [{ + "order": CustomSortOrder.alphabetical, + "type": CustomSortGroupType.Outsiders + }], + "outsidersGroupIdx": 0, + "targetFoldersPaths": ["mock-folder/*"] + }, + matchChildren: { + defaultOrder: CustomSortOrder.alphabeticalReverse, + "groups": [{ + "order": CustomSortOrder.alphabeticalReverse, + "type": CustomSortGroupType.Outsiders + }], + "outsidersGroupIdx": 0, + "targetFoldersPaths": ["mock-folder/..."], + implicit: true + }, + name: "mock-folder", + subtree: {} + } + } +} + +const txtInputTargetFolderMultiSpecF_implicitCase: string = ` + // typically implicit specs come context-free, no notion of current folder, that's why using explicit target-folder path +target-folder: mock-folder + // Reverse order to distinguish between implicit and explicit spec +> a-z +` + +const expectedSortSpecForMultiSpecF_implicitCase: { [key: string]: CustomSortSpec } = { + 'mock-folder': { + defaultOrder: CustomSortOrder.alphabeticalReverse, + groups: [{ + order: CustomSortOrder.alphabeticalReverse, + type: CustomSortGroupType.Outsiders + }], + outsidersGroupIdx: 0, + targetFoldersPaths: ['mock-folder'], + implicit: true + } +} + describe('SortingSpecProcessor path wildcard priorities', () => { let processor: SortingSpecProcessor; beforeEach(() => { @@ -1504,12 +1629,52 @@ describe('SortingSpecProcessor path wildcard priorities', () => { expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecD) expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecD) }) + it('should not raise error for multiple spec for the same path and choose correct spec, case D - with implicit spec', () => { + const inputTxtArrImplicit: Array = txtInputTargetFolderMultiSpecD_implicitCase.split('\n') + const resultImplicit = processor.parseSortSpecFromText(inputTxtArrImplicit, 'implicit ignored param', 'implicit ignored param', null, true) + expect(resultImplicit?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecD_implicitCase) + expect(resultImplicit?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecD_implicitCase) + const inputTxtArrExplicit: Array = txtInputTargetFolderMultiSpecD.split('\n') + const result = processor.parseSortSpecFromText(inputTxtArrExplicit, 'mock-folder', 'custom-name-note.md', resultImplicit) + expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecD) + expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecD) + }) it('should not raise error for multiple spec for the same path and choose correct spec, case E', () => { const inputTxtArr: Array = txtInputTargetFolderMultiSpecE.split('\n') const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md') expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecE) expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecE) }) + it('should not raise error for multiple spec for the same path and choose correct spec, case E - with implicit spec', () => { + const inputTxtArrImplicit: Array = txtInputTargetFolderMultiSpecE_implicitCase.split('\n') + const resultImplicit = processor.parseSortSpecFromText(inputTxtArrImplicit, 'implicit ignored param', 'implicit ignored param', null, true) + expect(resultImplicit?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecE_implicitCase) + expect(resultImplicit?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecE_implicitCase) + const inputTxtArrExplicit: Array = txtInputTargetFolderMultiSpecE.split('\n') + const result = processor.parseSortSpecFromText(inputTxtArrExplicit, 'mock-folder', 'custom-name-note.md', resultImplicit) + expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecE) + expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecE) + }) + it('should not raise error for multiple spec for the same path and choose correct spec, mixed case D+E - with implicit spec, which looses all', () => { + const inputTxtArrImplicit: Array = txtInputTargetFolderMultiSpecE_implicitCase.split('\n') + const resultImplicit = processor.parseSortSpecFromText(inputTxtArrImplicit, 'implicit ignored param', 'implicit ignored param', null, true) + expect(resultImplicit?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecE_implicitCase) + expect(resultImplicit?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecE_implicitCase) + const inputTxtArrExplicit: Array = txtInputTargetFolderMultiSpecD.split('\n') + const result = processor.parseSortSpecFromText(inputTxtArrExplicit, 'mock-folder', 'custom-name-note.md', resultImplicit) + expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecD) + expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecDplusE_implicitCase) + }) + it('should not raise error for multiple spec for the same path and choose correct spec, mixed case E+F => implicit by name', () => { + const inputTxtArrImplicit: Array = txtInputTargetFolderMultiSpecF_implicitCase.split('\n') + const resultImplicit = processor.parseSortSpecFromText(inputTxtArrImplicit, 'implicit ignored param', 'implicit ignored param', null, true) + expect(resultImplicit?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecF_implicitCase) + expect(resultImplicit?.sortSpecByWildcard?.tree).toBeUndefined() + const inputTxtArrExplicit: Array = txtInputTargetFolderMultiSpecE.split('\n') + const result = processor.parseSortSpecFromText(inputTxtArrExplicit, 'mock-folder', 'custom-name-note.md', resultImplicit) + expect(result?.sortSpecByPath).toEqual(expectedSortSpecForMultiSpecF_implicitCase) + expect(result?.sortSpecByWildcard?.tree).toEqual(expectedWildcardMatchingTreeForMultiSpecE) + }) }) const txtInputAdvancedFolderDateSortingMethods: string = ` diff --git a/src/custom-sort/sorting-spec-processor.ts b/src/custom-sort/sorting-spec-processor.ts index 112be63..f5c085f 100644 --- a/src/custom-sort/sorting-spec-processor.ts +++ b/src/custom-sort/sorting-spec-processor.ts @@ -601,12 +601,18 @@ const endsWithWildcardPatternSuffix = (path: string): boolean => { path.endsWith(MATCH_ALL_SUFFIX) } + +/* + Important note: + even if the actual enum labels seem to be unused thus unneeded, their numerical values (implied by the order below) matter. + They define the priorities of the rules for wilcard target-folder specs. The rule with higher priority overrides the one with lower + */ enum WildcardPriority { NO_WILDCARD = 1, NO_WILDCARD_IMPLICIT, MATCH_CHILDREN, - MATCH_CHILDREN_IMPLICIT, MATCH_ALL, + MATCH_CHILDREN_IMPLICIT, MATCH_ALL_IMPLICIT }