Skip to content

Commit b874885

Browse files
committed
feat: add manzil related methods
1 parent 7eb7084 commit b874885

13 files changed

+245
-10
lines changed

src/findManzil.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { findAyahIdBySurah } from "./findAyahIdBySurah"
2+
import { findManzilByAyahId } from "./findManzilByAyahId"
3+
import { AyahId, AyahNo, Manzil, Surah } from "./types"
4+
5+
/**
6+
* Finds the Manzil number for a given Surah and Ayah
7+
* @param surah - The Surah number or object
8+
* @param ayah - Optional Ayah number (defaults to 1)
9+
* @returns The Manzil number (1-7) containing the specified Ayah
10+
*/
11+
export function findManzil(surah: Surah, ayah: AyahNo = 1): Manzil {
12+
const ayahId: AyahId = findAyahIdBySurah(surah, ayah)
13+
14+
return findManzilByAyahId(ayahId)
15+
}

src/findManzilByAyahId.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ManzilList } from "./lists/manzilList"
2+
import { AyahId, Manzil } from "./types"
3+
import { binarySearch } from "./utils"
4+
import { checkValidAyahId } from "./validation"
5+
6+
7+
/**
8+
* Finds the Manzil number for a given Ayah ID using binary search.
9+
* A Manzil is one of seven approximately equal divisions of the Quran.
10+
*
11+
* @param ayahId - The ID of the Ayah to find the Manzil for
12+
* @returns The Manzil number (1-7) containing the specified Ayah
13+
* @throws {Error} If the provided Ayah ID is invalid
14+
*
15+
* @example
16+
* ```typescript
17+
* const manzil = findManzilByAyahId(2345); // Returns the Manzil containing Ayah 2345
18+
* ```
19+
*/
20+
export function findManzilByAyahId(ayahId: AyahId): Manzil {
21+
checkValidAyahId(ayahId)
22+
23+
const jj = binarySearch(ManzilList, ayahId)
24+
const juz = jj < 0 ? -jj - 2 : jj
25+
return juz as Manzil
26+
}

src/findRangeAroundSurahAyah.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { checkValidSurah } from "./validation"
99
*
1010
* @param surah - The surah number (1-114)
1111
* @param ayah - The ayah number within the surah
12-
* @param mode - The range mode: "juz", "surah", "ayah", "page", or "all"
12+
* @param mode - The range mode: "juz", "surah", "ayah", "page", "ruku" or "all"
1313
* @returns A tuple containing the start and end ayah IDs of the range
1414
*/
1515
export function findRangeAroundSurahAyah(

src/getManzilMeta.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { findSurahAyahByAyahId } from "./findSurahAyahByAyahId"
2+
import { ManzilList } from "./lists/manzilList"
3+
import { AyahId, ManzilMeta } from "./types"
4+
import { checkValidManzil } from "./validation"
5+
6+
/**
7+
* Retrieves metadata for a specific Manzil (section) of the Quran
8+
*
9+
* @param manzilNum - The number of the Manzil (1-7)
10+
* @returns The metadata for the specified Manzil containing:
11+
* - manzilNum: The Manzil number
12+
* - firstAyahId: ID of the first ayah in the Manzil
13+
* - lastAyahId: ID of the last ayah in the Manzil
14+
* - first: Surah and ayah details of the first ayah
15+
* - last: Surah and ayah details of the last ayah
16+
* @throws Will throw an error if manzilNum is invalid
17+
*/
18+
export function getManzilMeta(manzilNum: number): ManzilMeta {
19+
checkValidManzil(manzilNum)
20+
21+
const [firstAyahId, nextManzilAyahId]: [AyahId, AyahId] = [
22+
ManzilList[manzilNum],
23+
ManzilList[manzilNum + 1]
24+
]
25+
const lastAyahId = nextManzilAyahId - 1
26+
return {
27+
manzilNum,
28+
firstAyahId,
29+
lastAyahId,
30+
first: findSurahAyahByAyahId(firstAyahId),
31+
last: findSurahAyahByAyahId(lastAyahId)
32+
}
33+
}

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export { findJuz } from "./findJuz"
1111
export { findJuzAndShift, findJuzAndShiftByAyahId } from "./findJuzAndShift"
1212
export { findJuzByAyahId } from "./findJuzByAyahId"
1313
export { findJuzMetaBySurah } from "./findJuzMetaBySurah"
14+
export { findManzil } from "./findManzil"
15+
export { findManzilByAyahId } from "./findManzilByAyahId"
1416
export { findPage } from "./findPage"
1517
export { findPagebyAyahId } from "./findPagebyAyahId"
1618
export { findRangeAroundAyah } from "./findRangeAroundAyah"
@@ -25,6 +27,7 @@ export { getAyahMeta } from "./getAyahMeta"
2527
export { getAyahMetasForSurah } from "./getAyahMetasForSurah"
2628
export { getJuzMeta } from "./getJuzMeta"
2729
export { getList } from "./getList"
30+
export { getManzilMeta } from "./getManzilMeta"
2831
export { getPageMeta } from "./getPageMeta"
2932
export { getRukuMeta } from "./getRukuMeta"
3033
export { getRubAlHizb } from "./getRubAlHizb"
@@ -45,8 +48,8 @@ export { SajdaList } from "./lists/sajdaList"
4548
export { SurahList } from "./lists/surahList"
4649
export { nextAyah } from "./nextAyah"
4750
export { prevAyah } from "./prevAyah"
48-
export { isValidRuku, isValidAyahId, isValidAyahNo, isValidJuz, isValidPage, isValidSurah, isValidSurahAyah } from "./typeGuards"
49-
export { checkValidRuku, checkValidAyahId, checkValidJuz, checkValidPage, checkValidSurah, checkValidSurahAyah } from "./validation"
51+
export { isValidManzil, isValidRuku, isValidAyahId, isValidAyahNo, isValidJuz, isValidPage, isValidSurah, isValidSurahAyah } from "./typeGuards"
52+
export { checkValidManzil, checkValidRuku, checkValidAyahId, checkValidJuz, checkValidPage, checkValidSurah, checkValidSurahAyah } from "./validation"
5053

5154
// ------------------ Sura i18 Data ---------------------
5255

src/typeGuards.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { maxAyahsInSurah, meta } from "./const"
22
import { getAyahCountInSurah } from "./getAyahCountInSurah"
3-
import { AyahId, AyahNo, HizbId, Juz, RubAlHizbId, Ruku, Surah, SurahAyah } from "./types"
3+
import { AyahId, AyahNo, HizbId, Juz, Manzil, RubAlHizbId, Ruku, Surah, SurahAyah } from "./types"
44

55
/**
66
* Checks if the given value is a valid AyahId.
@@ -104,3 +104,18 @@ export function isValidPage(x: unknown): x is Juz {
104104
export function isValidRuku(x: unknown): x is Ruku {
105105
return Number.isInteger(x) && 1 <= (x as number) && x as number <= meta.numRukus
106106
}
107+
108+
/**
109+
* Type guard to check if a value is a valid Manzil number
110+
*
111+
* @param x - The value to check
112+
* @returns True if the value is an integer between 1 and the total number of Manzils
113+
*
114+
* @example
115+
* if (isValidManzil(3)) {
116+
* // value is a valid Manzil number
117+
* }
118+
*/
119+
export function isValidManzil(x: unknown): x is Manzil {
120+
return Number.isInteger(x) && 1 <= (x as number) && x as number <= meta.numManzils
121+
}

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ export type PageMeta = {
110110
pageNum: Page
111111
} & RangeMeta
112112

113+
export type ManzilMeta = {
114+
manzilNum: Manzil
115+
} & RangeMeta
116+
113117
export type JuzMeta = {
114118
juzNum: Juz
115119
} & RangeMeta

src/validation.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { meta } from "./const"
22
import { getAyahCountInSurah } from "./getAyahCountInSurah"
3-
import { isValidAyahId, isValidJuz, isValidPage, isValidRuku, isValidSurah } from "./typeGuards"
4-
import { AyahId, AyahNo, Juz, Page, Ruku, Surah, SurahAyah } from "./types"
3+
import { isValidAyahId, isValidJuz, isValidManzil, isValidPage, isValidRuku, isValidSurah } from "./typeGuards"
4+
import { AyahId, AyahNo, Juz, Manzil, Page, Ruku, Surah, SurahAyah } from "./types"
55

66
/**
77
* Validates if the provided value is a valid Surah number.
@@ -119,6 +119,22 @@ export function checkValidRuku(x: unknown | number | Ruku): asserts x is Ruku {
119119
throw new TypeError("Ruku must be an integer")
120120
}
121121
if (!isValidRuku(x)) {
122-
throw new RangeError("Ruku must be between 1 and " + meta.numJuzs)
122+
throw new RangeError("Ruku must be between 1 and " + meta.numRukus)
123+
}
124+
}
125+
126+
/**
127+
* Type guard that checks if a value is a valid Manzil number.
128+
* @param x - The value to check
129+
* @throws {TypeError} If the value is not an integer
130+
* @throws {RangeError} If the value is not within valid Manzil range (1 to max manzils)
131+
* @remarks This is an assertion function that ensures the input is a valid Manzil type
132+
*/
133+
export function checkValidManzil(x: unknown | number | Manzil): asserts x is Manzil {
134+
if (typeof x !== "number" || !Number.isInteger(x)) {
135+
throw new TypeError("Manzil must be an integer")
136+
}
137+
if (!isValidManzil(x)) {
138+
throw new RangeError("Manzil must be between 1 and " + meta.numManzils)
123139
}
124140
}

tests/findManzil.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { findManzil } from "../src"
2+
3+
describe("findManzil", () => {
4+
it("should return correct manzil for surah 1", () => {
5+
expect(findManzil(1)).toBe(1)
6+
})
7+
8+
it("should return correct manzil for surah 6", () => {
9+
expect(findManzil(6)).toBe(2)
10+
})
11+
12+
it("should return correct manzil for surah 17", () => {
13+
expect(findManzil(17)).toBe(4)
14+
})
15+
16+
it("should return correct manzil for surah 26", () => {
17+
expect(findManzil(26)).toBe(5)
18+
})
19+
20+
it("should return correct manzil for surah 37", () => {
21+
expect(findManzil(37)).toBe(6)
22+
})
23+
24+
it("should return correct manzil for surah 50", () => {
25+
expect(findManzil(50)).toBe(7)
26+
})
27+
28+
it("should return correct manzil for surah 67", () => {
29+
expect(findManzil(67)).toBe(7)
30+
})
31+
32+
it("should handle specific ayah numbers", () => {
33+
expect(findManzil(2, 142)).toBe(1)
34+
expect(findManzil(4, 24)).toBe(1)
35+
})
36+
})

tests/findManzilByAyahId.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { findManzilByAyahId } from "../src"
2+
3+
describe("findManzilByAyahId", () => {
4+
it("should return correct manzil for beginning of Quran", () => {
5+
expect(findManzilByAyahId(1)).toBe(1)
6+
})
7+
8+
it("should return correct manzil for middle ayahs", () => {
9+
expect(findManzilByAyahId(1000)).toBe(2)
10+
expect(findManzilByAyahId(2000)).toBe(3)
11+
expect(findManzilByAyahId(3000)).toBe(5)
12+
})
13+
14+
it("should return correct manzil for end of Quran", () => {
15+
expect(findManzilByAyahId(6236)).toBe(7)
16+
})
17+
18+
it("should throw error for invalid ayah IDs", () => {
19+
expect(() => findManzilByAyahId(0)).toThrow()
20+
expect(() => findManzilByAyahId(-1)).toThrow()
21+
expect(() => findManzilByAyahId(6237)).toThrow()
22+
})
23+
24+
it("should handle boundary cases between manzils", () => {
25+
expect(findManzilByAyahId(670)).toBe(2)
26+
expect(findManzilByAyahId(671)).toBe(2)
27+
expect(findManzilByAyahId(1365)).toBe(3)
28+
expect(findManzilByAyahId(1366)).toBe(3)
29+
})
30+
})

0 commit comments

Comments
 (0)